Module 1c, Learning the basic concepts of GeoDMS, calculations over multiple domains


learning objective: learning how to make calculations across multiple domain units
introduction
In modelling, data often needs to be related or (dis)aggregated between multiple domain units. In a relational database, this is done with join and group-by statements. In the GeoDMS, this is done through explicitly configured relations.
what is a relation?
A relation is an attribute that connects two domain units. Concretely: for each element in domain unit A, the relation records the corresponding position (index number) in domain unit B.
For example, suppose you have municipalities and provinces as two domain units. A relation from municipality to province tells you, for each municipality, which province it belongs to:
| municipality (domain A) | municipality_rel (→ index in province) |
|---|---|
| Amsterdam | 2 (Noord-Holland) |
| Rotterdam | 3 (Zuid-Holland) |
| Utrecht | 4 (Utrecht) |
| Eindhoven | 6 (Noord-Brabant) |
The values unit of the relation is the domain unit being pointed to — in this case province. The domain unit of the relation is the source domain — municipality.
This leads to the definition:
A relation is an attribute with domain unit A and values unit B (where B is also a domain unit). It relates attributes between domain units A and B.
By using index numbers rather than string keys, the GeoDMS can evaluate relations very efficiently, even on large datasets.
Naming convention: use the name of the target domain unit with the suffix _rel as the relation name. A relation towards province should be named province_rel, a relation towards municipality should be named municipality_rel.
how to make a relation
The GeoDMS provides several functions that produce a relation. The three most common approaches are:
1. Based on matching keys or codes
If both domain units share a common identifier (such as a municipality code), use rlookup to create a relation:
// CBS neighbourhood table has a municipality code attribute.
// rlookup finds the position of each neighbourhood's code in the municipality code list:
attribute<municipality> municipality_rel (neighbourhood) :=
rlookup(neighbourhood/municipality_code, municipality/code);
2. Based on a geographic relationship
For spatial joins — for example, finding which province each municipality centroid falls in — use point_in_polygon:
attribute<province> province_rel (municipality) :=
point_in_polygon(municipality/centroid, province/geometry);
3. Based on a classification
Numeric attributes can be classified into class units using a classify function. The result is a relation from the original domain to the class domain:
attribute<size_class> size_class_rel (province) :=
classify(province/inhabitants, size_class/ClassBreaks);
This connects province to size_class based on which class each province’s population falls into. Classifications are covered in detail in Module 1d.
Other GeoDMS functions also produce relations — see the documentation of individual functions for their signatures.
how to use a relation
Once you have a relation, you can use it in two main ways:
Lookup: bringing an attribute from one domain to another
The [] operator performs an index lookup — it maps a value from domain B to each element of domain A using the relation:
// For each municipality, look up the province name:
attribute<string> province_name (municipality) :=
province/name[municipality/province_rel];
Read this as: “for each municipality, take the index stored in province_rel and use it to look up the corresponding value from province/name”.
You also see this pattern when working with selections. Selection functions create a new (smaller) domain unit and produce an org_rel attribute — a relation from the selection back to the original domain:
// select only provinces with more than 1 million inhabitants:
unit<uint32> large_province := select_with_org_rel(province/inhabitants > 1000000[nr_inh]);
// bring attributes from the original domain to the selection:
attribute<string> name (large_province) := province/name[large_province/org_rel];
Aggregation: computing group statistics
Relations also enable aggregation — computing a summary value per group. For example, summing the number of inhabitants per province, given neighbourhood-level data:
attribute<nr_inh> total_inhabitants (province) :=
sum(neighbourhood/inhabitants, neighbourhood/province_rel);
The second argument to sum is the relation — it tells the GeoDMS which province each neighbourhood belongs to.

Reading tip: for an overview of all aggregation functions, see aggregation functions
try it yourself!
In this exercise, you will create a relation between two postcode domain units and use it to count elements.
- Download the project here and unzip it to a project folder such as C:/prj/GeoDMSAcademy.
- Open exercise.dms (in the
GeoDMS_Academy/basics_multiple_domains/cfgsubfolder) in a text editor. The configuration contains aPC4_pointsdomain (4-digit postcode centroids).
Work through the following steps:
- Create a new attribute with the first two characters of each PC4 code — this is the PC2 code for each point.
- Use the unique operator to create a new domain unit
PC2_pointsfrom the unique PC2 values.uniquetakes an attribute and returns a new domain unit containing each distinct value exactly once, along with avaluesattribute holding those values. - Create a relation
pc2_relfrom thePC4_pointsdomain to the newPC2_pointsdomain using rlookup. - Count the number of PC4 points per PC2 point using sum or count with the relation.
Try to solve these steps yourself first. The reference solution is in result.dms in the same cfg subfolder.
Go to previous module: Module 1b, Learning the basic concepts of GeoDMS, understanding units
Go to next module: Module 1d, Learning the basic concepts of GeoDMS, classifying and visualising data