Python Server

visualization
Tasio Mendez 7 years ago
commit 6f8cc9ba50

2
.gitignore vendored

@ -0,0 +1,2 @@
__pycache__/
output/

@ -0,0 +1,25 @@
name: ControlModelM2_sim
max_time: 50
num_trials: 2
network_params:
generator: barabasi_albert_graph
n: 100
m: 2
network_agents:
- agent_type: ControlModelM2
weight: 0.1
state:
id: 1
- agent_type: ControlModelM2
weight: 0.9
state:
id: 0
environment_params:
prob_neutral_making_denier: 0.035
prob_infect: 0.075
prob_cured_healing_infected: 0.035
prob_cured_vaccinate_neutral: 0.035
prob_vaccinated_healing_infected: 0.035
prob_vaccinated_vaccinate_neutral: 0.035
prob_generate_anti_rumor: 0.035
standard_variance: 0.055

@ -0,0 +1,15 @@
from server import ModularServer
from visualization import GraphVisualization, Model
def run(model, params=None):
graphVisualization = GraphVisualization(params)
server = ModularServer(model, graphVisualization, name="SOIL Model")
server.port = 8001
server.launch()
if __name__ == "__main__":
soil = Model(dump=False)
run(soil)

@ -0,0 +1,159 @@
import os
import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.escape
import tornado.gen
import webbrowser
import yaml
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class VisualizationElement:
"""
Defines an element of the visualization.
Attributes:
package_includes: A list of external JavaScript files to include that
are part of the packages.
local_includes: A list of JavaScript files that are local to the
directory that the server is being run in.
js_code: A JavaScript code string to instantiate the element.
Methods:
render: Takes a model object, and produces JSON data which can be sent
to the client.
"""
package_includes = []
local_includes = []
js_code = ''
render_args = {}
def __init__(self):
pass
def render(self, model):
return '<b>VisualizationElement goes here</b>.'
class PageHandler(tornado.web.RequestHandler):
""" Handler for the HTML template which holds the visualization. """
def get(self):
self.render('index.html', port=self.application.port,
model_name=self.application.model_name,
package_includes=self.application.package_includes,
local_includes=self.application.local_includes,
scripts=self.application.js_code)
class SocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
if self.application.verbose:
logger.info('Socket opened!')
def check_origin(self, origin):
return True
def on_message(self, message):
""" Receiving a message from the websocket, parse, and act accordingly. """
msg = tornado.escape.json_decode(message)
if msg['type'] == 'config_file':
if self.application.verbose:
print(msg['data'])
config = list(yaml.load_all(msg['data']))
if len(config) > 1:
error = 'Please, provide only one configuration.'
if self.application.verbose:
logger.error(error)
self.write_message({'type': 'error',
'error': error})
return
else:
config = config[0]
self.name = config['name']
self.application.model.run(config)
trials = []
for i in range(config['num_trials']):
trials.append('{}_trial_{}'.format(self.name, i))
self.write_message({'type': 'trials',
'data': trials })
elif msg['type'] == 'get_trial':
if self.application.verbose:
logger.info('Trial {} requested!'.format(msg['data']))
self.write_message({'type': 'get_trial',
'data': self.application.model.get_trial(self.name, msg['data']) })
else:
if self.application.verbose:
logger.info('Unexpected message!')
class ModularServer(tornado.web.Application):
""" Main visualization application. """
portrayal_method = None
port = 8001
model_args = ()
model_kwargs = {}
page_handler = (r'/', PageHandler)
socket_handler = (r'/ws', SocketHandler)
static_handler = (r'/(.*)', tornado.web.StaticFileHandler,
# {'path': os.path.dirname(__file__)})
{'path': 'templates'})
local_handler = (r'/local/(.*)', tornado.web.StaticFileHandler,
{'path': ''})
handlers = [page_handler, socket_handler, static_handler, local_handler]
settings = {'debug': True,
'template_path': os.path.dirname(__file__) + '/templates'}
def __init__(self, model, visualization_element, name='SOIL Model', verbose=True,
*args, **kwargs):
self.verbose = verbose
self.package_includes = set()
self.local_includes = set()
self.js_code = []
self.visualization_element = visualization_element
self.model_name = name
self.model = model
self.model_args = args
self.model_kwargs = kwargs
#self.reset_model()
# Initializing the application itself:
super().__init__(self.handlers, **self.settings)
'''
def reset_model(self):
self.model = self.model_cls(*self.model_args, **self.model_kwargs)
'''
def render_model(self):
return self.visualization_element.render(self.model)
def launch(self, port=None):
""" Run the app. """
if port is not None:
self.port = port
url = 'http://127.0.0.1:{PORT}'.format(PORT=self.port)
print('Interface starting at {url}'.format(url=url))
self.listen(self.port)
webbrowser.open(url)
tornado.ioloop.IOLoop.instance().start()

@ -0,0 +1,238 @@
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
svg#graph, #configuration {
background-color: white;
margin-top: 15px;
border-style: double;
border-color: rgba(0, 0, 0, 0.35);
border-radius: 5px;
padding: 0px;
}
#timeline {
padding: 0;
margin-top: 20px;
}
#configuration {
margin-top: 15px;
padding: 15px;
border-left: none !important;
overflow: auto;
}
button {
outline: none !important;
}
.btn-toolbar.controls {
position: absolute;
right: 0;
}
.controls > .btn {
margin-left: 10px !important;
}
button.pressed {
background-color: rgb(167, 242, 168);
-webkit-animation: background 1s cubic-bezier(1,0,0,1) infinite;
animation: background 1s cubic-bezier(1,0,0,1) infinite;
cursor: default !important;
}
@-webkit-keyframes background {
50% { background-color: #dddddd; }
100% { background-color: rgb(167, 242, 168); }
}
@keyframes background {
50% { background-color: #dddddd; }
100% { background-color: rgb(167, 242, 168); }
}
#slider3 {
background: repeating-linear-gradient( 90deg, white 27px, white 30px, #fff 32px, #aaa 33px );
background-color: white;
}
.config-item {
margin-top: 15px !important;
}
/** LOADER **/
#load {
position: absolute;
font-weight: bold;
}
#load.loader {
border: 5px solid #f3f3f3;
border-radius: 50%;
border-top: 5px solid #3498db;
border-bottom: 5px solid #3498db;
width: 30px;
height: 30px;
-webkit-animation: spin 1s linear infinite;
animation: spin 1s linear infinite;
position: absolute;
}
#load:before {
content: 'No file'
}
#load.loader:before {
content: '' !important;
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/** ALERT **/
.alert-danger {
position: absolute;
margin-top: 20px;
margin-left: 5px;
}
/** FILE BROWSER **/
.custom-file {
position: relative;
display: inline-block;
width: 100%;
height: 35px;
margin-bottom: 0;
cursor: pointer;
}
.custom-file-input {
min-width: 14rem;
max-width: 100%;
height: 35px;
margin: 0;
filter: alpha(opacity=0);
opacity: 0;
}
.custom-file-control {
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 5;
height: 35px;
padding: .5rem 1rem;
line-height: 1.5;
color: #464a4c;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: #fff;
border: 1px solid rgba(0,0,0,.15);
border-radius: .25rem;
}
.custom-file-control::before {
content: "Browse";
position: absolute;
top: -1px;
right: -1px;
bottom: -1px;
z-index: 6;
display: block;
height: 35px;
padding: .5rem 1rem;
line-height: 1.5;
color: #464a4c;
background-color: #eceeef;
border: 1px solid rgba(0,0,0,.15);
border-radius: 0 .25rem .25rem 0;
}
.custom-file-control::after {
content: attr(data-content);
}
/** TABLES **/
#percentTable {
height: 150px !important;
width: 100% !important;
}
#percentTable tr {
padding: 5px 2px;
}
hr {
margin-top: 15px !important;
margin-bottom: 15px !important;
}
#info-graph {
width: 70% !important;
}
.logo {
margin-top: -40px;
position: absolute;
right: 15px;
}
/** SLIDER **/
.speed-slider,
.link-distance-slider {
padding: 0 10px !important;
margin-top: 5px !important;
width: 100% !important;
}
.speed-slider .slider,
.link-distance-slider .slider {
width: 100% !important;
}
.speed-slider .slider-selection,
.link-distance-slider .slider-selection {
background-image: linear-gradient(to bottom,
rgba(36, 110, 162, 0.5) 0%,
rgba(3, 169, 224, 0.5) 100%) !important;
}
.slider-disabled .slider-selection {
opacity: 0.5;
}
.slider.slider-disabled .slider-track {
cursor: default !important;
}
table#speed,
table#link-distance {
width: 100%;
}
table#speed .min,
table#speed .max,
table#link-distance .min,
table#link-distance .max {
font-weight: normal !important;
}

