mirror of
https://github.com/gsi-upm/soil
synced 2024-11-14 15:32:29 +00:00
Python Server
This commit is contained in:
commit
6f8cc9ba50
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
__pycache__/
|
||||||
|
output/
|
25
config.yml
Normal file
25
config.yml
Normal file
@ -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
|
15
run.py
Normal file
15
run.py
Normal file
@ -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)
|
159
server.py
Normal file
159
server.py
Normal file
@ -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()
|
238
templates/css/main.css
Normal file
238
templates/css/main.css
Normal file
@ -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;
|
||||||
|
}
|
72
templates/css/timeline.css
Normal file
72
templates/css/timeline.css
Normal file
@ -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;
|
||||||
|
}
|
140
templates/img/logo_gsi.svg
Normal file
140
templates/img/logo_gsi.svg
Normal file
@ -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 |
226
templates/index.html
Normal file
226
templates/index.html
Normal file
@ -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">×</a>
|
||||||
|
<strong>Error! </strong><span id="error-message"></span>
|
||||||
|
</div>
|
||||||
|
<!-- //ERROR ALERT -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
269
templates/js/socket.js
Executable file
269
templates/js/socket.js
Executable file
@ -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];
|
||||||
|
}
|
429
templates/js/timeline.js
Normal file
429
templates/js/timeline.js
Normal file
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}));
|
426
templates/js/visualization.js
Normal file
426
templates/js/visualization.js
Normal file
@ -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);
|
20
templates/js_old/helper.js
Normal file
20
templates/js_old/helper.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
};
|
561
templates/js_old/initial.js
Normal file
561
templates/js_old/initial.js
Normal file
@ -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);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///]]
|
622
templates/js_old/parser.js
Normal file
622
templates/js_old/parser.js
Normal file
@ -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);
|
67
visualization.py
Normal file
67
visualization.py
Normal file
@ -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…
Reference in New Issue
Block a user