1
0
mirror of https://github.com/gsi-upm/soil synced 2024-11-22 19:22:29 +00:00

Colors and shapes

This commit is contained in:
Tasio Mendez 2018-04-10 18:26:24 +02:00
parent 9caec2dad3
commit da1ce1ea30
14 changed files with 485 additions and 30 deletions

245
TerroristNetworkModel.py Normal file
View File

@ -0,0 +1,245 @@
import random
import networkx as nx
from soil.agents import BaseAgent
from scipy.spatial import cKDTree as KDTree
global betweenness_centrality_global
global degree_centrality_global
betweenness_centrality_global = None
degree_centrality_global = None
class TerroristSpreadModel(BaseAgent):
"""
Settings:
information_spread_intensity
terrorist_additional_influence
min_vulnerability (optional else zero)
max_vulnerability
prob_interaction
"""
def __init__(self, environment=None, agent_id=0, state=()):
super().__init__(environment=environment, agent_id=agent_id, state=state)
global betweenness_centrality_global
global degree_centrality_global
if betweenness_centrality_global == None:
betweenness_centrality_global = nx.betweenness_centrality(self.global_topology)
if degree_centrality_global == None:
degree_centrality_global = nx.degree_centrality(self.global_topology)
self.information_spread_intensity = environment.environment_params['information_spread_intensity']
self.terrorist_additional_influence = environment.environment_params['terrorist_additional_influence']
self.prob_interaction = environment.environment_params['prob_interaction']
if self.state['id'] == 0: # Civilian
self.initial_belief = random.uniform(0.00, 0.5)
elif self.state['id'] == 1: # Terrorist
self.initial_belief = random.uniform(0.8, 1.00)
elif self.state['id'] == 2: # Leader
self.initial_belief = 1.00
if 'min_vulnerability' in environment.environment_params:
self.vulnerability = random.uniform( environment.environment_params['min_vulnerability'], environment.environment_params['max_vulnerability'] )
else :
self.vulnerability = random.uniform( 0, environment.environment_params['max_vulnerability'] )
self.mean_belief = self.initial_belief
self.betweenness_centrality = betweenness_centrality_global[self.id]
self.degree_centrality = degree_centrality_global[self.id]
# self.state['radicalism'] = self.mean_belief
def count_neighboring_agents(self, state_id=None):
if isinstance(state_id, list):
return len(self.get_neighboring_agents(state_id))
else:
return len(super().get_agents(state_id, limit_neighbors=True))
def get_neighboring_agents(self, state_id=None):
if isinstance(state_id, list):
_list = []
for i in state_id:
_list += super().get_agents(i, limit_neighbors=True)
return [ neighbour for neighbour in _list if isinstance(neighbour, TerroristSpreadModel) ]
else:
_list = super().get_agents(state_id, limit_neighbors=True)
return [ neighbour for neighbour in _list if isinstance(neighbour, TerroristSpreadModel) ]
def step(self):
if self.state['id'] == 0: # Civilian
self.civilian_behaviour()
elif self.state['id'] == 1: # Terrorist
self.terrorist_behaviour()
elif self.state['id'] == 2: # Leader
self.leader_behaviour()
def civilian_behaviour(self):
if self.count_neighboring_agents() > 0:
neighbours = []
for neighbour in self.get_neighboring_agents():
if random.random() < self.prob_interaction:
neighbours.append(neighbour)
influence = sum( neighbour.degree_centrality for neighbour in neighbours )
mean_belief = sum( neighbour.mean_belief * neighbour.degree_centrality / influence for neighbour in neighbours )
self.initial_belief = self.mean_belief
mean_belief = mean_belief * self.information_spread_intensity + self.initial_belief * ( 1 - self.information_spread_intensity )
self.mean_belief = mean_belief * self.vulnerability + self.initial_belief * ( 1 - self.vulnerability )
if self.mean_belief >= 0.8:
self.state['id'] = 1
# self.state['radicalism'] = self.mean_belief
def leader_behaviour(self):
self.mean_belief = self.mean_belief ** ( 1 - self.terrorist_additional_influence )
if self.count_neighboring_agents(state_id=[1,2]) > 0:
for neighbour in self.get_neighboring_agents(state_id=[1,2]):
if neighbour.betweenness_centrality > self.betweenness_centrality:
self.state['id'] = 1
# self.state['radicalism'] = self.mean_belief
def terrorist_behaviour(self):
if self.count_neighboring_agents(state_id=[1,2]) > 0:
neighbours = self.get_neighboring_agents(state_id=[1,2])
influence = sum( neighbour.degree_centrality for neighbour in neighbours )
mean_belief = sum( neighbour.mean_belief * neighbour.degree_centrality / influence for neighbour in neighbours )
self.initial_belief = self.mean_belief
self.mean_belief = mean_belief * self.vulnerability + self.initial_belief * ( 1 - self.vulnerability )
self.mean_belief = self.mean_belief ** ( 1 - self.terrorist_additional_influence )
if self.count_neighboring_agents(state_id=2) == 0 and self.count_neighboring_agents(state_id=1) > 0:
max_betweenness_centrality = self
for neighbour in self.get_neighboring_agents(state_id=1):
if neighbour.betweenness_centrality > max_betweenness_centrality.betweenness_centrality:
max_betweenness_centrality = neighbour
if max_betweenness_centrality == self:
self.state['id'] = 2
# self.state['radicalism'] = self.mean_belief
def add_edge(self, G, source, target):
G.add_edge(source.id, target.id, start=self.env._now)
def link_search(self, G, node, radius):
pos = nx.get_node_attributes(G, 'pos')
nodes, coords = list(zip(*pos.items()))
kdtree = KDTree(coords) # Cannot provide generator.
edge_indexes = kdtree.query_pairs(radius, 2)
_list = [ edge[int(not edge.index(node))] for edge in edge_indexes if node in edge ]
return [ G.nodes()[index]['agent'] for index in _list ]
def social_search(self, G, node, steps):
nodes = list(nx.ego_graph(G, node, radius=steps).nodes())
nodes.remove(node)
return [ G.nodes()[index]['agent'] for index in nodes ]
class TrainingAreaModel(BaseAgent):
"""
Settings:
training_influence
min_vulnerability
Requires TerroristSpreadModel.
"""
def __init__(self, environment=None, agent_id=0, state=()):
super().__init__(environment=environment, agent_id=agent_id, state=state)
self.training_influence = environment.environment_params['training_influence']
if 'min_vulnerability' in environment.environment_params:
self.min_vulnerability = environment.environment_params['min_vulnerability']
else: self.min_vulnerability = 0
def step(self):
for neighbour in self.get_neighboring_agents():
if isinstance(neighbour, TerroristSpreadModel) and neighbour.vulnerability > self.min_vulnerability:
neighbour.vulnerability = neighbour.vulnerability * ( 1 - self.training_influence )
class HavenModel(BaseAgent):
"""
Settings:
haven_influence
max_vulnerability
Requires TerroristSpreadModel.
"""
def __init__(self, environment=None, agent_id=0, state=()):
super().__init__(environment=environment, agent_id=agent_id, state=state)
self.haven_influence = environment.environment_params['haven_influence']
self.max_vulnerability = environment.environment_params['max_vulnerability']
def step(self):
if self.count_neighboring_agents(state_id=0) == 0:
self.state['id'] = 1 # Terrorism Haven
else:
self.state['id'] = 0 # Civilian Haven
for neighbour in self.get_neighboring_agents():
if isinstance(neighbour, TerroristSpreadModel) and neighbour.vulnerability < self.max_vulnerability:
neighbour.vulnerability = neighbour.vulnerability ** ( 1 - self.haven_influence )
class TerroristNetworkModel(TerroristSpreadModel):
"""
Settings:
sphere_influence
vision_range
weight_social_distance
weight_link_distance
"""
def __init__(self, environment=None, agent_id=0, state=()):
super().__init__(environment=environment, agent_id=agent_id, state=state)
self.vision_range = environment.environment_params['vision_range']
self.sphere_influence = environment.environment_params['sphere_influence']
self.weight_social_distance = environment.environment_params['weight_social_distance']
self.weight_link_distance = environment.environment_params['weight_link_distance']
def step(self):
if self.state['id'] == 1 or self.state['id'] == 2:
self.update_relationships()
super().step()
def update_relationships(self):
if self.count_neighboring_agents(state_id=0) == 0:
close_ups = self.link_search(self.global_topology, self.id, self.vision_range)
step_neighbours = self.social_search(self.global_topology, self.id, self.sphere_influence)
search = list(set(close_ups).union(step_neighbours))
neighbours = self.get_neighboring_agents()
search = [item for item in search if not item in neighbours and isinstance(item, TerroristNetworkModel)]
for agent in search:
social_distance = 1 / self.shortest_path_length(self.global_topology, self.id, agent.id)
spatial_proximity = ( 1 - self.get_distance(self.global_topology, self.id, agent.id) )
prob_new_interaction = self.weight_social_distance * social_distance + self.weight_link_distance * spatial_proximity
if random.random() < prob_new_interaction:
self.add_edge(self.global_topology, self, agent)
break
def get_distance(self, G, source, target):
source_x, source_y = nx.get_node_attributes(G, 'pos')[source]
target_x, target_y = nx.get_node_attributes(G, 'pos')[target]
dx = abs( source_x - target_x )
dy = abs( source_y - target_y )
return ( dx ** 2 + dy ** 2 ) ** ( 1 / 2 )
def shortest_path_length(self, G, source, target):
try:
return nx.shortest_path_length(G, source, target)
except nx.NetworkXNoPath:
return float('inf')