@ -0,0 +1,72 @@
#slider3 {
margin: 0 0 10px 0;
}
.d3-slider {
position: relative;
font-family: Verdana,Arial,sans-serif;
font-size: 1.1em;
border: 1px solid #aaaaaa;
z-index: 2;
}
.d3-slider-horizontal {
height: 40px;
}
.d3-slider-range {
background:#2980b9;
left:0px;
right:0px;
height: 0.8em;
position: absolute;
}
.d3-slider-handle {
position: absolute;
width: .8em;
height: 48px;
border: 1px solid #d3d3d3;
border-radius: 4px;
background: #eee;
background: linear-gradient(to bottom, #eee 0%, #ddd 100%);
z-index: 3;
}
.d3-slider-handle:hover {
border: 1px solid #999999;
}
.d3-slider-horizontal .d3-slider-handle {
top: -.3em;
margin-left: -.4em;
}
.d3-slider-axis {
position: relative;
z-index: 1;
}
.d3-slider-axis-bottom {
top: 38px;
}
.d3-slider-axis-right {
left: .8em;
}
.d3-slider-axis path {
stroke-width: 0;
fill: none;
}
.d3-slider-axis line {
fill: none;
stroke: #aaa;
shape-rendering: crispEdges;
stroke-dasharray: 2;
}
.d3-slider-axis text {
font-size: 11px;
}

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="360"
height="330"
id="svg3752"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="logo_gsi_nuevo.svg"
inkscape:export-filename="/home/cif/GoogleDrive/docs/gsi/corporativo/logos-fondos/logos/logo_gsi_nuevo_740_671.png"
inkscape:export-xdpi="185.6273"
inkscape:export-ydpi="185.6273">
<defs
id="defs3754">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3760" />
<inkscape:perspective
id="perspective3730"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<filter
inkscape:collect="always"
id="filter3757"
x="-0.4412556"
width="1.8825113"
y="-0.4412556"
height="1.8825113"
color-interpolation-filters="sRGB">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.91928251"
id="feGaussianBlur3759" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.5"
inkscape:cx="187.12284"
inkscape:cy="78.411806"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1855"
inkscape:window-height="1056"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata3757">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-722.36218)">
<g
style="display:inline"
id="g3761"
transform="matrix(2.2932314,0,0,2.2932314,-744.72199,6804.6985)">
<g
transform="translate(-161.76758,3.3349672)"
id="g2940">
<g
id="g2922">
<g
transform="translate(-79.72168,-3162.9998)"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#00a9e0;fill-opacity:1;stroke:none;font-family:Calibri;-inkscape-font-specification:Calibri Bold"
id="flowRoot4140">
<path
d="m 688.88086,587.87773 c -6e-5,3.71096 -0.70806,6.98244 -2.12402,9.81446 -1.36724,2.83204 -3.32037,5.2002 -5.85938,7.10449 -2.53911,1.9043 -5.54203,3.32031 -9.00879,4.24805 -3.46683,0.97656 -7.22659,1.46484 -11.2793,1.46484 -2.44143,0 -4.78517,-0.19531 -7.03125,-0.58594 -2.19728,-0.34179 -4.17482,-0.78125 -5.93261,-1.31836 -1.75783,-0.58593 -3.22267,-1.17187 -4.39453,-1.75781 -1.17189,-0.58593 -2.02638,-1.12304 -2.56348,-1.61133 -0.53712,-0.5371 -0.95215,-1.34277 -1.24512,-2.41699 -0.29297,-1.12304 -0.43946,-2.75878 -0.43945,-4.90723 -10e-6,-1.416 0.0488,-2.53904 0.14648,-3.36914 0.0977,-0.87889 0.24414,-1.56248 0.43946,-2.05078 0.1953,-0.53709 0.43944,-0.87889 0.73242,-1.02539 0.29296,-0.19529 0.65917,-0.29295 1.09863,-0.29297 0.5371,2e-5 1.31835,0.3174 2.34375,0.95215 1.07421,0.58595 2.39257,1.24513 3.95508,1.97754 1.56248,0.73244 3.36912,1.41603 5.41992,2.05078 2.09959,0.63478 4.46775,0.95216 7.10449,0.95215 1.66013,10e-6 3.12497,-0.17089 4.39454,-0.5127 1.31832,-0.34178 2.44137,-0.83006 3.36914,-1.46484 0.97652,-0.63475 1.70894,-1.44041 2.19726,-2.41699 0.48825,-0.97655 0.73239,-2.09959 0.73242,-3.36914 -3e-5,-1.46482 -0.4639,-2.70994 -1.3916,-3.73535 -0.87894,-1.0742 -2.07523,-2.00193 -3.58887,-2.78321 -1.46487,-0.78122 -3.14944,-1.51364 -5.05371,-2.19726 -1.85549,-0.68357 -3.7842,-1.4404 -5.78613,-2.27051 -1.95314,-0.83005 -3.88185,-1.78219 -5.78613,-2.85645 -1.85548,-1.07418 -3.54005,-2.39254 -5.05371,-3.95507 -1.46486,-1.56246 -2.66114,-3.44235 -3.58887,-5.63965 -0.87891,-2.19722 -1.31837,-4.83394 -1.31836,-7.91016 -10e-6,-3.12494 0.61035,-5.98139 1.83106,-8.56933 1.22069,-2.63666 2.9785,-4.88275 5.27343,-6.73829 2.29491,-1.8554 5.07811,-3.29582 8.34961,-4.32128 3.32029,-1.02532 7.03122,-1.53802 11.13281,-1.53809 2.05075,7e-5 4.02829,0.14656 5.93262,0.43945 1.95309,0.29304 3.7109,0.65925 5.27344,1.09864 1.56245,0.43952 2.88081,0.9278 3.95508,1.46484 1.07417,0.48835 1.831,0.9278 2.27051,1.31836 0.48823,0.34186 0.83002,0.70807 1.02539,1.09863 0.19526,0.34186 0.34174,0.78132 0.43945,1.31836 0.0976,0.48835 0.17085,1.12311 0.21973,1.9043 0.0976,0.73248 0.14643,1.66022 0.14648,2.7832 -5e-5,1.31842 -0.0489,2.39264 -0.14648,3.22266 -0.0489,0.83013 -0.17095,1.48931 -0.36622,1.97754 -0.14653,0.48833 -0.36626,0.83013 -0.65918,1.02539 -0.29301,0.14654 -0.63481,0.21978 -1.02539,0.21972 -0.4395,6e-5 -1.12309,-0.24408 -2.05078,-0.73242 -0.92778,-0.53705 -2.09965,-1.09858 -3.51562,-1.68457 -1.36723,-0.58588 -2.97856,-1.12299 -4.83399,-1.61133 -1.80667,-0.53705 -3.88187,-0.8056 -6.22558,-0.80566 -1.66019,6e-5 -3.10062,0.17096 -4.32129,0.51269 -1.22073,0.34186 -2.22171,0.83014 -3.00293,1.46485 -0.78128,0.63482 -1.36721,1.39166 -1.75781,2.27051 -0.39065,0.83013 -0.58596,1.73345 -0.58594,2.70996 -2e-5,1.51372 0.46384,2.78325 1.3916,3.80859 0.92771,1.02544 2.14841,1.92876 3.66211,2.70996 1.51364,0.7813 3.22262,1.51372 5.12695,2.19727 1.95309,0.68363 3.90622,1.44047 5.85938,2.27051 2.00191,0.78129 3.95503,1.70902 5.85937,2.7832 1.95308,1.07425 3.68648,2.39261 5.2002,3.95508 1.51362,1.56253 2.73432,3.44241 3.66211,5.63964 0.92768,2.14847 1.39154,4.71194 1.3916,7.69043"
style="font-size:150px;fill:#00a9e0;font-family:Calibri;-inkscape-font-specification:Calibri Bold"
id="path3806" />
<path
d="m 721.10742,606.33477 c -3e-5,0.48828 -0.14651,0.92773 -0.43945,1.31836 -0.293,0.34179 -0.80569,0.63476 -1.53809,0.8789 -0.68362,0.24414 -1.61135,0.41504 -2.7832,0.5127 -1.1719,0.14648 -2.66115,0.21972 -4.46777,0.21972 -1.80666,0 -3.29592,-0.0732 -4.46778,-0.21972 -1.17189,-0.0977 -2.12403,-0.26856 -2.85644,-0.5127 -0.68361,-0.24414 -1.17189,-0.53711 -1.46485,-0.8789 -0.29297,-0.39063 -0.43946,-0.83008 -0.43945,-1.31836 l 0,-65.18555 c -10e-6,-0.48821 0.14648,-0.90325 0.43945,-1.24512 0.29296,-0.39055 0.78124,-0.70794 1.46485,-0.95215 0.73241,-0.2929 1.68455,-0.51262 2.85644,-0.65918 1.17186,-0.14641 2.66112,-0.21965 4.46778,-0.21972 1.80662,7e-5 3.29587,0.0733 4.46777,0.21972 1.17185,0.14656 2.09958,0.36628 2.7832,0.65918 0.7324,0.24421 1.24509,0.5616 1.53809,0.95215 0.29294,0.34187 0.43942,0.75691 0.43945,1.24512 l 0,65.18555 m 1.3916,-87.45118 c -3e-5,3.71103 -0.75686,6.2745 -2.2705,7.69043 -1.5137,1.4161 -4.32132,2.12411 -8.42286,2.12403 -4.1504,8e-5 -6.95802,-0.68352 -8.42285,-2.05078 -1.41602,-1.36711 -2.12403,-3.83293 -2.12402,-7.39747 -10e-6,-3.71084 0.73241,-6.27431 2.19726,-7.69042 1.51367,-1.46475 4.34569,-2.19717 8.4961,-2.19727 4.10154,1e-4 6.88474,0.70811 8.34961,2.12402 1.46481,1.36729 2.19723,3.8331 2.19726,7.39746"
style="font-size:150px;fill:#00a9e0;font-family:Calibri;-inkscape-font-specification:Calibri Bold"
id="path3808" />
</g>
<g
transform="matrix(1,0,0,1.1503876,-79.72168,-3243.7762)"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#00a9e0;fill-opacity:1;stroke:none;font-family:Calibri;-inkscape-font-specification:Calibri Bold"
id="flowRoot2914">
<path
d="m 633.80273,545.54375 c -6e-5,2.34381 -0.26862,4.07721 -0.80566,5.2002 -0.53718,1.1231 -1.19635,1.68462 -1.97754,1.68457 l -7.69043,0 c 1.07416,1.17193 1.831,2.5147 2.27051,4.02832 0.48822,1.46489 0.73236,3.00297 0.73242,4.61425 -6e-5,3.80864 -0.63482,7.20219 -1.9043,10.18067 -1.26958,2.92972 -3.10064,5.41995 -5.49316,7.4707 -2.3438,2.00198 -5.20024,3.54007 -8.56934,4.61426 -3.32035,1.02541 -7.03128,1.53811 -11.13281,1.53808 -2.09964,3e-5 -4.10159,-0.24411 -6.00586,-0.73242 -1.90432,-0.53708 -3.36916,-1.14743 -4.39453,-1.83105 -0.58596,0.63479 -1.12307,1.39162 -1.61133,2.27051 -0.43947,0.87893 -0.6592,1.85549 -0.65918,2.92968 -2e-5,1.41604 0.61033,2.58791 1.83106,3.51563 1.26951,0.87892 3.02732,1.3672 5.27344,1.46484 l 15.89355,0.58594 c 3.71089,0.1465 7.00679,0.68361 9.8877,1.61133 2.92963,0.87892 5.37103,2.14845 7.32421,3.80859 2.00189,1.61134 3.51556,3.56446 4.54102,5.85938 1.07415,2.29492 1.61126,4.90722 1.61133,7.83691 -7e-5,3.22265 -0.70808,6.24999 -2.12403,9.08203 -1.41607,2.88085 -3.5401,5.37108 -6.37207,7.47071 -2.83208,2.09958 -6.39653,3.75974 -10.69336,4.98046 -4.24809,1.22068 -9.22855,1.83103 -14.9414,1.83106 -5.56644,-3e-5 -10.32717,-0.43948 -14.28223,-1.31836 -3.90626,-0.87893 -7.12892,-2.09963 -9.66797,-3.66211 -2.49024,-1.56252 -4.32129,-3.4424 -5.49316,-5.63965 -1.12305,-2.14845 -1.68457,-4.51661 -1.68457,-7.10449 0,-1.61134 0.19531,-3.14942 0.58594,-4.61426 0.43945,-1.46485 1.0498,-2.88086 1.83105,-4.24805 0.83007,-1.31835 1.83105,-2.58788 3.00293,-3.80859 1.17187,-1.2207 2.51464,-2.39257 4.02832,-3.51562 -2.09962,-1.12304 -3.73536,-2.63671 -4.90723,-4.54102 -1.12305,-1.95311 -1.68457,-4.07713 -1.68457,-6.37207 0,-2.88084 0.65918,-5.49314 1.97754,-7.83691 1.31835,-2.39255 3.02734,-4.54099 5.12696,-6.44532 -1.709,-1.70895 -3.07618,-3.75973 -4.10157,-6.15234 -1.02539,-2.39254 -1.53809,-5.37105 -1.53808,-8.93555 -10e-6,-3.80854 0.65917,-7.20209 1.97754,-10.18066 1.36718,-3.02728 3.24706,-5.56634 5.63965,-7.61719 2.39256,-2.09954 5.249,-3.68645 8.56933,-4.76074 3.32029,-1.12298 6.98239,-1.6845 10.98633,-1.68457 2.05075,7e-5 4.00387,0.12214 5.85937,0.36621 1.90426,0.24421 3.66207,0.58601 5.27344,1.02539 l 20.72754,0 c 0.83001,7e-5 1.48919,0.53718 1.97754,1.61133 0.53704,1.07428 0.8056,2.88092 0.80566,5.41992 m -23.65722,15.4541 c -5e-5,-3.51557 -0.97661,-6.24994 -2.92969,-8.20312 -1.95316,-1.95307 -4.71195,-2.92963 -8.27637,-2.92969 -1.80667,6e-5 -3.39358,0.31744 -4.76074,0.95215 -1.36721,0.58599 -2.51467,1.41607 -3.44238,2.49023 -0.87893,1.02545 -1.53811,2.24615 -1.97754,3.66211 -0.43948,1.36724 -0.6592,2.80767 -0.65918,4.32129 -2e-5,3.32036 0.97654,5.95707 2.92969,7.91016 1.95309,1.90433 4.66305,2.85648 8.12988,2.85644 1.85543,4e-5 3.46676,-0.29293 4.83398,-0.8789 1.36715,-0.5859 2.4902,-1.39157 3.36914,-2.417 0.9277,-1.02535 1.61129,-2.19722 2.05079,-3.51562 0.48823,-1.36714 0.73237,-2.78316 0.73242,-4.24805 m 4.32129,52.14844 c -5e-5,-2.19727 -0.87896,-3.88184 -2.63672,-5.05371 -1.75786,-1.17187 -4.17485,-1.80664 -7.25098,-1.9043 l -13.11035,-0.36621 c -1.26956,0.92774 -2.29495,1.83106 -3.07617,2.70996 -0.73245,0.83008 -1.3428,1.63574 -1.83106,2.41699 -0.43947,0.78125 -0.73244,1.53809 -0.8789,2.27051 -0.14651,0.73242 -0.21975,1.48925 -0.21973,2.27051 -2e-5,2.4414 1.22068,4.29686 3.66211,5.56641 2.49021,1.26951 5.98142,1.90428 10.47363,1.90429 2.78317,-10e-6 5.12692,-0.29298 7.03125,-0.8789 1.90426,-0.53713 3.44234,-1.26955 4.61426,-2.19727 1.17183,-0.92774 2.00191,-1.97755 2.49023,-3.14941 0.48824,-1.12306 0.73238,-2.31935 0.73243,-3.58887"
style="font-size:150px;fill:#00a9e0;font-family:Calibri;-inkscape-font-specification:Calibri Bold"
id="path3811" />
</g>
</g>
</g>
<g
transform="translate(-154.76465,-3152.2336)"
style="font-size:42px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#00629b;fill-opacity:0.98431373;stroke:none;font-family:Calibri;-inkscape-font-specification:AlArabiya Bold"
id="flowRoot3727">
<path
d="m 574.2041,625.37123 c -2e-5,1.62697 -0.23928,3.08302 -0.71777,4.36817 -0.47854,1.28516 -1.18264,2.37207 -2.11231,3.26074 -0.9297,0.88867 -2.07814,1.56543 -3.44531,2.03027 -1.3672,0.46485 -2.93947,0.69727 -4.7168,0.69727 -1.66798,0 -3.16505,-0.20508 -4.49121,-0.61523 -1.32618,-0.42383 -2.44727,-1.05957 -3.36328,-1.90723 -0.91602,-0.84765 -1.62012,-1.90039 -2.1123,-3.1582 -0.47852,-1.27148 -0.71778,-2.75488 -0.71778,-4.4502 l 0,-16.13965 c 0,-0.13669 0.041,-0.25974 0.12305,-0.36914 0.082,-0.10935 0.22558,-0.19821 0.43066,-0.2666 0.21875,-0.0683 0.49902,-0.12302 0.84082,-0.16406 0.3418,-0.041 0.7793,-0.0615 1.3125,-0.0615 0.51953,3e-5 0.95019,0.0205 1.292,0.0615 0.34179,0.041 0.61522,0.0957 0.82031,0.16406 0.20507,0.0684 0.34862,0.15725 0.43066,0.2666 0.0957,0.1094 0.14355,0.23245 0.14356,0.36914 l 0,15.66797 c -10e-6,1.05274 0.12987,1.96876 0.38965,2.74805 0.25975,0.76563 0.62889,1.40137 1.10742,1.90722 0.49218,0.50587 1.07323,0.88868 1.74316,1.14844 0.68358,0.2461 1.44237,0.36915 2.27637,0.36914 0.84764,10e-6 1.60643,-0.12988 2.27637,-0.38965 0.6699,-0.25976 1.23728,-0.63573 1.70215,-1.12793 0.46482,-0.50585 0.82029,-1.12108 1.0664,-1.8457 0.25975,-0.73827 0.38963,-1.57226 0.38965,-2.50195 l 0,-15.97559 c -2e-5,-0.13669 0.041,-0.25974 0.12305,-0.36914 0.082,-0.10935 0.22556,-0.19821 0.43066,-0.2666 0.20506,-0.0683 0.4785,-0.12302 0.82031,-0.16406 0.35545,-0.041 0.79295,-0.0615 1.3125,-0.0615 0.51951,3e-5 0.94334,0.0205 1.27149,0.0615 0.34177,0.041 0.61521,0.0957 0.82031,0.16406 0.20505,0.0684 0.34861,0.15725 0.43066,0.2666 0.082,0.1094 0.12303,0.23245 0.12305,0.36914 l 0,15.91406"
id="path3814" />
<path
d="m 598.11621,616.77846 c -2e-5,1.49025 -0.23244,2.80959 -0.69726,3.95801 -0.46487,1.14845 -1.14163,2.11915 -2.03028,2.91211 -0.88869,0.77931 -1.98244,1.37403 -3.28125,1.78418 -1.28517,0.41016 -2.80274,0.61524 -4.55273,0.61523 l -2.21485,0 0,8.46973 c 0,0.13672 -0.0479,0.25976 -0.14355,0.36914 -0.082,0.10937 -0.22559,0.19824 -0.43067,0.2666 -0.20508,0.0684 -0.47852,0.12305 -0.82031,0.16406 -0.3418,0.041 -0.7793,0.0615 -1.3125,0.0615 -0.51953,0 -0.95703,-0.0205 -1.3125,-0.0615 -0.3418,-0.041 -0.61524,-0.0957 -0.82031,-0.16406 -0.20508,-0.0684 -0.34864,-0.15723 -0.43066,-0.2666 -0.082,-0.10938 -0.12305,-0.23242 -0.12305,-0.36914 l 0,-23.8711 c 0,-0.64255 0.16406,-1.12106 0.49219,-1.43554 0.34179,-0.3281 0.78613,-0.49216 1.333,-0.49219 l 6.25489,0 c 0.62889,3e-5 1.22362,0.0274 1.78418,0.082 0.5742,0.041 1.2578,0.14358 2.05078,0.30762 0.79295,0.15042 1.59276,0.43752 2.39941,0.86133 0.8203,0.42385 1.51756,0.96389 2.0918,1.62011 0.5742,0.64261 1.0117,1.40139 1.3125,2.27637 0.30076,0.86135 0.45115,1.83205 0.45117,2.91211 m -5.63965,0.38965 c -10e-6,-0.92967 -0.16408,-1.69529 -0.49219,-2.29688 -0.32813,-0.60154 -0.73145,-1.04587 -1.20996,-1.333 -0.47852,-0.28709 -0.98438,-0.46483 -1.51757,-0.53321 -0.51955,-0.082 -1.05959,-0.12302 -1.62012,-0.12304 l -2.29688,0 0,9.00293 2.41993,0 c 0.86131,1e-5 1.57908,-0.1162 2.15332,-0.34864 0.58787,-0.2324 1.06639,-0.55369 1.43554,-0.96386 0.36913,-0.42382 0.6494,-0.92284 0.84082,-1.49707 0.19139,-0.58788 0.2871,-1.22362 0.28711,-1.90723"
id="path3816" />
<path
d="m 633.2666,634.51772 c -3e-5,0.13672 -0.0411,0.25976 -0.12305,0.36914 -0.0684,0.10937 -0.20511,0.19824 -0.41015,0.2666 -0.19144,0.0684 -0.45121,0.12305 -0.7793,0.16406 -0.32816,0.041 -0.74515,0.0615 -1.25098,0.0615 -0.49221,0 -0.90237,-0.0205 -1.23046,-0.0615 -0.32816,-0.041 -0.58792,-0.0957 -0.7793,-0.16406 -0.19144,-0.0684 -0.32815,-0.15723 -0.41016,-0.2666 -0.0821,-0.10938 -0.12307,-0.23242 -0.12304,-0.36914 l 0,-21.59473 -0.041,0 -7.69043,21.57422 c -0.0547,0.17774 -0.14357,0.32813 -0.2666,0.45117 -0.12307,0.10938 -0.29397,0.19825 -0.5127,0.2666 -0.20509,0.0684 -0.4717,0.10938 -0.7998,0.12305 -0.32814,0.0273 -0.72463,0.041 -1.18945,0.041 -0.46487,0 -0.86135,-0.0205 -1.18946,-0.0615 -0.32814,-0.0273 -0.60158,-0.0752 -0.82031,-0.14355 -0.20509,-0.082 -0.36916,-0.17774 -0.49219,-0.28711 -0.12306,-0.10938 -0.20509,-0.23926 -0.24609,-0.38965 l -7.42383,-21.57422 -0.041,0 0,21.59473 c -1e-5,0.13672 -0.041,0.25976 -0.12305,0.36914 -0.0684,0.10937 -0.20509,0.19824 -0.41016,0.2666 -0.20508,0.0684 -0.47168,0.12305 -0.7998,0.16406 -0.31446,0.041 -0.72462,0.0615 -1.23047,0.0615 -0.49219,0 -0.90235,-0.0205 -1.23047,-0.0615 -0.32813,-0.041 -0.59473,-0.0957 -0.7998,-0.16406 -0.19141,-0.0684 -0.32813,-0.15723 -0.41016,-0.2666 -0.0684,-0.10938 -0.10254,-0.23242 -0.10254,-0.36914 l 0,-23.64551 c 0,-0.69724 0.18457,-1.23044 0.55371,-1.59961 0.36914,-0.36911 0.86133,-0.55368 1.47656,-0.55371 l 3.52735,0 c 0.62889,3e-5 1.16893,0.0547 1.62011,0.16406 0.45117,0.0957 0.84081,0.26663 1.16895,0.5127 0.32811,0.23245 0.60155,0.5469 0.82031,0.94336 0.21874,0.38283 0.41015,0.86135 0.57422,1.43554 l 5.74219,15.81153 0.082,0 5.94727,-15.77051 c 0.17771,-0.57419 0.36911,-1.05955 0.57421,-1.45605 0.21873,-0.39646 0.46482,-0.71775 0.73829,-0.96387 0.28708,-0.24607 0.62204,-0.41697 1.00488,-0.5127 0.38278,-0.10935 0.82712,-0.16403 1.33301,-0.16406 l 3.62988,0 c 0.36911,3e-5 0.68356,0.0479 0.94336,0.14356 0.2734,0.0957 0.49215,0.23928 0.65625,0.43066 0.1777,0.17776 0.30758,0.40335 0.38965,0.67676 0.0957,0.25979 0.14352,0.56057 0.14355,0.90234 l 0,23.64551"
id="path3818" />
</g>
<path
transform="matrix(1.6,0,0,1.6,-233,-3460.4252)"
style="fill:#ffffff;fill-opacity:0.98431373;filter:url(#filter3757)"
d="m 445,510.49219 c 0,1.38071 -1.11929,2.5 -2.5,2.5 -1.38071,0 -2.5,-1.11929 -2.5,-2.5 0,-1.38071 1.11929,-2.5 2.5,-2.5 1.38071,0 2.5,1.11929 2.5,2.5 z"
id="path3735" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

@ -0,0 +1,226 @@
<!DOCTYPE html>
<head>
<!-- FAVICON -->
<link rel="shortcut icon" href="http://gsi.dit.upm.es/templates/purity_iii/favicon.ico" type="image/x-icon">
<link rel="icon" href="http://gsi.dit.upm.es/templates/purity_iii/favicon.ico" type="image/x-icon">
<!-- JQUERY -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- BOOTSTRAP 3.3.7 -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- BOOTSTRAP SLIDER -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.9.0/css/bootstrap-slider.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.9.0/bootstrap-slider.js"></script>
<!-- D3.js // DATA-DRIVEN DOCUMENTS -->
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
<script type="text/javascript" src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<!-- JAVASCRIPTS -->
<script type="text/javascript" src="js/visualization.js"></script>
<script type="text/javascript" src="js/timeline.js"></script>
<script type="text/javascript" src="js/socket.js"></script>
<!-- STYLESHEETS -->
<link rel="stylesheet" type="text/css" href="css/main.css">
<link rel="stylesheet" type="text/css" href="css/timeline.css">
<title>{{ model_name }}</title>
<script type="text/javascript">//<![CDATA[
var width = window.innerWidth * 0.75,
height = window.innerHeight * 4 / 5,
speed = 1000,
play,
slider;
window.onload = function() {
"use strict";
// Create svg, timeline and settings
self.GraphVisualization.create('graph');
$('<div>').attr('id', 'load').appendTo('#graph_container').css('left', width / 2 - 25).css('top', height / 2);
$('#configuration').css("height", height);
d3.select('#slider3').attr("width", width).call(
d3.slider().axis(true).min(0).max(100)
);
// Load a file
$('#configuration #file').change(function() {
var file = $('#file')[0].files[0];
self.GraphVisualization.reset();
$('#load').show();
$('.custom-file-control').attr("data-content",
file['name'] || "Choose file..."
);
if ( file['type'] !== "application/x-yaml" ) {
console.log('File format not supported. Sorry for the inconvenience.');
_socket.error('File format not supported. Sorry for the inconvenience.');
return;
} else {
$('.alert.alert-danger').hide();
}
var fileReader = new FileReader();
if (fileReader && file) {
fileReader.readAsText(file);
fileReader.onload = function () {
var content = fileReader.result;
$('#load').show().addClass('loader');
_socket.send(content, 'config_file');
};
}
});
// Select 'trials'
$('.config-item #trials').change(function() {
_socket.send($(this).val(), 'get_trial');
});
// Select 'attributes'
$('.config-item #properties').change(function() {
self.GraphVisualization.update_graph($(this).val(), slider.value(), function() {
update_statistics_table();
})
});
}
///]]
</script>
</head>
<body>
<div class="container-fluid">
<div id="graph_container">
<svg id="graph" class="col-sm-9"></svg>
</div>
<div id="configuration" class="col-sm-3">
<!-- Load File -->
<form enctype="multipart/form-data">
<label class="custom-file">
<input type="file" id="file" name="file" class="custom-file-input">
<span class="custom-file-control" data-content="Choose file..."></span>
</label>
</form>
<hr />
<!-- //Load File -->
<!-- TRIALS -->
<div class="config-item">
Trials:
<select id="trials" class="form-control form-control-sm">
</select>
</div>
<hr />
<!-- //TRIALS -->
<!-- Graph Info -->
<div class="config-item">
<table id="info-graph">
<tbody>
<tr id="nodes"><th>Nodes:</th></tr>
<tr id="links"><th>Links:</th></tr>
</tbody>
</table>
<div class="logo pull-right">
<img src="img/logo_gsi.svg" style="height: 40px;">
</div>
</div>
<hr />
<!-- //Graph Info -->
<!-- PROPIEDADES -->
<div class="config-item">
Propiedades:
<select id="properties" class="form-control form-control-sm">
<optgroup id="properties-dynamic" label="Dynamics"></optgroup>
<optgroup id="properties-static" label="Statics"></optgroup>
</select>
</div>
<hr />
<div class="config-item">
<table id="percentTable">
<tbody><tr><th></th></tr></tbody>
</table>
</div>
<hr />
<!-- //PROPIEDADES -->
<!-- SPEED -->
<div class="config-item">
<table id="speed"><tbody><tr>
<th class="text-left min">min</th>
<th class="text-center">Speed</th>
<th class="text-right max">max</th>
</tr></tbody></table>
<div class="speed-slider">
<input id="speed-slider" type="text" data-slider-min="1" data-slider-max="1000" data-slider-step="0.01" data-slider-value="1000" data-slider-tooltip="hide" data-slider-reversed="true" data-slider-enabled="false" data-slider-scale="logarithmic"/>
</div>
</div>
<hr />
<script type="text/javascript">
$('#speed-slider').slider();
</script>
<!-- //SPEED -->
<!-- LINK DISTANCE -->
<div class="config-item">
<table id="link-distance"><tbody><tr>
<th class="text-left min">min</th>
<th class="text-center">Link Distance</th>
<th class="text-right max">max</th>
</tr></tbody></table>
<div class="link-distance-slider">
<input id="link-distance-slider" type="text" data-slider-min="30" data-slider-max="1000" data-slider-step="0.01" data-slider-value="30" data-slider-tooltip="hide" data-slider-reversed="false" data-slider-enabled="false" data-slider-scale="logarithmic"/>
</div>
</div>
<script type="text/javascript">
$('#link-distance-slider').slider();
</script>
<!-- //LINK DISTANCE -->
</div>
<!-- TIMELINE -->
<div id="timeline" class="col-sm-12">
<div id="slider3" class="pull-left col-sm-9"></div>
<div class="btn-toolbar controls">
<button type="button" id="button_play" class="btn btn-lg">
<span class="glyphicon glyphicon-play" aria-hidden="true"></span>
</button>
<button type="button" id="button_pause" class="btn btn-lg">
<span class="glyphicon glyphicon-pause" aria-hidden="true"></span>
</button>
<button type="button" id="button_zoomFit" class="btn btn-lg">
<span class="glyphicon glyphicon-fullscreen" aria-hidden="true"></span>
</button>
</div>
</div>
<!-- //TIMELINE -->
<!-- ERROR ALERT -->
<div class="alert alert-danger alert-dismissable fade in" style="display: none;">
<a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
<strong>Error! </strong><span id="error-message"></span>
</div>
<!-- //ERROR ALERT -->
</div>
</body>
</html>

@ -0,0 +1,269 @@
// Open the websocket connection
var ws = new WebSocket((window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host + '/ws');
// Open conection with Socket
ws.onopen = function() {
console.log('Connection opened!');
};
// Receive data from server
ws.onmessage = function(message) {
console.log('Message received!');
var msg = JSON.parse(message.data);
switch(msg['type']) {
case 'trials':
$('#load').removeClass('loader');
set_trials(msg['data']);
break;
case 'get_trial':
console.log(msg['data']);
GraphVisualization.import(convertJSON(msg['data']['graph']), msg['data']['models'], function() {
$('#load').hide();
reset_configuration();
set_configuration();
reset_timeline();
set_timeline(msg['data']['graph']);
});
break;
case 'error':
console.log(msg['error']);
_socket.error(msg['error']);
$('#load').removeClass('loader');
default:
console.log('Unexpected message!')
}
}
var _socket = {
send: function(message, type) {
var json = {}
json['type'] = type
json['data'] = message
ws.send(JSON.stringify(json))
},
error: function(message) {
$('#error-message').text(message);
$('.alert.alert-danger').show();
}
};
var set_trials = function(trials) {
for ( i in trials ) {
$('<option>').val(i).text(trials[i]).appendTo('select#trials');
}
// Request first trial as default
_socket.send(0, 'get_trial')
};
var convertJSON = function(json) {
json.links.forEach(function(link) {
link.source = json.nodes[link.source]
link.target = json.nodes[link.target]
});
// Fix spells for nodes
json.nodes.forEach(function(node) {
for (i in node.spells) {
if (node.spells[i][0] > node.spells[i][1]) {
aux = node.spells[i][0];
node.spells[i][0] = node.spells[i][1];
node.spells[i][1] = aux;
}
}
});
return json;
}
var update_statistics_table = function() {
$('#percentTable tbody').empty()
var statisticsSorted = Object.keys(self.GraphVisualization.statistics).sort(function(a,b) {
return self.GraphVisualization.statistics[b] - self.GraphVisualization.statistics[a];
});
for ( var i in statisticsSorted ) {
if ( i <= 5 ) {
// Draw table
var appendTo = '#percentTable > tbody tr:nth-child(' + Number(parseInt(i) + 1) + ')';
var propertyName = (statisticsSorted[i].includes('class')) ?
statisticsSorted[i].split('.').pop().split('\'')[0] : statisticsSorted[i];
$('<tr>').addClass('col-sm-12').appendTo('#percentTable > tbody');
$('<td>').css('background-color', self.GraphVisualization.color(statisticsSorted[i])).addClass('col-sm-1').appendTo(appendTo);
$('<td>').addClass('text-left col-sm-4').text(self.GraphVisualization.statistics[statisticsSorted[i]] + ' %').appendTo(appendTo);
$('<td>').addClass('text-right col-sm-6 property-name').text(propertyName).appendTo(appendTo);
}
}
}
var set_configuration = function() {
// Number of nodes and links info table
$('<tr>').appendTo('#info-graph > tbody');
$('<th>').text('Nodes:').appendTo('#info-graph > tbody tr:nth-child(1)');
$('<th>').text(self.GraphVisualization.nodes).addClass('text-right').appendTo('#info-graph > tbody tr:nth-child(1)');
$('<tr>').appendTo('#info-graph > tbody');
$('<th>').text('Links:').appendTo('#info-graph > tbody tr:nth-child(2)');
$('<th>').text(self.GraphVisualization.links).addClass('text-right').appendTo('#info-graph > tbody tr:nth-child(2)');
// Options of 'Select'
for ( var i in self.GraphVisualization.model['dynamic'] ) {
$('<option>').val(self.GraphVisualization.model['dynamic'][i].title)
.text(self.GraphVisualization.model['dynamic'][i].title).appendTo('#properties-dynamic');
}
for ( var i in self.GraphVisualization.model['static'] ) {
$('<option>').val(self.GraphVisualization.model['static'][i].title)
.text(self.GraphVisualization.model['static'][i].title).appendTo('#properties-static');
}
// Hide optgroups if they are empty
if ( $('#properties-dynamic').children().length === 0 ) $('#properties-dynamic').hide();
if ( $('#properties-static').children().length === 0 ) $('#properties-static').hide();
update_statistics_table();
// Enable 'Link Distance' slider
$('#link-distance-slider').slider('enable').on('change', function(value) {
self.GraphVisualization.set_link_distance(value.value.newValue);
});
}
var reset_configuration = function() {
// Information table about the graph
$('#info-graph > tbody').empty();
// 'Select' for properties
$('#properties-dynamic').empty().show();
$('#properties-static').empty().show();
// 'Link Distance' slider
$('#link-distance-slider').slider('disable').slider('setValue', 30);
}
var set_timeline = function(graph) {
// 'Timeline' slider
var [min, max] = get_min_and_max(graph);
var stepUnix = (max - min) / 200;
var minUnix = (min !== Math.min()) ? min : 0;
var maxUnix = (max !== Math.max()) ? max : minUnix + 20;
slider = d3.slider();
d3.select('#slider3').attr('width', width).call(
slider.axis(true).min(minUnix).max(maxUnix).step(stepUnix).value(maxUnix)
.on('slide', function(evt, value) {
self.GraphVisualization.update_graph($('.config-item #properties').val(), value, function() {
update_statistics_table();
});
})
);
// Draw graph for the first time
self.GraphVisualization.update_graph($('.config-item #properties').val(), maxUnix, function() {
update_statistics_table();
});
// 'Speed' slider
$('#speed-slider').slider('enable').on('change', function(value) {
speed = value.value.newValue;
});
// Button 'Play'
$('button#button_play').on('click', function() {
$('button#button_play').addClass('pressed').prop("disabled", true);
$('#speed-slider').slider('disable');
slider.step( 1 / speed );
if (slider.value() >= maxUnix) {
slider.value(minUnix);
self.GraphVisualization.update_graph($('.config-item #properties').val(), slider.value(), function() {
update_statistics_table();
});
setTimeout(player, 1000);
} else {
player();
}
var i = slider.value();
function player() {
clearInterval(play);
play = setInterval(function() {
self.GraphVisualization.update_graph($('.config-item #properties').val(), slider.value(), function() {
update_statistics_table();
});
if (slider.value() + slider.step() >= maxUnix) {
slider.value(maxUnix);
slider.step(stepUnix);
clearInterval(play);
$('button#button_play').removeClass('pressed').prop("disabled", false);
$('#speed-slider').slider('enable');
} else {
slider.value(i);
i += slider.step();
}
}, 5);
}
});
// Button 'Pause'
$('button#button_pause').on('click', function() {
clearInterval(play);
slider.step(stepUnix);
$('button#button_play').removeClass('pressed').prop("disabled", false);
$('#speed-slider').slider('enable');
});
// Button 'Zoom to Fit'
$('button#button_zoomFit').click(function() { self.GraphVisualization.fit(); });
}
var reset_timeline = function() {
// 'Timeline' slider
$('#slider3').html('');
// 'Speed' slider
$('#speed-slider').slider('disable').slider('setValue', 1000);
// Buttons
clearInterval(play);
$('button#button_play').off().removeClass('pressed').prop("disabled", false);
$('button#button_pause').off();
$('button#button_zoomFit').off();
}
var get_min_and_max = function(graph) {
var max = Math.max();
var min = Math.min()
graph.links.forEach(function(link) {
if (link.end > max) max = link.end
if (link.start > max) max = link.start
if (link.end < min) min = link.end
if (link.start < min) min = link.start
});
graph.nodes.forEach(function(node) {
for (property in node) {
if ( Array.isArray(node[property]) ) {
for (i in node[property]) {
for (j in node[property][i]) {
if (node[property][i][j] > max) max = node[property][i][j];
if (node[property][i][j] < min) min = node[property][i][j];
}
}
}
}
})
return [min, max];
}

@ -0,0 +1,429 @@
/*
D3.js Slider
Inspired by jQuery UI Slider
Copyright (c) 2013, Bjorn Sandvik - http://blog.thematicmapping.org
BSD license: http://opensource.org/licenses/BSD-3-Clause
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['d3'], factory);
} else if (typeof exports === 'object') {
if (process.browser) {
// Browserify. Import css too using cssify.
require('./d3.slider.css');
}
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory(require('d3'));
} else {
// Browser globals (root is window)
root.d3.slider = factory(root.d3);
}
}(this, function (d3) {
return function module() {
"use strict";
// Public variables width default settings
var min = 0,
max = 100,
step = 0.01,
animate = true,
orientation = "horizontal",
axis = false,
margin = 50,
value,
active = 1,
snap = false,
scale;
// Private variables
var axisScale,
dispatch = d3.dispatch("slide", "slideend"),
formatPercent = d3.format(".2%"),
tickPadding = 5,
tickFormat = d3.format(".0"),
handle1,
handle2 = null,
divRange,
sliderLength;
function slider(selection) {
selection.each(function() {
// Create scale if not defined by user
if (!scale) {
scale = d3.scale.linear().domain([min, max]);
}
// Start value
value = value || scale.domain()[0];
// DIV container
var div = d3.select(this).classed("d3-slider d3-slider-" + orientation, true);
var drag = d3.behavior.drag();
drag.on('dragend', function () {
dispatch.slideend(d3.event, value);
})
// Slider handle
//if range slider, create two
// var divRange;
if (toType(value) == "array" && value.length == 2) {
handle1 = div.append("a")
.classed("d3-slider-handle", true)
.attr("xlink:href", "#")
.attr('id', "handle-one")
.on("click", stopPropagation)
.call(drag);
handle2 = div.append("a")
.classed("d3-slider-handle", true)
.attr('id', "handle-two")
.attr("xlink:href", "#")
.on("click", stopPropagation)
.call(drag);
} else {
handle1 = div.append("a")
.classed("d3-slider-handle", true)
.attr("xlink:href", "#")
.attr('id', "handle-one")
.on("click", stopPropagation)
.call(drag);
}
// Horizontal slider
if (orientation === "horizontal") {
div.on("click", onClickHorizontal);
if (toType(value) == "array" && value.length == 2) {
divRange = d3.select(this).append('div').classed("d3-slider-range", true);
handle1.style("left", formatPercent(scale(value[ 0 ])));
divRange.style("left", formatPercent(scale(value[ 0 ])));
drag.on("drag", onDragHorizontal);
var width = 100 - parseFloat(formatPercent(scale(value[ 1 ])));
handle2.style("left", formatPercent(scale(value[ 1 ])));
divRange.style("right", width+"%");
drag.on("drag", onDragHorizontal);
} else {
handle1.style("left", formatPercent(scale(value)));
drag.on("drag", onDragHorizontal);
}
sliderLength = parseInt(div.style("width"), 10);
} else { // Vertical
div.on("click", onClickVertical);
drag.on("drag", onDragVertical);
if (toType(value) == "array" && value.length == 2) {
divRange = d3.select(this).append('div').classed("d3-slider-range-vertical", true);
handle1.style("bottom", formatPercent(scale(value[ 0 ])));
divRange.style("bottom", formatPercent(scale(value[ 0 ])));
drag.on("drag", onDragVertical);
var top = 100 - parseFloat(formatPercent(scale(value[ 1 ])));
handle2.style("bottom", formatPercent(scale(value[ 1 ])));
divRange.style("top", top+"%");
drag.on("drag", onDragVertical);
} else {
handle1.style("bottom", formatPercent(scale(value)));
drag.on("drag", onDragVertical);
}
sliderLength = parseInt(div.style("height"), 10);
}
if (axis) {
createAxis(div);
}
function createAxis(dom) {
// Create axis if not defined by user
if (typeof axis === "boolean") {
axis = d3.svg.axis()
.ticks(Math.round(sliderLength) / 100)
.tickFormat(tickFormat)
.tickPadding(tickPadding)
.orient((orientation === "horizontal") ? "bottom" : "right");
}
// Copy slider scale to move from percentages to pixels
axisScale = scale.ticks ? scale.copy().range([0, sliderLength]) : scale.copy().rangePoints([0, sliderLength], 0.5);
axis.scale(axisScale);
// Create SVG axis container
var svg = dom.append("svg")
.classed("d3-slider-axis d3-slider-axis-" + axis.orient(), true)
.on("click", stopPropagation);
var g = svg.append("g");
// Horizontal axis
if (orientation === "horizontal") {
svg.style("margin-left", -margin - 16 + "px");
svg.attr({
width: sliderLength + margin * 2,
height: margin + 30
});
if (axis.orient() === "top") {
svg.style("top", -margin + "px");
g.attr("transform", "translate(" + margin + "," + margin + ")");
} else { // bottom
g.attr("transform", "translate(" + margin + ",0)");
}
} else { // Vertical
svg.style("top", -margin + "px");
svg.attr({
width: margin,
height: sliderLength + margin * 2
});
if (axis.orient() === "left") {
svg.style("left", -margin + "px");
g.attr("transform", "translate(" + margin + "," + margin + ")");
} else { // right
g.attr("transform", "translate(" + 0 + "," + margin + ")");
}
}
g.call(axis);
}
function onClickHorizontal() {
if (toType(value) != "array") {
var pos = Math.max(0, Math.min(sliderLength, d3.event.offsetX || d3.event.layerX));
moveHandle(scale.invert ?
stepValue(scale.invert(pos / sliderLength))
: nearestTick(pos / sliderLength));
}
}
function onClickVertical() {
if (toType(value) != "array") {
var pos = sliderLength - Math.max(0, Math.min(sliderLength, d3.event.offsetY || d3.event.layerY));
moveHandle(scale.invert ?
stepValue(scale.invert(pos / sliderLength))
: nearestTick(pos / sliderLength));
}
}
function onDragHorizontal() {
if ( d3.event.sourceEvent.target.id === "handle-one") {
active = 1;
} else if ( d3.event.sourceEvent.target.id == "handle-two" ) {
active = 2;
}
var pos = Math.max(0, Math.min(sliderLength, d3.event.x));
moveHandle(scale.invert ?
stepValue(scale.invert(pos / sliderLength))
: nearestTick(pos / sliderLength));
}
function onDragVertical() {
if ( d3.event.sourceEvent.target.id === "handle-one") {
active = 1;
} else if ( d3.event.sourceEvent.target.id == "handle-two" ) {
active = 2;
}
var pos = sliderLength - Math.max(0, Math.min(sliderLength, d3.event.y))
moveHandle(scale.invert ?
stepValue(scale.invert(pos / sliderLength))
: nearestTick(pos / sliderLength));
}
function stopPropagation() {
d3.event.stopPropagation();
}
});
}
// Move slider handle on click/drag
function moveHandle(newValue) {
var currentValue = toType(value) == "array" && value.length == 2 ? value[active - 1]: value,
oldPos = formatPercent(scale(stepValue(currentValue))),
newPos = formatPercent(scale(stepValue(newValue))),
position = (orientation === "horizontal") ? "left" : "bottom";
if (oldPos !== newPos) {
if (toType(value) == "array" && value.length == 2) {
value[ active - 1 ] = newValue;
if (d3.event) {
dispatch.slide(d3.event, value );
};
} else {
if (d3.event) {
dispatch.slide(d3.event.sourceEvent || d3.event, value = newValue);
};
}
if ( value[ 0 ] >= value[ 1 ] ) return;
if ( active === 1 ) {
if (toType(value) == "array" && value.length == 2) {
(position === "left") ? divRange.style("left", newPos) : divRange.style("bottom", newPos);
}
if (animate) {
handle1.transition()
.styleTween(position, function() { return d3.interpolate(oldPos, newPos); })
.duration((typeof animate === "number") ? animate : 250);
} else {
handle1.style(position, newPos);
}
} else {
var width = 100 - parseFloat(newPos);
var top = 100 - parseFloat(newPos);
(position === "left") ? divRange.style("right", width + "%") : divRange.style("top", top + "%");
if (animate) {
handle2.transition()
.styleTween(position, function() { return d3.interpolate(oldPos, newPos); })
.duration((typeof animate === "number") ? animate : 250);
} else {
handle2.style(position, newPos);
}
}
}
}
// Calculate nearest step value
function stepValue(val) {
if (val === scale.domain()[0] || val === scale.domain()[1]) {
return val;
}
var alignValue = val;
if (snap) {
alignValue = nearestTick(scale(val));
} else{
var valModStep = (val - scale.domain()[0]) % step;
alignValue = val - valModStep;
if (Math.abs(valModStep) * 2 >= step) {
alignValue += (valModStep > 0) ? step : -step;
}
};
return alignValue;
}
// Find the nearest tick
function nearestTick(pos) {
var ticks = scale.ticks ? scale.ticks() : scale.domain();
var dist = ticks.map(function(d) {return pos - scale(d);});
var i = -1,
index = 0,
r = scale.ticks ? scale.range()[1] : scale.rangeExtent()[1];
do {
i++;
if (Math.abs(dist[i]) < r) {
r = Math.abs(dist[i]);
index = i;
};
} while (dist[i] > 0 && i < dist.length - 1);
return ticks[index];
};
// Return the type of an object
function toType(v) {
return ({}).toString.call(v).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
};
// Getter/setter functions
slider.min = function(_) {
if (!arguments.length) return min;
min = _;
return slider;
};
slider.max = function(_) {
if (!arguments.length) return max;
max = _;
return slider;
};
slider.step = function(_) {
if (!arguments.length) return step;
step = _;
return slider;
};
slider.animate = function(_) {
if (!arguments.length) return animate;
animate = _;
return slider;
};
slider.orientation = function(_) {
if (!arguments.length) return orientation;
orientation = _;
return slider;
};
slider.axis = function(_) {
if (!arguments.length) return axis;
axis = _;
return slider;
};
slider.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return slider;
};
slider.value = function(_) {
if (!arguments.length) return value;
if (value) {
moveHandle(stepValue(_));
};
value = _;
return slider;
};
slider.snap = function(_) {
if (!arguments.length) return snap;
snap = _;
return slider;
};
slider.scale = function(_) {
if (!arguments.length) return scale;
scale = _;
return slider;
};
d3.rebind(slider, dispatch, "on");
return slider;
}
}));

@ -0,0 +1,426 @@
;(function(undefined) {
"use strict";
/**
* Graph Visualization
* ===================
*
* Author: Tasio Méndez (tasiomendez)
* URL: https://github.com/tasiomendez/
* Version: 0.1
*/
// Private constants
var width = window.innerWidth * 0.75,
height = window.innerHeight * 4 / 5,
focus_opacity = 0.1,
radius = 8;
// Private variables
var graph, // JSON data for the graph
model, // Definition of the attributes of the nodes
linkedByIndex, // Nodes linked by index
name, // Name of the graph (id for svg item)
svg, // Svg item
force, // Set up the force layout
color, // Color for nodes
zoom, // Zoom
groot, // Append sections to svg to have nodes and edges separately
glinks,
gnodes,
data_node, // Actual node data for the graph
data_link, // Actual link data for the graph
link, // Line svgs
node; // Circles for the nodes
Number.prototype.between = function(min, max) {
var min = (min) ? min : Math.max(),
max = (max) ? max : Math.min();
return this > min && this <= max;
};
var lastFocusNode;
var _helpers = {
set_node: function(node, property) {
// Add nodes if data has more nodes than before
node.enter().append('circle')
.attr('class', 'node')
.attr('r', radius)
.style('fill', function (d) {
if ( Array.isArray(d[property]) ) {
return color(d[property][0][0]);
} else {
return color(d[property]);
}
})
// Cancel zoom movement so you can move the node
.on('mousedown', function(d) {
d3.event.stopPropagation();
})
// Double-click to focus neighbours
.on('dblclick', function(d) {
d3.event.stopPropagation();
if (d === lastFocusNode) {
lastFocusNode = undefined;
node.style('opacity', 1);
link.style('opacity', 1);
} else {
lastFocusNode = d;
_helpers.set_focus(d);
}
}).call(force.drag);
// Remove nodes if data has less nodes than before
node.exit().remove();
// Update existing nodes
node.attr('class', 'node')
.attr('r', radius)
.style('fill', function (d) {
if ( Array.isArray(d[property]) ) {
return color(d[property][0][0]);
} else {
return color(d[property]);
}
});
},
set_link: function(link) {
// Remove links if data has more links than before
link.enter().append('line')
.attr('class', 'link')
.style('stroke-width', function (d) {
return Math.sqrt(d.value);
});
// Remove links if data has less links than before
link.exit().remove();
},
isConnected: function(source, neighbour) {
return linkedByIndex[source.id + ',' + neighbour.id] ||
linkedByIndex[neighbour.id + ',' + source.id];
},
set_focus: function(d) {
node.style('opacity', function(o) {
return _helpers.isConnected(d,o) || d.index == o.index ? 1 : focus_opacity;
});
link.style('opacity', function(o) {
return o.source.index == d.index || o.target.index == d.index ? 1 : focus_opacity;
});
}
}
/**
* Graph Visualization Core Functions
* ----------------------------------
*
* The graph visualization functions themselves.
*/
function Graph() {
// Color
color = d3.scale.category20();
// Set up the force layout
force = d3.layout.force()
.charge(-500)
.linkDistance(30)
.size([width, height]);
// Append sections to svg to have nodes and edges separately
groot = svg.append('g') .attr('id', 'root');
glinks = groot.append('g') .attr('id', 'links');
gnodes = groot.append('g') .attr('id', 'nodes');
// Zoom
zoom = d3.behavior
.zoom()
.scaleExtent([1/10, 10])
.on('zoom', function () {
//console.trace("zoom", d3.event.translate, d3.event.scale);
groot.attr('transform',
'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')');
});
// Activate zoom for the svg item
svg.style('background-color', 'rgb(255,255,255)')
.call(zoom);
// Update linkedByIndex
linkedByIndex = {};
graph.links.forEach(function(d) {
linkedByIndex[d.source.id + ',' + d.target.id] = true;
});
// Creates the graph data structure out of the json data
force.nodes(graph.nodes)
.links(graph.links)
.start();
// Now we are giving the SVGs coordinates - the force layout is generating the coordinates
// which this code is using to update the attributes of the SVG elements
force.on('tick', function () {
link.attr('x1', function (d) {
return d.source.x;
}).attr('y1', function (d) {
return d.source.y;
}).attr('x2', function (d) {
return d.target.x;
}).attr('y2', function (d) {
return d.target.y;
});
node.attr('transform', function translate(d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
});
}
function update_data(property, time) {
// Reset data
var delete_links = true;
data_node = [];
data_link = graph.links.slice();
// Nodes
graph.nodes.forEach(function(node) {
if (Array.isArray(node.spells)) {
node.spells.forEach( function(d) {
if ( time.between(d[0], d[1]) ) {
data_node.push(node);
} else {
graph.links.forEach(function(link) {
if (link.source === node || link.target === node)
data_link.splice(data_link.indexOf(link), 1);
});
}
});
} else {
data_node.push(node);
}
});
// Links
graph.links.forEach(function(link) {
if ( !(time.between(link.start, link.end)) && data_link.includes(link) )
data_link.splice(data_link.indexOf(link), 1);
});
// Reset force
force.stop()
.nodes(data_node)
.links(data_link)
.start();
// Create all the line svgs but without locations
link = glinks.selectAll('.link').data(data_link);
_helpers.set_link(link);
// Do the same with the circles for the nodes - no
node = gnodes.selectAll('.node').data(data_node);
_helpers.set_node(node, property);
// Node Attributes
var statistics = {}
self.GraphVisualization.statistics = {};
data_node.forEach(function(n) {
// Count node properties
if ( Array.isArray(n[property]) ) {
statistics[n[property][0][0]] = (!statistics[n[property][0][0]]) ? 1 : statistics[n[property][0][0]] + 1;
} else {
statistics[n[property]] = (!statistics[n[property]]) ? 1 : statistics[n[property]] + 1;
}
});
for ( i in statistics ) {
statistics[i] = (statistics[i] / data_node.length * 100).toFixed(2);
}
self.GraphVisualization.statistics = statistics
}
/**
* Public API
* -----------
*
* User-accessible functions.
*/
/**
* Create the space where the graph will we drawn.
* A function that identifies the svg item.
*
* @param {object} id The id of the svg item.
* @return {object} This class.
*/
function create(id, callback) {
name = id;
svg = d3.select('svg#' + name)
.attr('width', width)
.attr('height', height)
.style('background-color', 'rgba(128,128,128,0.1)');
if (callback) { callback(this.GraphVisualization); }
else { return this.GraphVisualization }
}
/**
* Import JSON and attributes.
* A function that imports the graph and the attributes of all the nodes.
*
* @param {object} json The json structure of the graph.
* @param {object} attributes Definition of the attributes of the nodes
* (statics and dynamics).
* @param {object} callback A function called at the end.
*/
function importJSON(json, attributes, callback) {
reset()
graph = json;
model = attributes
// Create the graph itself
Graph();
self.GraphVisualization.nodes = graph.nodes.length;
self.GraphVisualization.links = graph.links.length;
self.GraphVisualization.model = model
// Draw graph with default property and time for the first time
update_data(model.dynamic[0].title, 0)
if (callback) { callback(); }
}
/**
* Set link distance.
* A function that set the link distance. If it is not called, it uses 30 as default
*
* @param {object} distance Distance.
* @param {object} callback A function called at the end.
*/
function set_link_distance(distance, callback) {
if (graph) {
force.stop().linkDistance(distance).start();
// Update radius of the nodes to see them better
var r = d3.scale.linear().domain([30, 1000]).range([8, 24]);
radius = r(distance);
node.attr('r', radius);
if (callback) { callback(radius); }
}
}
/**
* Set property and instant of time.
* A function that draws the graph depends on the property and instant of time selected.
*
* @param {object} property Property to show.
* @param {object} time Instant of time.
* @param {object} callback A function called at the end.
*/
function update_graph(property, time, callback) {
if (graph) {
update_data(property, time);
if (callback) { callback(); }
}
}
/**
* Adjust the graph to the whole area.
* A function that adjust the graph to the svg item.
*
* @param {object} padding Space from the graph to the border.
* 85% by default.
* @param {object} transition Duration of the zoom action.
* 750 milliseconds by default.
* @param {object} callback A function called at the end.
*/
function zoom_to_fit(padding, transition, callback) {
var bounds = groot.node().getBBox();
var parent = groot.node().parentElement;
var fullWidth = parent.clientWidth,
fullHeight = parent.clientHeight;
var widthBounds = bounds.width,
heightBounds = bounds.height;
var midX = bounds.x + widthBounds / 2,
midY = bounds.y + heightBounds / 2;
if (widthBounds == 0 || heightBounds == 0) return; // nothing to fit
var scale = (padding || 0.85) / Math.max(widthBounds / fullWidth, heightBounds / fullHeight);
var translate = [fullWidth / 2 - scale * midX, fullHeight / 2 - scale * midY];
//console.trace("zoomFit", translate, scale);
groot
.transition()
.duration(transition || 750) // milliseconds
.call(zoom.translate(translate).scale(scale).event);
if (callback) { callback(); }
}
/**
* Reset the whole graph.
* A function that reset the svg item.
*
*/
function reset() {
d3.select('svg#' + name)
.html('')
.attr('width', width)
.attr('height', height)
.style('background-color', 'rgba(128,128,128,0.1)');
}
/**
* Get color for a value.
* A function that get the color of a node or a group of nodes.
*
* @param {object} value Value.
* @return {object} color The color in hexadecimal.
*/
function color(value) {
if (graph) {
return color(value);
}
}
/**
* Exporting
* ---------
*/
this.GraphVisualization = {
// Functions
create: create,
import: importJSON,
update_graph: update_graph,
set_link_distance: set_link_distance,
fit: zoom_to_fit,
reset: reset,
// Attributes
model: {},
nodes: undefined,
links: undefined,
// Getters
color: color,
// Statistics
statistics: {},
// Version
version: '0.1'
};
}).call(this);

@ -0,0 +1,20 @@
var _helpers = {
attributesInterval: function (attributes, callback) {
for ( var property in attributes ) {
for ( var i = 0; i < attributes[property].length; i++ ) {
attributes[property][i].interval.forEach(function(d) {
callback(d);
});
}
}
},
dynamicAttsToArray: function(atts) {
var array = []
for ( var property in atts ) {
array.push(property)
}
return array;
}
};

@ -0,0 +1,561 @@
//<![CDATA[
window.onload = function() {
"use strict";
// Public variables (User Configuration)
var colorProperty,
speed = 1000;
// Private variables
var width = window.innerWidth * 0.75,
height = window.innerHeight * 4 / 5,
focus_opacity = 0.1,
radius = 8;
// Private Graph variables
var color, // Color for nodes
zoom, // Zoom
force, // Set up the force layout
svg, // Svg item
groot, // Append sections to svg to have nodes and edges separately
glinks,
gnodes,
data_node, // Actual node data for the graph
data_link, // Actual link data for the graph
graph, // Graph
atts, // Dynamic attributes if it is necessary
linkedByIndex, // Nodes linked by index
link, // Line svgs
node; // Circles for the nodes
// Private Timeline variables
var minInterval, // Min value of the graph
maxInterval, // Max value of the graph
minUnix, // Min value for the timeline
maxUnix, // Max value for the timeline
stepUnix, // Step value for the timeline
slider, // Slider
time, // Variable to calculate date intervals
play; // Constant for the interval to play the simulation
d3.select('#graph')
.attr("width", width)
.attr("height", height);
d3.select('#slider3').attr("width", width).call(d3.slider().axis(true)
.min(0).max(100)
);
$('.load').css("left", width / 2 - 25)
.css("top", height / 2);
$('#configuration').css("height", height)
loader(false);
$('#file').change(function() {
var index = $('.custom-file-input').val().lastIndexOf("\\") + 1;
$('.custom-file-control').attr("data-content",
$('.custom-file-input').val().substr(index) || "Choose file...");
$.ajax({
url: 'upload.php', // point to server-side PHP script
type: 'POST',
data: new FormData($('form')[0]),
timeout: 3000,
cache: false,
contentType: false,
processData: false,
beforeSend: function() {
clearPreviousGraph();
}
}).done(function(response) {
// Show loader
loader(true);
$('.alert-danger').hide();
// Load File
setTimeout(function() {
loadFile(response);
}, 100);
}).fail(function(response) {
loader(false);
$('#error-message').text(response.responseText || "Connection timed out");
$('.alert-danger').show();
});
});
function clearPreviousGraph() {
$('#graph').html('');
$('#slider3').html('');
$('#properties-dynamic').html('').show();
$('#properties-static').html('').show();
$('#percentTable > tbody').empty();
$('#info-graph > tbody').empty();
$('#info-graph > tbody').empty();
}
function loader(show) {
$('.load').show();
// Show loader
if (show) {
$('svg#graph').css("background-color", "white");
$('.load').text("").addClass("loader");
// Show "No File"
} else {
$('svg#graph').css("background-color", "rgba(128,128,128,0.1)");
$('.load').text("No file").removeClass("loader");
}
}
=====================================================================================================================================================================
function loadFile(file) {
/**
* GRAPH
*/
// Color
color = d3.scale.category20();
// Zoom
zoom = d3.behavior
.zoom()
.scaleExtent([1/10, 10])
.on('zoom', function () {
//console.trace("zoom", d3.event.translate, d3.event.scale);
groot.attr('transform',
'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')');
});
// Set up the force layout
force = d3.layout.force()
.charge(-500)
.linkDistance(30)
.size([width, height]);
// Activate zoom for the svg item
svg = d3.select('#graph')
.attr("width", width)
.attr("height", height)
.call(zoom);
// Append sections to svg to have nodes and edges separately
groot = svg.append("g") .attr("id", "root");
glinks = groot.append("g") .attr("id", "links");
gnodes = groot.append("g") .attr("id", "nodes");
// Read the data from the graph
graph = GexfParser.fetch(file);
console.log("Graph", graph);
if (graph.mode === "dynamic") {
atts = GexfParser.dynamic(file, graph.timeformat)
colorProperty = graph.model[0].title;
console.log("Dynamic Attributes", atts);
} else {
atts = null;
}
// Number of nodes and links info table
$('<tr>').appendTo('#info-graph > tbody');
$('<th>').text('Nodes:').appendTo('#info-graph > tbody tr:nth-child(1)');
$('<th>').text(graph.nodes.length).addClass('text-right').appendTo('#info-graph > tbody tr:nth-child(1)');
$('<tr>').appendTo('#info-graph > tbody');
$('<th>').text('Links:').appendTo('#info-graph > tbody tr:nth-child(2)');
$('<th>').text(graph.links.length).addClass('text-right').appendTo('#info-graph > tbody tr:nth-child(2)');
// Update linkedByIndex
linkedByIndex = {};
graph.links.forEach(function(d) {
linkedByIndex[d.source + "," + d.target] = true;
});
// Creates the graph data structure out of the json data
force.nodes(graph.nodes)
.links(graph.links)
.start();
// Create all the line svgs but without locations yet
link = glinks.selectAll(".link").data(graph.links);
set_link(link);
// Do the same with the circles for the nodes - no
var lastFocusNode;
node = gnodes.selectAll(".node").data(graph.nodes);
set_node(node);
// Now we are giving the SVGs coordinates - the force layout is generating the coordinates which this code is using to update the attributes
// of the SVG elements
force.on("tick", function () {
link.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
node.attr('transform', function translate(d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
});
function set_node(node) {
// Add nodes if the data has more nodes than before
node.enter().append("circle")
.attr("class", "node")
.attr("r", radius)
.style("fill", function (d) {
return color(d.attributes[colorProperty]);
})
// Cancel zoom movement so you can move the node
.on("mousedown", function(d) {
d3.event.stopPropagation();
})
// Double-click to focus neighbours
.on("dblclick", function(d) {
d3.event.stopPropagation();
if (d === lastFocusNode) {
lastFocusNode = undefined;
node.style("opacity", 1);
link.style("opacity", 1);
} else {
lastFocusNode = d;
set_focus(d);
}
}).call(force.drag);
// Remove nodes if the data has less nodes than before
node.exit().remove();
}
function set_link(link) {
// Remove links if the data has more links than before
link.enter().append("line")
.attr("class", "link")
.style("stroke-width", function (d) {
return Math.sqrt(d.value);
});
// Remove links if the data has less links than before
link.exit().remove();
}
function set_focus(d) {
node.style("opacity", function(o) {
return isConnected(d,o) || d.index == o.index ? 1 : focus_opacity;
});
link.style("opacity", function(o) {
return o.source.index == d.index || o.target.index == d.index ? 1 : focus_opacity;
});
function isConnected(source, neighbour) {
return linkedByIndex[source.index + "," + neighbour.index] ||
linkedByIndex[neighbour.index + "," + source.index];
}
}
=====================================================================================================================================================================
/**
* TIMELINE
*/
// Maximum and minimum of all the intervals
minInterval = Math.min();
maxInterval = Math.max();
_helpers.attributesInterval(atts, function(d) {
if ( d[0] < minInterval && d[0] != -Infinity ) minInterval = d[0];
if ( d[1] < minInterval && d[0] != +Infinity ) minInterval = d[1];
if ( d[0] > maxInterval && d[0] != -Infinity ) maxInterval = d[0];
if ( d[1] > maxInterval && d[1] != +Infinity ) maxInterval = d[1];
});
// Transform dates to ints
if ( graph.timeformat === "date" ) {
time = d3.scale.linear().domain([minInterval, maxInterval]).range([1, 20]);
// Attributes
_helpers.attributesInterval(atts, function(d) {
if (d[0] !== -Infinity) d[0] = time(d[0]);
if (d[1] !== +Infinity) d[1] = time(d[1]);
});
// Nodes
graph.nodes.forEach( function(node) {
if (Array.isArray(node.spell)) {
node.spell.forEach( function(d) {
if (d[0] !== -Infinity) d[0] = time(d[0]);
if (d[1] !== +Infinity) d[1] = time(d[1]);
});
}
});
// Links
graph.links.forEach( function(link) {
if (link.spell[0] !== -Infinity) link.spell[0] = time(link.spell[0]);
if (link.spell[1] !== +Infinity) link.spell[1] = time(link.spell[1]);
});
minInterval = 1;
maxInterval = 20;
}
stepUnix = (maxInterval - minInterval) / 200;
minUnix = (minInterval !== Math.min()) ? minInterval - 2 * stepUnix : 0;
maxUnix = (maxInterval !== Math.max()) ? maxInterval + 2 * stepUnix : minUnix + 20;
// Create the slider
slider = d3.slider();
d3.select("#slider3").attr("width", width).call(slider.axis(true)
.min(minUnix).max(maxUnix).step(stepUnix).value(maxUnix)
.on("slide", function(evt, value) {
updateData(value);
})
);
function updateData(value) {
var statics = {};
$('#percentTable > tbody').empty();
// Reset data
var delete_links = true;
data_node = [];
data_link = graph.links.slice();
// Nodes
graph.nodes.forEach(function(n) {
if (Array.isArray(n.spell)) {
n.spell.forEach( function(d) {
if (d[0] < value && value <= d[1]) {
data_node.push(n);
delete_links = false;
}
});
if (delete_links) {
graph.links.forEach(function(e) {
if (e.source === n || e.target === n) {
data_link.splice(data_link.indexOf(e), 1);
}
});
}
} else {
data_node.push(n);
}
});
// Links
graph.links.forEach(function(e) {
if ( !(e.spell[0] < value && value <= e.spell[1]) && data_link.includes(e) )
data_link.splice(data_link.indexOf(e), 1);
});
// Reset force
force.stop()
.nodes(data_node)
.links(data_link)
.start();
// Redraw Graph
link = glinks.selectAll(".link").data(data_link);
set_link(link);
node = gnodes.selectAll(".node").data(data_node);
set_node(node);
// Node Attributes
data_node.forEach(function(n) {
for (var property in atts) {
var interval = atts[property][n.index].interval
for ( var i = 0; i < interval.length; i++ ) {
//console.log(interval[i][0], value, interval[i][1], "->", interval[i][2])
if (interval[i][0] < value && value <= interval[i][1])
n.attributes[property] = interval[i][2];
else if (value === minUnix)
n.attributes[property] = interval[0][2];
}
}
// Count node properties
statics[n.attributes[colorProperty]] = (!statics[n.attributes[colorProperty]]) ? 1 :
statics[n.attributes[colorProperty]] + 1;
});
node.style("fill", function (d) {
return color(d.attributes[colorProperty]);
});
// Show the five properties with more percentage
var staticsSorted = Object.keys(statics).sort(function(a,b) {
return statics[b] - statics[a];
});
for ( var i = 0; ( i < staticsSorted.length ) && ( i < 5 ); i++ ) {
var percent = statics[staticsSorted[i]] / data_node.length * 100;
var propertyName = (staticsSorted[i].includes("class")) ?
staticsSorted[i].split('.').pop().split('\'')[0] : staticsSorted[i];
// Draw table every time
var appendTo = '#percentTable > tbody tr:nth-child(' + Number(i + 1) + ')';
$('<tr>').addClass('col-sm-12').appendTo('#percentTable > tbody');
$('<td>').css("background-color", color(staticsSorted[i])).addClass('col-sm-1').attr('data-value', staticsSorted[i]).appendTo(appendTo);
$('<td>').addClass('text-left col-sm-4').text(percent.toFixed(2) + " %").appendTo(appendTo);
$('<td>').addClass('text-right col-sm-6 property-name').text(propertyName).appendTo(appendTo);
}
return;
}
=====================================================================================================================================================================
/**
* PAGE ELEMENTS
*/
$('.load').hide();
$('button#button_play').on('click', function() {
$('button#button_play').addClass('pressed').prop("disabled", true);
$('#speed-slider').slider('disable');
slider.step( 1 / speed );
if (slider.value() >= maxUnix) {
slider.value(minUnix);
updateData(slider.value());
setTimeout(player, 2000);
} else {
player();
}
var i = slider.value();
function player() {
clearInterval(play);
play = setInterval(function() {
if (slider.value() + slider.step() >= maxUnix) {
slider.value(maxUnix);
slider.step(stepUnix);
clearInterval(play);
$('button#button_play').removeClass('pressed').prop("disabled", false);
$('#speed-slider').slider('enable');
} else {
updateData(slider.value());
slider.value(i);
i += slider.step();
}
}, 5);
}
});
$('button#button_pause').on('click', function() {
clearInterval(play);
slider.step(stepUnix);
$('button#button_play').removeClass('pressed').prop("disabled", false);
$('#speed-slider').slider('enable');
});
var dynamicArray = _helpers.dynamicAttsToArray(atts);
for (var i = 0; i < graph.model.length; i++) {
if ( dynamicArray.includes(graph.model[i].title) )
$('<option>').val(graph.model[i].title).text(graph.model[i].title).appendTo('#properties-dynamic');
else
$('<option>').val(graph.model[i].title).text(graph.model[i].title).appendTo('#properties-static');
}
// Hide optgroups if they are empty
if ( $('#properties-static').children().length === 0 ) $('#properties-static').hide();
if ( $('#properties-dynamic').children().length === 0 ) $('#properties-dynamic').hide();
$('select#properties').change(function() {
colorProperty = $('select#properties').val();
updateData(slider.value());
});
$('button#button_zoomFit').click(function() {
var paddingPercent = 0.7;
var transitionDuration = 750;
var bounds = groot.node().getBBox();
var parent = groot.node().parentElement;
var fullWidth = parent.clientWidth,
fullHeight = parent.clientHeight;
var widthBounds = bounds.width,
heightBounds = bounds.height;
var midX = bounds.x + widthBounds / 2,
midY = bounds.y + heightBounds / 2;
if (widthBounds == 0 || heightBounds == 0) return; // nothing to fit
var scale = (paddingPercent || 0.75) / Math.max(widthBounds / fullWidth, heightBounds / fullHeight);
var translate = [fullWidth / 2 - scale * midX, fullHeight / 2 - scale * midY];
//console.trace("zoomFit", translate, scale);
groot
.transition()
.duration(transitionDuration || 0) // milliseconds
.call(zoom.translate(translate).scale(scale).event);
});
// Speed for the timeline
$('#speed-slider').slider('enable').on('change', function(value) {
speed = value.value.newValue;
});
// Link distance between the nodes
$('#link-distance-slider').slider('enable').on('change', function(value) {
force.stop().linkDistance(value.value.newValue).start();
// Update radius of the nodes to see them better
var r = d3.scale.linear().domain([30, 1000]).range([8, 24]);
radius = r(value.value.newValue);
node.attr('r', radius);
});
updateData(slider.value());
// Clear all events
$('#file').change(function() {
$('button#button_play').off();
$('button#button_pause').off();
$('select#properties').off();
$('button#button_zoomFit').off();
$('#speed-slider').slider('disable');
$('#link-distance-slider').slider('disable').slider('setValue', 30);
});
}
}
///]]

@ -0,0 +1,622 @@
;(function(undefined) {
'use strict';
/**
* GEXF Parser
* ============
*
* Author: PLIQUE Guillaume (Yomguithereal)
* URL: https://github.com/Yomguithereal/gexf-parser
* Version: 1.0
*/
/**
* Helper Namespace
* -----------------
*
* A useful batch of function dealing with DOM operations and types.
*/
var _helpers = {
nodeListToArray: function(nodeList) {
// Return array
var children = [];
// Iterating
for (var i = 0, len = nodeList.length; i < len; ++i) {
if (nodeList[i].nodeName !== '#text')
children.push(nodeList[i]);
}
return children;
},
nodeListEach: function(nodeList, func) {
// Iterating
for (var i = 0, len = nodeList.length; i < len; ++i) {
if (nodeList[i].nodeName !== '#text')
func(nodeList[i]);
}
},
nodeListToHash: function(nodeList, filter) {
// Return object
var children = {};
// Iterating
for (var i = 0; i < nodeList.length; i++) {
if (nodeList[i].nodeName !== '#text') {
var prop = filter(nodeList[i]);
children[prop.key] = prop.value;
}
}
return children;
},
namedNodeMapToObject: function(nodeMap) {
// Return object
var attributes = {};
// Iterating
for (var i = 0; i < nodeMap.length; i++) {
attributes[nodeMap[i].name] = nodeMap[i].value;
}
return attributes;
},
getFirstElementByTagNS: function(node, ns, tag) {
var el = node.getElementsByTagName(ns + ':' + tag)[0];
if (!el)
el = node.getElementsByTagNameNS(ns, tag)[0];
if (!el)
el = node.getElementsByTagName(tag)[0];
return el;
},
getAttributeNS: function(node, ns, attribute) {
var attr_value = node.getAttribute(ns + ':' + attribute);
if (attr_value === undefined)
attr_value = node.getAttributeNS(ns, attribute);
if (attr_value === undefined)
attr_value = node.getAttribute(attribute);
return attr_value;
},
enforceType: function(type, value) {
switch (type) {
case 'boolean':
value = (value === 'true');
break;
case 'integer':
case 'long':
case 'float':
case 'double':
value = +value;
break;
}
return value;
},
getRGB: function(values) {
return (values[3]) ?
'rgba(' + values.join(',') + ')' :
'rgb(' + values.slice(0, -1).join(',') + ')';
}
};
/**
* Parser Core Functions
* ----------------------
*
* The XML parser's functions themselves.
*/
/**
* Node structure.
* A function returning an object guarded with default value.
*
* @param {object} properties The node properties.
* @return {object} The guarded node object.
*/
function Node(properties) {
// Possible Properties
return {
id: properties.id,
index: properties.index,
label: properties.label,
attributes: properties.attributes || {},
viz: properties.viz || {},
spell: properties.spell || {}
};
}
/**
* Edge structure.
* A function returning an object guarded with default value.
*
* @param {object} properties The edge properties.
* @return {object} The guarded edge object.
*/
function Edge(properties, nodeList) {
var list = _helpers.nodeListToArray(nodeList);
function findNodeSource (node) {
return node.id === properties.source;
}
function findNodeTarget (node) {
return node.id === properties.target;
}
// Possible Properties
return {
id: properties.id,
type: properties.type || 'undirected',
label: properties.label || '',
source: list.indexOf(list.find(findNodeSource)),
target: list.indexOf(list.find(findNodeTarget)),
value: +properties.value || 1.0,
viz: properties.viz || {},
spell: properties.spell || {}
};
}
/**
* Graph parser.
* This structure parse a gexf string and return an object containing the
* parsed graph.
*
* @param {string} xml The xml string of the gexf file to parse.
* @return {object} The parsed graph.
*/
function Graph(xml) {
var _xml = {};
// Basic Properties
//------------------
_xml.els = {
root: xml.getElementsByTagName('gexf')[0],
graph: xml.getElementsByTagName('graph')[0],
meta: xml.getElementsByTagName('meta')[0],
model: xml.getElementsByTagName('attribute'),
nodes: xml.getElementsByTagName('node'),
links: xml.getElementsByTagName('edge')
};
_xml.hasViz = !!_helpers.getAttributeNS(_xml.els.root, 'xmlns', 'viz');
_xml.version = _xml.els.root.getAttribute('version') || '1.0';
_xml.mode = _xml.els.graph.getAttribute('mode') || 'static';
_xml.timeformat = _xml.els.graph.getAttribute('timeformat') || null;
var edgeType = _xml.els.graph.getAttribute('defaultedgetype');
_xml.defaultEdgetype = edgeType || 'undirected';
// Parser Functions
//------------------
// Meta Data
function _metaData() {
var metas = {};
if (!_xml.els.meta)
return metas;
// Last modified date
metas.lastmodifieddate = _xml.els.meta.getAttribute('lastmodifieddate');
// Other information
_helpers.nodeListEach(_xml.els.meta.childNodes, function(child) {
metas[child.tagName.toLowerCase()] = child.textContent;
});
return metas;
}
// Model
function _model() {
var attributes = [];
// Iterating through attributes
_helpers.nodeListEach(_xml.els.model, function(attr) {
// Properties
var properties = {
id: attr.getAttribute('id') || attr.getAttribute('for'),
type: attr.getAttribute('type') || 'string',
title: attr.getAttribute('title') || ''
};
// Defaults
var default_el = _helpers.nodeListToArray(attr.childNodes);
if (default_el.length > 0)
properties.defaultValue = default_el[0].textContent;
// Creating attribute
attributes.push(properties);
});
return attributes;
}
// Nodes
function _nodes(model) {
var nodes = [];
// Iteration through nodes
_helpers.nodeListEach(_xml.els.nodes, function(n) {
// Basic properties
var properties = {
id: n.getAttribute('id'),
label: n.getAttribute('label') || ''
};
// Retrieving data from nodes if any
if (model.length > 0)
properties.attributes = _nodeData(model, n);
// Retrieving viz information
if (_xml.hasViz)
properties.viz = _nodeViz(n);
properties.spell = _nodeSpell(n);
// Pushing node
nodes.push(Node(properties));
});
return nodes;
}
// Spells from nodes
function _nodeSpell(node) {
var spells = node.getElementsByTagName('spells')[0];
if (spells) {
var spell = spells.getElementsByTagName('spell');
var interval = [];
for ( var i = 0; i < spell.length; i++ ) {
var start = ( (_xml.timeformat !== "date") ? parseFloat(spell[i].getAttribute("start")) || -Infinity :
(spell[i].getAttribute('start') ? new Date(spell[i].getAttribute('start')) : -Infinity) );
var end = ( (_xml.timeformat !== "date") ? parseFloat(spell[i].getAttribute("end")) || Infinity :
(spell[i].getAttribute('end') ? new Date(spell[i].getAttribute('end')) : Infinity) );
interval.push([start, end]);
}
return interval;
} else {
return;
}
}
// Data from nodes
function _nodeData(model, node) {
var data = {};
var attvalues_els = node.getElementsByTagName('attvalue');
// Getting Node Indicated Attributes
var ah = _helpers.nodeListToHash(attvalues_els, function(el) {
var attributes = _helpers.namedNodeMapToObject(el.attributes);
var key = attributes.id || attributes['for'];
// Returning object
return { key: key, value: attributes.value };
});
// Iterating through model
model.map(function(a) {
// Default value?
var att_title = a.title.toLowerCase();
data[att_title] = !(a.id in ah) && 'defaultValue' in a ?
_helpers.enforceType(a.type, a.defaultValue) :
_helpers.enforceType(a.type, ah[a.id]);
});
return data;
}
// Viz information from nodes
function _nodeViz(node) {
var viz = {};
// Color
var color_el = _helpers.getFirstElementByTagNS(node, 'viz', 'color');
if (color_el) {
var color = ['r', 'g', 'b', 'a'].map(function(c) {
return color_el.getAttribute(c);
});
viz.color = _helpers.getRGB(color);
}
// Position
var pos_el = _helpers.getFirstElementByTagNS(node, 'viz', 'position');
if (pos_el) {
viz.position = {};
['x', 'y', 'z'].map(function(p) {
viz.position[p] = +pos_el.getAttribute(p);
});
}
// Size
var size_el = _helpers.getFirstElementByTagNS(node, 'viz', 'size');
if (size_el)
viz.size = +size_el.getAttribute('value');
// Shape
var shape_el = _helpers.getFirstElementByTagNS(node, 'viz', 'shape');
if (shape_el)
viz.shape = shape_el.getAttribute('value');
return viz;
}
// Edges
function _edges(default_type) {
var edges = [];
// Iteration through edges
_helpers.nodeListEach(_xml.els.links, function(e) {
// Creating the edge
var properties = _helpers.namedNodeMapToObject(e.attributes);
if (!('type' in properties)) {
properties.type = default_type;
}
// Retrieving viz information
if (_xml.hasViz)
properties.viz = _edgeViz(e);
properties.spell = _edgeSpell(e);
edges.push(Edge(properties, _xml.els.nodes));
});
return edges;
}
// Spells from edges
function _edgeSpell(edge) {
var start = ( (_xml.timeformat !== "date") ? parseFloat(edge.getAttribute("start")) || -Infinity :
(edge.getAttribute('start') ? new Date(edge.getAttribute('start')) : -Infinity) );
var end = ( (_xml.timeformat !== "date") ? parseFloat(edge.getAttribute("end")) || Infinity :
(edge.getAttribute('end') ? new Date(edge.getAttribute('end')) : Infinity) );
return [start, end];
}
// Viz information from edges
function _edgeViz(edge) {
var viz = {};
// Color
var color_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'color');
if (color_el) {
var color = ['r', 'g', 'b', 'a'].map(function(c) {
return color_el.getAttribute(c);
});
viz.color = _helpers.getRGB(color);
}
// Shape
var shape_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'shape');
if (shape_el)
viz.shape = shape_el.getAttribute('value');
// Thickness
var thick_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'thickness');
if (thick_el)
viz.thickness = +thick_el.getAttribute('value');
return viz;
}
// Returning the Graph
//---------------------
_xml.model = _model();
return {
version: _xml.version,
mode: _xml.mode,
timeformat: _xml.timeformat,
defaultEdgeType: _xml.defaultEdgetype,
meta: _metaData(),
model: _xml.model,
nodes: _nodes(_xml.model),
links: _edges(_xml.defaultEdgetype)
};
}
/**
* Dynamics attributes parser.
* This structure parse a gexf string and return an object containing the
* dynamics attributes.
*
* @param {string} xml The xml string of the gexf file to parse.
* @return {object} The dynamics attributes and its values.
*/
function dynamicAttributes(xml, timeformat) {
var _xml = {};
_xml.els = {
nodes: xml.getElementsByTagName('node'),
attributes: xml.getElementsByTagName('attributes')
}
for ( var i = 0; i < _xml.els.attributes.length; i++ ) {
if (_xml.els.attributes[i].getAttribute("mode") === "dynamic")
_xml.els.attributes = _xml.els.attributes[i];
}
var x = {};
for ( var i = 0; i < _xml.els.attributes.children.length; i++ ) {
var att = x[_xml.els.attributes.children[i].getAttribute("title")] = [];
var node = 0;
_helpers.nodeListEach(_xml.els.nodes, function(n) {
att[node] = { interval: [], index: node }
for ( var j = 0; j < n.getElementsByTagName('attvalue').length; j++ ) {
if (n.getElementsByTagName('attvalue')[j].getAttribute("for") === _xml.els.attributes.children[i].getAttribute("id")) {
var element = n.getElementsByTagName('attvalue')[j];
var start = ( (timeformat !== "date") ? parseFloat(element.getAttribute("start")) || -Infinity :
(element.getAttribute('start') ? new Date(element.getAttribute('start')) : -Infinity) );
var end = ( (timeformat !== "date") ? parseFloat(element.getAttribute("end")) || Infinity :
(element.getAttribute('end') ? new Date(element.getAttribute('end')) : Infinity) );
var value = element.getAttribute("value");
att[node].interval.push([start, end, value]);
}
}
node++;
});
}
return x;
}
/**
* Public API
* -----------
*
* User-accessible functions.
*/
// Fetching GEXF with XHR
function fetch(gexf_url, callback) {
var xhr = (function() {
if (window.XMLHttpRequest)
return new XMLHttpRequest();
var names,
i;
if (window.ActiveXObject) {
names = [
'Msxml2.XMLHTTP.6.0',
'Msxml2.XMLHTTP.3.0',
'Msxml2.XMLHTTP',
'Microsoft.XMLHTTP'
];
for (i in names)
try {
return new ActiveXObject(names[i]);
} catch (e) {}
}
return null;
})();
if (!xhr)
throw 'XMLHttpRequest not supported, cannot load the file.';
// Async?
var async = (typeof callback === 'function'),
getResult;
// If we can't override MIME type, we are on IE 9
// We'll be parsing the response string then.
if (xhr.overrideMimeType) {
xhr.overrideMimeType('text/xml');
getResult = function(r) {
return r.responseXML;
};
}
else {
getResult = function(r) {
var p = new DOMParser();
return p.parseFromString(r.responseText, 'application/xml');
};
}
xhr.open('GET', gexf_url, async);
if (async)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4)
callback(getResult(xhr));
};
xhr.send();
return (async) ? xhr : getResult(xhr);
}
// Parsing the GEXF File
function parse(gexf) {
return Graph(gexf);
}
// Fetch and parse the GEXF File
function fetchAndParse(gexf_url, callback) {
if (typeof callback === 'function') {
return fetch(gexf_url, function(gexf) {
callback(Graph(gexf));
});
} else
return Graph(fetch(gexf_url));
}
function parseDynamicAttributes(gexf, timeformat) {
return dynamicAttributes(fetch(gexf), timeformat);
}
/**
* Exporting
* ----------
*/
this.GexfParser = {
// Functions
parse: parse,
fetch: fetchAndParse,
dynamic: parseDynamicAttributes,
// Version
version: '0.1'
};
}).call(this);

