Skip to main content
Version: 1.8.1

3. Movement of People

This third step presents how to create a road system from GIS data. More precisely, it shows how to build a graph from a list of polylines and to constrain the movement of an agent according to this graph.

Formulation

  • For each people agent, definition of 2 new attributes: living_place (building of type 'Residential') and working_place (building of type 'Industrial').
  • For each people agent: definition of start_work and end_work hours attributes that respectively represent when the agent leaves its house to go to work and when it leaves its working place to go back home. These hours will be randomly defined between 6 AM (min_work_start) and 8 AM (max_work_start) for start_work and 4 PM (min_work_end) and 8 PM (max_work_end) for end_work.
  • For each people agent: definition of an objective attribute: this one can 'go home' or 'working'.
  • For each people agent: define a speed. The speed will be randomly defined between 1 km/h (min_speed) and 5 km/h (max_speed).
  • The people agents move along the road, taking the shortest path.
  • All the previously introduced values will be defined in global attributes.

Model Definition

people agents

First, we have to change the skill of the people agents: as we want to use an action of the moving skill (goto), we will provide the people agents with this skill. A skill is a built-in module that provide the modeler a self-contain and relevant set of actions and variables.

species people skills: [moving] {
...
}

Then, we have to add new attributes to the people agents: living_place, working_place, start_work, end_work and objective. In addition, we will add a the_target variable that will represent the point toward which the agent will be currently moving.

species people skills: [moving]{
rgb color <- #yellow ;
building living_place <- nil ;
building working_place <- nil ;
int start_work ;
int end_work ;
string objective ;
point the_target <- nil ;

...
}

We define two reflexes that allow to change the objective (and the_target) of the agent at the start_work and end_work hours. Concerning the_target value, we choose a random point in the objective building (working_place or living_place) by using the any_location_in operator. The attribute current_date is a built-in global attribute of type date that is automatically incremented from the starting_date of the simulation by step value.

species people skills: [moving]{  
...
reflex time_to_work when: current_date.hour = start_work and objective = "resting" {
objective <- "working" ;
the_target <- any_location_in (working_place);
}

reflex time_to_go_home when: current_date.hour = end_work and objective = "working" {
objective <- "resting" ;
the_target <- any_location_in (living_place);
}
...
}

At last, we define a reflex that allows the agent to move. If a target point is defined (the_target != nil), the agent moves toward its target using the goto action (provided by the moving skill). Note that we specified a graph to constraint the movement of the agents on the road network with the facet on. We will see later how this graph is built. The agent uses the shortest path (according to the graph) to go to the target point. When the agent arrives at its destination (the_target = self.location), the target is set to nil (the agent will stop moving).

species people skills: [moving]{
...
reflex move when: the_target != nil {
do goto target: the_target on: the_graph ;
if the_target = location {
the_target <- nil ;
}
}
}

Parameters

We add several parameters (and thus global variables) (min_work_start, max_work_start, min_work_end, max_work_end, min_speed and max_speed) and two global variables: the_graph (graph computed from the road network). In addition, we set the starting date in order that the simulation starts at midnight.

In the global section:

global {
...
date starting_date <- date("2019-09-01-00-00-00");
int min_work_start <- 6;
int max_work_start <- 8;
int min_work_end <- 16;
int max_work_end <- 20;
float min_speed <- 1.0 #km / #h;
float max_speed <- 5.0 #km / #h;
graph the_graph;
...
}

In the experiment section, we add a parameter statement for each global variable we want to have as a parameter:

experiment road_traffic type: gui {
...
parameter "Earliest hour to start work" var: min_work_start category: "People" min: 2 max: 8;
parameter "Latest hour to start work" var: max_work_start category: "People" min: 8 max: 12;
parameter "Earliest hour to end work" var: min_work_end category: "People" min: 12 max: 16;
parameter "Latest hour to end work" var: max_work_end category: "People" min: 16 max: 23;
parameter "minimal speed" var: min_speed category: "People" min: 0.1 #km/#h ;
parameter "maximal speed" var: max_speed category: "People" max: 10 #km/#h;
...
}

Initialization

First, we need to compute from the road agents, a graph for the moving of the people agents. The operator as_edge_graph allows doing that. It automatically builds from a set of agents or geometries a graph where the agents are the edges of the graph, a node represent the extremities of the agent geometry.

