A relation holds the description of what happens when entities interact with each other.
Ocelet makes extensive use of the concept of interaction graph. An interaction graph is first of all a graph (in the mathematical sense of the term), that is, it is composed of a set of entities (the nodes of the graph) and a set of links between the entities(the arcs of the graph).
With the definition of a relation, it is possible to build an instance (or more) of the interaction graph. The definition is first about the nature of the graph: it indicates the type of entities that are likely to interact. It then describes what happens when the entities interact with each other: this is done in interaction functions.
A relation can be used in a scenario through calls to predefined functions (for building the graphs), then through filter functions, and interaction functions that have been defined.
Definition of a relation
relation
Name of the relation<
Entity1 role1,
Entity2 role2>
{
definitions of interaction functions, filters, and properties of arcs}
The Name of the relation must begin with a letter (preferably upper case) but can eventually be followed by numbers, and not contain blanks or special characters.
The definition of a relation can contain as many properties, filters and interaction functions as needed.
In this definition, Entity1 and Entity2 are entity types that have to be defined elsewhere. These are not instances but real entity types (for that reason they are given names starting with a capital letter). The terms role1 and role2 are identifiers to be chosen, and will be used to represent an instance of an entity (a node of the graph) in the interaction functions. In other words, when defining an interaction function, role1 will be used as if it was a variable of type Entity1, and the same with role2 as an instance of Entity2. Each arc of the graph will therefore have two ends: role1 and role2, and the definition of the interaction functions will allow to describe what happens between the two ends during an interaction.
Definition of an interaction function
interaction
name of the function(
(Type argument (,
Type argument)* )?)
{
function body code
(agg
{
aggregation operators}
)?}
The name of the function must begin with a letter (preferably lower case) but can eventually be followed by numbers, and not contain blanks or special characters.
Optionally, one or more arguments can be declared that will be passed on to the function (the (...)?
notation used here means 0 or 1 occurence). In that case each argument will be declared per Argument Type
. The Type is either a simple Ocelet type ( Integer, Double, Point, etc. ), a composite type, an entity type, a datafacer, or even another relation. The argument is a chosen variable name and must begin with a letter in lower case.
If several arguments are declared, they must be separated with a comma: Type1 arg1, Type2 arg2, Type3 arg3
etc. (The (...)*
notation used above means 0 occurrence or more).
The function body code contains a series of instructions of the language, calls to usual functions, or to aggregation functions. This code describes what happens on each arc of the graph when an entity role1 interact with an entity role2.
It is important to note that in the code of an interaction function, the properties of the entities role1 and role2 are not modified directly when assigned a new value. The principle is to read state n and write state n+1 of the properties. the effective modification is carried out once the interaction function has been applied to all of the arcs of the graph.
In some cases, it is through an aggregation operator that this assignment takes place. A specific syntax has to be used to indicate for which properties the aggregation operators will be called:
agg
{
( role.
property<<
|+<<
aggregation operator)*}
Aggregation operators can de declared for as many properties as necessary, as indicated by the (...)*
notation. An aggregation operator can be taken among those predefined in Ocelet (Random, Max, Min, Mean, etc.) or a user defined one.
The <<
| +<<
notation means that either <<
or +<<
can be used. In the first case, only new values assigned to the property (values of state n+1) are passed to the list of candidate values to the aggregation operator. In the second case, the previous value (of state n) of the property is also included in the list of candidate values. See section on the usage of aggregation operators for more details.
Definition of a property of an arc
When necessary, properties can be attached to arcs of the interaction graphs. These properties can just be defined in the relation. The values of these arc properties can be updated and read in the code of the interaction functions as if they were variables. each property can be declared in this way:
property
Type name
The Property Type is either a simple Ocelet type (Integer, Double, Point, etc.), or a composite type defined by the user using a structure.
The name of the property must begin with a letter (preferably lower case) but can eventually be followed by numbers, and not contain blanks or special characters.
Definition of a filter function
Filters are functions for retaining only certain arcs of the interaction graph. If for example, an interaction function needs to be applied to only part of the graph, it may be convenient to use a filter to select the arcs that fulfill somr criteria.
A filter is therefore a function that must determine whether each of the arc must be retained or not, and return a vlue of type Boolean: either true
(if the arc is retained) or false
(if not retained). The syntax for defining a filter on the arcs is as follows:
filter
name of filter{
code of filter}
The name of the filter must begin with a letter (preferably lower case) but can eventually be followed by numbers, and not contain blanks or special characters.
The code of filter is the body of the function. It must return either true
(if the arc is retained) or false
(if not).
One or more entity types can be referred to in the definition of a Relation. For this reason, entity definitions have also been included in the examples below.
entity Plot {
Integer id
Integer landcover
MultiPolygon geom
String crop
}
relation Neighbour<Plot p1, Plot p2> {
// True if p1 and p2 have the same land cover as the argument lc provided
filter sameLandcover(Integer lc) {
return ((p1.landcover == lc) && (p2.landcover == lc))
}
// True if distance between p1 and p2 is less than dmax
filter distance(Double dmax) {
return (p1.geom.distance(p2.geom) <= dmax)
}
// Draws an arc of the graph in a Kml file
// KmlOut used here is a Datafacer of type KmlExport
interaction drawEdge(KmlOut ko, Date beg, Date end){
let pc1 = Point|xy(p1.geom.centroid.x,p1.geom.centroid.y)
let pc2 = Point|xy(p2.geom.centroid.x,p2.geom.centroid.y)
let arc = Line|points(pc1,pc2)
ko.addGeometry("Neighbours",p1.id+" -- "+p2.id,beg,end,arc,"neighb",0)
}
}
entity Farmer {
// The farmer chooses which crop to put on a given plot
// given the plot identifier and the date.
service String getSowing(Integer idParc, Date datesow) {
...
}
}
relation Farm<Farmer fm, Plot p> {
// The crop used on the plot is updated
interaction Sowing(Date dateSow) {
p.crop = fm.getSowing(p.id, dateSow)
}
}
A Relation is defined to describe a type of interaction graph. From that definition, one or more variables can be created containing a graph of that type (an interaction graph is an instance of a relation).
scenario MyModel {
// A list of plots obtained from a Shapefile datafacer
let shpData = new ShpDatafacer
let lplot = shpData.readAll()
// Creata a neighbourhood graphe
let graphNeighbourhood = Neighbour{}
// Add a list of plots to the graph
graphNeighbourhood.addAllPlot(lplot)
...
}
Functions available on interaction graphs
Once a variable is initialised with an interaction graph, two types of functions can be applied to the graph:
Predefined functions
connect()
: adds an arc that has been retained in a graph filterdisconnect()
: removes an arc from the graphgetComplete()
: run through the (virtual) arcs of the complete graph. The arcs of the complete graph are not really added to the graph.Integer size()
: returns the number of arcs in the graphFunctions generated from the definition of a relation
// Builds arcs between neighbouring plots
// (less than 30m distance between them)
graphNeighbourhood.complete.distance(30).connect()
Interaction Graphs where an entity contains a Cell type
When using a relation between entities not defined with Cell type entities, connections between entities need to be explicitly done using the connect() function. In the case of a relation where a least one of the entities has a Cell property, and the others are defined with either a Geometry or a Cell type, the connection is implicit through spatial correspondance or neighbourhood. There exist three types of implit graphs, according to the representation used:
Predefined functions for a relation between cellular entities of the same type
connect(List<Entity> entity)
: Automatically connect cellular entities with their neighboursInteger size()
: returns the number of edges of the graphFunctions generated from the definition of a relation
createSquares(Geometry geom, Double resolutionX, Double resolutionY)
: Create square or rectangular shape cellular entities. Resolution correspond to the length of the sides of the square/rectangle. Entities are created to cover the zone corresponding to the envelope of the geometry passed on as parameter.createHexagons(Geometry geom, Double resolution)
: Create regular hexagonal shape cellular entities. Resolution correspond to the length of the side of the hexagon. Entities are created to cover the zone corresponding to the envelope of the geometry passed on as parameter.createTriangles(Geometry geom, Double resolution)
: Create equilateral triangle shape cellular entities. Resolution correspond to the length of the side of the triangle. Entities are created to cover the zone corresponding to the envelope of the geometry passed on as parameter.
Création d'entités cellulaires de forme triangulaire. La résolution correspond au côté du triangle. getAllEntity_Name()
: Returns the list of cellular entities created with the methods createSquares(...), createHexagons(...) or createTriangles(...), where Entity_Name is the name used in the definition of the entity type. extendedMoore(Integer distance)
: Create an extended Moore neighbourhood defined by a neighbourhood distance expressed in number of pixels. extendedCircularMoore(Integer distance)
: Create an extended circular Moore neighbourhood defined by a neighbourhood distance expressed in number of pixels.Predefined functions for a relation between cellular entities of different types
connect(List<Entity1> entities1, List<Entity2> entities2)
: Automatically connect cellular entities of the first list to entities of the second list per spatial correspondance.Integer size()
: returns the number of edges of the graphPredefined functions for a relation between cellular entities and entities with geometry type properties
connect(List<Entity1> entities1, List<Entity2> entities2)
: Automatically connect cellular entities to geometric entities per spatial correspondance.Integer size()
: returns the number of edges of the graphExemple d'utilisation dans un scénario
scenario MyModel {
// To obtain a list of parcels from a Shapefile datafacer
let shpData = new ShpDatafacer
let lparc = shpData.readAll()
// Creation of a neighbourhood graph
let graphNeighbourhood = new Neighbour
// Add a list of Plots to the graph
graphNeighbourhood.addAllPlot(lplot)
//Obtain a list of DtmCell from a RasterFile datafacer whose spatial extent is taken from that of the Shapefile
let dtm = new Dtm
let dtmList = dtm.readAllDtmCell(shpData.boundaries)
//Create a neighbourhood relation between Hexagon entities
let hexaNeighbours = new HexagonNeighbours
//Create entities from the previous relation on the extent defined from the Shapefile ()hexagons have a 100 m side)
hexaNeighbours.createHexagons(shpData.boundaries, 100.0)
//Obtain a list of Hexagons from the relation
let hexaList = hexaNeighbours.getAllHexagon
// Create a relation between Hexagons and Plots
let hexaPlot = new HexagonAndPlot
hexaPlot.connect(hexaList, lplot)
//Run an interaction to set land cover types from plots to hexagons
hexaPlot.setLC
// Create a relation between Hexagons and dtmCells
let dtmHexa = new dtmToHexagon
dtmHexa.connect(dtmList, hexaList)
//Run interaction to set the altitude from dtmCells to hexagons
dtmHexa.setDtmValue
//Run interaction on hexagon Neighbours to calculate the average altitude of neighbouring cells and assign it to the central hexagon
hexaNeighbours.meanOfNeighbours
...
}