@ -0,0 +1,67 @@
import os
import networkx as nx
from server import VisualizationElement
from soil.simulation import SoilSimulation
from xml.etree import ElementTree
class Model():
def __init__(self, dump=True, dir_path='output'):
self.dump = dump
self.dir_path = dir_path
def run(self, config):
name = config['name']
print('Using config(s): {name}'.format(name=name))
sim = SoilSimulation(**config)
sim.dir_path = os.path.join(self.dir_path, name)
sim.dump = self.dump
print('Dumping results to {} : {}'.format(sim.dir_path, sim.dump))
sim.run_simulation()
def get_trial(self, name, trial):
graph = nx.read_gexf(os.path.join(self.dir_path, name, '{}_trial_{}.gexf'.format(name, trial)))
attributes = self.get_attributes(os.path.join(self.dir_path, name, '{}_trial_{}.gexf'.format(name, trial)))
json = {}
json['graph'] = nx.node_link_data(graph)
json['models'] = attributes
return json
def reset(self):
pass
def get_attributes(self, path_gexf):
attributes = {}
tree = ElementTree.parse(path_gexf)
root = tree.getroot()
ns = { 'gexf': 'http://www.gexf.net/1.2draft' }
for mode in root[0].findall('gexf:attributes', ns):
attributes[mode.attrib['mode']] = []
for attribute in mode:
values = {
'id' : attribute.attrib['id'],
'title' : attribute.attrib['title'],
'type' : attribute.attrib['type']
}
attributes[mode.attrib['mode']].append(values)
return attributes
class GraphVisualization(VisualizationElement):
package_includes = []
# TODO: esta por definir todos los ajustes de simulacion
def __init__(self, params=None):
new_element = ("new funcion()")
self.js_code = "elements.push(" + new_element + ");"
def render(self, model):
pass
Loading…
Cancel
Save