global {
init {
...
create road from: shape_file_roads ;
the_graph <- as_edge_graph(road);
...
}

We randomly assign one working place and one house to each people agent. To simplify the GAML code, we define two temporary variables: the list of buildings of type 'Residential' and the list of buildings of type 'Industrial' (by using the where command). At the creation of each people agent, we initialize its speed (built-in attribute coming from the moving skill), start_work and end_work to each people agent (according to the min and max values defined in the global). We define as well an initial objective ("resting"). Concerning the definition of the living_place and working_place, these ones are randomly chosen among the residential_buildings and industrial_buildings lists.

    init {
...
list<building> residential_buildings <- building where (each.type="Residential");
list<building> industrial_buildings <- building where (each.type="Industrial") ;
create people number: nb_people {
speed <- rnd(min_speed, max_speed);
start_work <- rnd (min_work_start, max_work_start);
end_work <- rnd(min_work_end, max_work_end);
living_place <- one_of(residential_buildings) ;
working_place <- one_of(industrial_buildings) ;
objective <- "resting";
location <- any_location_in (living_place);
}
...
}

Complete Model

model tutorial_gis_city_traffic

global {
file shape_file_buildings <- file("../includes/building.shp");
file shape_file_roads <- file("../includes/road.shp");
file shape_file_bounds <- file("../includes/bounds.shp");
geometry shape <- envelope(shape_file_bounds);
float step <- 10 #mn;
date starting_date <- date("2019-09-01-00-00-00");
int nb_people <- 100;
int min_work_start <- 6;
int max_work_start <- 8;
int min_work_end <- 16;
int max_work_end <- 20;
float min_speed <- 1.0 #km / #h;
float max_speed <- 5.0 #km / #h;
graph the_graph;

init {
create building from: shape_file_buildings with: [type::string(read ("NATURE"))] {
if type="Industrial" {
color <- #blue ;
}
}
create road from: shape_file_roads ;
the_graph <- as_edge_graph(road);

list<building> residential_buildings <- building where (each.type="Residential");
list<building> industrial_buildings <- building where (each.type="Industrial") ;
create people number: nb_people {
speed <- rnd(min_speed, max_speed);
start_work <- rnd (min_work_start, max_work_start);
end_work <- rnd(min_work_end, max_work_end);
living_place <- one_of(residential_buildings) ;
working_place <- one_of(industrial_buildings) ;
objective <- "resting";
location <- any_location_in (living_place);
}
}
}


species building {
string type;
rgb color <- #gray ;

aspect base {
draw shape color: color ;
}
}

species road {
rgb color <- #black ;
aspect base {
draw shape color: color ;
}
}

species people skills:[moving] {
rgb color <- #yellow ;
building living_place <- nil ;
building working_place <- nil ;
int start_work ;
int end_work ;
string objective ;
point the_target <- nil ;

reflex time_to_work when: current_date.hour = start_work and objective = "resting"{
objective <- "working" ;
the_target <- any_location_in (working_place);
}

reflex time_to_go_home when: current_date.hour = end_work and objective = "working"{
objective <- "resting" ;
the_target <- any_location_in (living_place);
}

reflex move when: the_target != nil {
do goto target: the_target on: the_graph ;
if the_target = location {
the_target <- nil ;
}
}

aspect base {
draw circle(10) color: color border: #black;
}
}


experiment road_traffic type: gui {
parameter "Shapefile for the buildings:" var: shape_file_buildings category: "GIS" ;
parameter "Shapefile for the roads:" var: shape_file_roads category: "GIS" ;
parameter "Shapefile for the bounds:" var: shape_file_bounds category: "GIS" ;
parameter "Number of people agents" var: nb_people category: "People" ;
parameter "Earliest hour to start work" var: min_work_start category: "People" min: 2 max: 8;
parameter "Latest hour to start work" var: max_work_start category: "People" min: 8 max: 12;
parameter "Earliest hour to end work" var: min_work_end category: "People" min: 12 max: 16;
parameter "Latest hour to end work" var: max_work_end category: "People" min: 16 max: 23;
parameter "minimal speed" var: min_speed category: "People" min: 0.1 #km/#h ;
parameter "maximal speed" var: max_speed category: "People" max: 10 #km/#h;

output {
display city_display type: opengl {
species building aspect: base ;
species road aspect: base ;
species people aspect: base ;
}
}
}