58
TerroristNetworkModel.yml Normal file
View File

@ -0,0 +1,58 @@
name: TerroristNetworkModel_sim
load_module: TerroristNetworkModel
max_time: 100
num_trials: 1
network_params:
generator: random_geometric_graph
radius: 0.2
# generator: geographical_threshold_graph
# theta: 20
n: 50
network_agents:
- agent_type: TerroristNetworkModel
weight: 0.8
state:
id: 0 # Civilians
- agent_type: TerroristNetworkModel
weight: 0.1
state:
id: 2 # Leaders
- agent_type: TrainingAreaModel
weight: 0.05
state:
id: 2 # Terrorism
- agent_type: HavenModel
weight: 0.05
state:
id: 0 # Civilian
environment_params:
# TerroristSpreadModel
information_spread_intensity: 0.7
terrorist_additional_influence: 0.035
max_vulnerability: 0.5
prob_interaction: 0.5
# TrainingAreaModel and HavenModel
training_influence: 0.20
haven_influence: 0.20
# TerroristNetworkModel
vision_range: 0.50
sphere_influence: 2
weight_social_distance: 0.035
weight_link_distance: 0.035
visualization_params:
# Icons downloaded from https://www.iconfinder.com/
shape_property: agent
shapes:
TrainingAreaModel: target
HavenModel: home
colors:
- attr_id: 0
color: green
- attr_id: 1
color: red
- attr_id: 2
color: '#c16a6a'

View File

@ -86,6 +86,9 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
self.config = self.config[0]
self.send_log('INFO.soil', 'Using config: {name}'.format(name=self.config['name']))
if 'visualization_params' in self.config:
self.write_message({'type': 'visualization_params',
'data': self.config['visualization_params']})
self.name = self.config['name']
self.run_simulation()
@ -93,6 +96,8 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
for key in self.config['environment_params']:
if type(self.config['environment_params'][key]) == float:
setting_type = 'number'
elif type(self.config['environment_params'][key]) == int:
setting_type = 'number'
elif type(self.config['environment_params'][key]) == bool:
setting_type = 'boolean'
else:
@ -149,8 +154,15 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
def run_simulation(self):
# Run simulation and capture logs
if 'visualization_params' in self.config:
del self.config['visualization_params']
with self.logging(self.application.model.name):
self.simulation = self.application.model.run(self.config)
try:
self.simulation = self.application.model.run(self.config)
except:
error = 'Something went wrong. Please, try again.'
self.write_message({'type': 'error',
'error': error})
trials = []
for i in range(self.config['num_trials']):
@ -232,5 +244,5 @@ class ModularServer(tornado.web.Application):
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)
# webbrowser.open(url)
tornado.ioloop.IOLoop.instance().start()

View File

@ -183,6 +183,7 @@ hr {
z-index: 5;
height: 35px;
padding: .5rem 1rem;
overflow: hidden;
line-height: 1.5;
color: #464a4c;
pointer-events: none;

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><svg height="1792" viewBox="0 0 1792 1792" width="1792" xmlns="http://www.w3.org/2000/svg"><path d="M1472 992v480q0 26-19 45t-45 19h-384v-384h-256v384h-384q-26 0-45-19t-19-45v-480q0-1 .5-3t.5-3l575-474 575 474q1 2 1 6zm223-69l-62 74q-8 9-21 11h-3q-13 0-21-7l-692-577-692 577q-12 8-24 7-13-2-21-11l-62-74q-8-10-7-23.5t11-21.5l719-599q32-26 76-26t76 26l244 204v-195q0-14 9-23t23-9h192q14 0 23 9t9 23v408l219 182q10 8 11 21.5t-7 23.5z"/></svg>

After

Width:  |  Height:  |  Size: 462 B

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="512px" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M448,448c0,0,0-26.4-2.2-40.2c-1.8-10.9-16.9-25.3-81.1-48.9c-63.2-23.2-59.3-11.9-59.3-54.6c0-27.7,14.1-11.6,23.1-64.2 c3.5-20.7,6.3-6.9,13.9-40.1c4-17.4-2.7-18.7-1.9-27c0.8-8.3,1.6-15.7,3.1-32.7C345.4,119.3,325.9,64,256,64 c-69.9,0-89.4,55.3-87.5,76.4c1.5,16.9,2.3,24.4,3.1,32.7c0.8,8.3-5.9,9.6-1.9,27c7.6,33.1,10.4,19.3,13.9,40.1 c9,52.6,23.1,36.5,23.1,64.2c0,42.8,3.9,31.5-59.3,54.6c-64.2,23.5-79.4,38-81.1,48.9C64,421.6,64,448,64,448h192H448z"/></svg>

After

Width:  |  Height:  |  Size: 812 B

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'><svg enable-background="new 0 0 91.8 92.6" id="Layer_1" version="1.0" viewBox="0 0 91.8 92.6" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M46.3,3.6c-23.5,0-42.5,19-42.5,42.5s19,42.5,42.5,42.5c23.5,0,42.5-19,42.5-42.5S69.8,3.6,46.3,3.6z M72.8,52.9H53v19.8c0,2-1.6,3.6-3.6,3.6h-6.2c-2,0-3.6-1.6-3.6-3.6V52.9H19.8c-2,0-3.6-1.6-3.6-3.6v-6.2c0-2,1.6-3.6,3.6-3.6h19.8 V19.7c0-2,1.6-3.6,3.6-3.6h6.2c2,0,3.6,1.6,3.6,3.6v19.8h19.8c2,0,3.6,1.6,3.6,3.6v6.2C76.4,51.2,74.8,52.9,72.8,52.9z" fill="#1E1E1E"/></svg>

After

Width:  |  Height:  |  Size: 697 B

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><svg height="16px" version="1.1" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><defs/><g fill="none" fill-rule="evenodd" id="Icons with numbers" stroke="none" stroke-width="1"><g fill="#000000" id="Group" transform="translate(-192.000000, -192.000000)"><path d="M201,205.917042 C203.512502,205.49553 205.495527,203.512505 205.917042,201 L203,201 L203,199 L205.917042,199 C205.495527,196.487495 203.512502,194.50447 201,194.082958 L201,197 L199,197 L199,194.082958 C196.487498,194.50447 194.504473,196.487495 194.082958,199 L197,199 L197,201 L194.082958,201 C194.504473,203.512505 196.487498,205.49553 199,205.917042 L199,203 L201,203 Z M200,208 C195.581722,208 192,204.418278 192,200 C192,195.581722 195.581722,192 200,192 C204.418278,192 208,195.581722 208,200 C208,204.418278 204.418278,208 200,208 Z M200,208" id="Oval 163"/></g></g></svg>

After

Width:  |  Height:  |  Size: 992 B

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 64 64" height="64px" id="Layer_1" version="1.1" viewBox="0 0 64 64" width="64px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><g><path d="M52.419,15.975c0,0,1.013,1.019,1.727,0.002l1.363-1.953c0.476-0.687-0.139-1.162-0.202-1.209 l-8.265-5.775H47.04c-0.509-0.354-0.847-0.139-1.024,0.06l-0.148,0.213l-1.259,1.802c-0.006,0.007-0.71,1.119,0.416,1.707v0.001 c1.61,0.792,4.563,2.462,7.392,5.158L52.419,15.975z" fill="#241F20"/></g><g><path d="M38.512,0.071H25.488c-1.011,0-1.839,0.812-1.839,1.839v1.518c0,1.026,0.828,1.854,1.839,1.854h0.644 v1.072c0.001,1.541,0.974,1.669,1.462,1.636c0.083-0.012,0.169-0.025,0.26-0.037c0.001,0,0.013-0.003,0.013-0.003L27.866,7.95 c1.734-0.237,4.605-0.464,7.898-0.045l0.002-0.003c0,0,2.109,0.391,2.103-1.549V5.281h0.644c1.012,0,1.839-0.827,1.839-1.854V1.91 C40.351,0.884,39.523,0.071,38.512,0.071z" fill="#241F20"/></g><path d="M32,10.301c-14.808,0-26.812,12.005-26.812,26.815c0,14.807,12.004,26.812,26.812,26.812 c14.809,0,26.812-12.006,26.812-26.812C58.812,22.306,46.809,10.301,32,10.301z M33.717,37.108 c-1.575,0.002-1.709-1.094-1.717-1.41V17.155c0.046-0.645,0.381-1.86,2.248-1.546c0.037,0.005,0.072,0.009,0.111,0.014 c0.12,0.02,0.233,0.036,0.32,0.043c5.44,0.764,17.373,4.302,18.864,20.343c-0.042,0.446-0.295,1.096-1.412,1.103 C42.529,37.085,36.454,37.097,33.717,37.108z" fill="#241F20"/></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -104,7 +104,7 @@
$('#simulation_modal .btn-success').click(function() {
if ( !jQuery.isEmptyObject(run_simulation()) ) {
self.GraphVisualization.reset();
$('#load').show().addClass('loader');;
$('#load').show().addClass('loader');
_socket.send(run_simulation(), 'run_simulation');
$('.console').append('<br/>');
}

View File

@ -15,15 +15,15 @@ ws.onmessage = function(message) {
switch(msg['type']) {
case 'trials':
$('#load').removeClass('loader');
reset_trials();
set_trials(msg['data']);
// $('#load').removeClass('loader');
break;
case 'get_trial':
console.log(msg['data']);
GraphVisualization.import(convertJSON(msg['data']), function() {
$('#load').hide();
self.GraphVisualization.import(convertJSON(msg['data']), function() {
reset_configuration();
set_configuration();
$('#home_menu').click(function() {
@ -34,6 +34,7 @@ ws.onmessage = function(message) {
});
reset_timeline();
set_timeline(msg['data']);
$('#load').hide();
});
$('#charts .chart').removeClass('no-data');
set_chart_nodes(msg['data'], chart_nodes)
@ -61,6 +62,11 @@ ws.onmessage = function(message) {
$('.console').animate({ scrollTop: $('.console')[0].scrollHeight }, 'fast');
break;
case 'visualization_params':
console.log(msg['data']);
self.GraphVisualization.set_params(msg['data']['shape_property'], msg['data']['shapes'], msg['data']['colors']);
break;
default:
console.log('Unexpected message!')
}
@ -103,6 +109,16 @@ var reset_trials = function() {
}
var convertJSON = function(json) {
// For NetworkX Geometric Graphs get positions
json.nodes.forEach(function(node) {
var scx = d3.scale.linear().domain([0, 1]).range([0, height]);
var scy = d3.scale.linear().domain([0, 1]).range([height, 0]);
if ( node.pos ) {
node.scx = scx(node.pos[0]);
node.scy = scy(node.pos[1]);
}
delete node.pos;
});
json.links.forEach(function(link) {
link.source = json.nodes[link.source]
link.target = json.nodes[link.target]
@ -136,7 +152,7 @@ var update_statistics_table = function() {
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>').css('background-color', self.GraphVisualization.color($('.config-item #properties').val(), 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);
}
@ -211,6 +227,9 @@ var set_timeline = function(graph) {
// Draw graph for the first time
self.GraphVisualization.update_graph($('.config-item #properties').val(), maxUnix, function() {
update_statistics_table();
setTimeout(function() {
self.GraphVisualization.fit();
}, 100);
});
// 'Speed' slider

View File

@ -82,7 +82,7 @@ var initGUI = function(model_params) {
addSliderInput(model_params[option]['label'], model_params[option]['value']);
break;
default:
console.log(model_params[option]['type'] + ' not defined!');
console.log(model_params[option]['label'] + ' not defined!');
break;
}
}

View File

@ -14,7 +14,7 @@
// Private constants
var focus_opacity = 0.1,
radius = 8,
required_node = ['id', 'index', 'label', 'px', 'py', 'spells', 'weight', 'x', 'y'];
required_node = ['id', 'index', 'label', 'px', 'py', 'spells', 'weight', 'x', 'y', 'pos', 'scx', 'scy'];
// Private variables
var width,
@ -35,7 +35,10 @@
data_link, // Actual link data for the graph
link, // Line svgs
node; // Circles for the nodes
node, // Circles for the nodes
shape_property, // Property to whom the shape will be applied
shapes, // Dictionary of shapes for nodes
colors; // Dictionary of colors for nodes
Number.prototype.between = function(min, max) {
var min = (min || min === 0) ? min : Math.max(),
@ -60,15 +63,29 @@
.attr('r', radius)
.style('fill', function (d) {
if ( Array.isArray(d[property]) ) {
var color_node = color(d[property][0][0]);
var color_node = _helpers.set_color(property, d[property][0][0]);
d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = color(p[0]);
if ( time.between(p[1], p[2]) ) color_node = _helpers.set_color(property, p[0]);
});
return color_node;
} else {
return color(d[property]);
return _helpers.set_color(property, d[property]);
}
})
.style('stroke', function(d) {
if (_helpers.set_shape(d[shape_property]) !== (-1))
if ( Array.isArray(d[property]) ) {
var color_node = _helpers.set_color(property, d[property][0][0]);
d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = _helpers.set_color(property, p[0]);
});
return color_node;
} else {
return _helpers.set_color(property, d[property]);
}
else
return '#ffffff';
})
// Cancel zoom movement so you can move the node
.on('mousedown', function(d) {
d3.event.stopPropagation();
@ -93,16 +110,33 @@
node.attr('class', 'node')
.attr('r', radius)
.style('fill', function (d) {
if (_helpers.set_shape(d[shape_property]) !== (-1)) {
return 'url(#' + _helpers.set_shape(d[shape_property]) + ')';
}
if ( Array.isArray(d[property]) ) {
var color_node = color(d[property][0][0]);
var color_node = _helpers.set_color(property, d[property][0][0]);
d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = color(p[0]);
if ( time.between(p[1], p[2]) ) color_node = _helpers.set_color(property, p[0]);
});
return color_node;
} else {
return color(d[property]);
return _helpers.set_color(property, d[property]);
}
})
.style('stroke', function(d) {
if (_helpers.set_shape(d[shape_property]) !== (-1))
if ( Array.isArray(d[property]) ) {
var color_node = _helpers.set_color(property, d[property][0][0]);
d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = _helpers.set_color(property, p[0]);
});
return color_node;
} else {
return _helpers.set_color(property, d[property]);
}
else
return '#ffffff';
})
.on('dblclick', function(d) {
d3.event.stopPropagation();
if (d === lastFocusNode) {
@ -139,12 +173,33 @@
});
},
push_once: function(array, item, key) {
for (var i in array) {
if ( array[i][key] == item[key] ) return false;
for (var i in array) {
if ( array[i][key] == item[key] ) return false;
}
array.push(item);
return true;
},
set_color: function(property, value) {
if ( colors instanceof Array ) {
for ( var c in colors ) {
if ( colors[c][property] == value ) { return colors[c]['color']; }
}
return color(value);
} else {
return color(value);
}
},
set_shape: function(value) {
if ( shapes instanceof Object && shape_property ) {
for ( var s in shapes ) {
var str_value = (value.includes('class')) ? value.split('.').pop().split('\'')[0] : value;
if ( str_value == s ) return shapes[s];
}
return (-1);
} else {
return (-1);
}
}
array.push(item);
return true;
}
}
@ -170,6 +225,29 @@
glinks = groot.append('g') .attr('id', 'links');
gnodes = groot.append('g') .attr('id', 'nodes');
// Add patterns for shapes
var defs = [];
for ( var i in shapes )
if (!defs.includes(shapes[i])) defs.push(shapes[i])
svg.append('defs')
.selectAll('pattern')
.data(defs)
.enter()
.append('pattern')
.attr('id', function(d, i) {
return d;
})
.attr('patternUnits', 'objectBoundingBox')
.attr('width', 1)
.attr('height', 1)
.append('image')
.attr('href', function(d) {
return window.location.protocol + '//' + window.location.host + '/img/svg/' + d + '.svg';
})
.attr('width', 16)
.attr('height', 16);
// Zoom
zoom = d3.behavior
.zoom()
@ -200,17 +278,22 @@
force.on('tick', function () {
link.attr('x1', function (d) {
return d.source.x;
if ( d.source.scx ) return d.source.scx;
else return d.source.x;
}).attr('y1', function (d) {
return d.source.y;
if ( d.source.scy ) return d.source.scy;
else return d.source.y;
}).attr('x2', function (d) {
return d.target.x;
if ( d.target.scx ) return d.target.scx;
else return d.target.x;
}).attr('y2', function (d) {
return d.target.y;
if ( d.target.scy ) return d.target.scy;
else return d.target.y;
});
node.attr('transform', function translate(d) {
return 'translate(' + d.x + ',' + d.y + ')';
if ( d.scx || d.scy ) return 'translate(' + d.scx + ',' + d.scy + ')';
else return 'translate(' + d.x + ',' + d.y + ')';
});
});
@ -384,6 +467,25 @@
}
}
/**
* Set shapes and color of graph.
* A function that set the shapes and colors of the nodes depending on their status.
*
* @param {object} set_shapes Shapes for nodes.
* @param {object} set_colors Colors for nodes.
* @param {object} callback A function called at the end.
*/
function set_params(set_shape_property, set_shapes, set_colors, callback) {
shape_property = set_shape_property;
shapes = set_shapes;
colors = set_colors;
self.GraphVisualization.shapes = shapes;
self.GraphVisualization.colors = colors;
if (callback) { callback(); }
}
/**
* Adjust the graph to the whole area.
* A function that adjust the graph to the svg item.
@ -437,9 +539,9 @@
* @param {object} value Value.
* @return {object} color The color in hexadecimal.
*/
function color(value) {
function color(property, value) {
if (graph) {
return color(value);
return _helpers.set_color(property, value);
}
}
@ -519,6 +621,7 @@
create: create,
import: importJSON,
update_graph: update_graph,
set_params: set_params,
set_link_distance: set_link_distance,
fit: zoom_to_fit,
reset: reset,
@ -530,6 +633,8 @@
// Getters
color: color,
shapes: shapes,
colors: colors,
get_attributes: get_attributes,
get_nodes: get_nodes,

View File

@ -1,4 +1,5 @@
import os
import networkx as nx
from server import VisualizationElement
from soil.simulation import SoilSimulation
from xml.etree import ElementTree
@ -22,7 +23,16 @@ class Model():
print('Dumping results to {} : {}'.format(sim.dir_path, sim.dump))
return sim.run_simulation()
simulation_results = sim.run_simulation()
G = simulation_results[0].history_to_graph()
for node in G.nodes():
if 'pos' in G.node[node]:
G.node[node]['viz'] = {"position": {"x": G.node[node]['pos'][0], "y": G.node[node]['pos'][1], "z": 0.0}}
del (G.node[node]['pos'])
nx.write_gexf(G, 'test.gexf', version='1.2draft')
return simulation_results
def reset(self):
pass