Assignment 2
3D city model processing
Deadline is 01 June 2026 at 12:30 pm
Late submission? 10% will be removed for each day that you are late.
You’re allowed for this assignment to work in a group of 3 (and thus submit only one solution for all of you). If you prefer to work alone or in pairs, it’s also fine.
The assignment is worth 20% of your final mark.

- Overview
- Your code should be able to read any 3DBAG file as input
- Methodology/workflow to use
- Requirements for the program
- Requirements for the CityJSON output
- Some tips
- Report
- Marking
- What to submit and how to submit it
Overview
In its current form, the 3DBAG dataset contains for each of the ~11M buildings in the Netherlands:
- the 3D geometry at three LoDs: 1.2+1.3+2.2
- several attributes, e.g. the construction year, the height of the ground, the type of roof, current status of the building, etc.
For this assignment, you are asked to modify/manipulate the 3DBAG and add some attributes that are useful in practice. Given one tile of the 3DBAG, your programme should do the following:
- keep only the LoD2.2 (and remove other LoDs in the file)
- put the LoD2.2 geometry into its parent
"Building"(and delete the"BuildingPart") - triangulate the geometry (and update all parts related, e.g. semantic surfaces)
- calculate the volume of the LoD2.2 geometry and add it as attribute
- calculate the total area of the roofs and add it as attribute
The updated model must be delivered in the CityJSON format (v2.0).
Your code should be able to read any 3DBAG file as input
The 3DBAG, developed by the 3D geoinformation group at TU Delft and by 3DGI (and also the topic of Lesson 7.1), is a dataset containing 3D models of all the buildings in the Netherlands. The 3DBAG is open data and it contains 3D models at multiple levels of detail (LoDs), which are generated by combining two open datasets: the building data from the BAG and the height data from the AHN. The 3DBAG is updated regularly, keeping it up-to-date with the latest openly available building stock and elevation information. More details about the 3DBAG are available on its website.
The buildings are tiled, and you can download a tile (which typically contains a few hundred buildings). To start, you can practice on the tile where BK-City is located:
Methodology/workflow to use
You have to use C++ and the CGAL library to do this assignment.
Your code needs to be a single binary/executable that reads one 3DBAG tile in CityJSON, and it will output a new one with the modifications applied.
You can use any packages of CGAL except the following functions, which you have to code (these are essential for the assignment and it’s one learning objective to learn how to implement those yourself):
- CGAL::area()
- CGAL::squared_area()
- CGAL::determinant()
- CGAL::normal()
- CGAL::volume()
- CGAL::Polygon_mesh_processing::triangulate_face()
- CGAL::Polygon_mesh_processing::triangulate_faces()
- CGAL::Polygon_mesh_processing::triangulate_polygons()
(Perhaps we forgot one that is similar to those, but the idea is to learn to code those, so ask us in doubt)
1. Keep only LoD2.2 and merge it to its parents
In the 3DBAG, a "Building" has a "lod": "0" geometry with several attributes, and the 3 LoDs (1.2, 1.3, and 2.2) are in its child "BuildingPart".
For your programme, you need to delete all LoDs except LoD2.2, which you move to its parent.
The "BuildingPart" should be deleted.
2. Triangulation of the surfaces
We refer to a triangulation of the surfaces of the buildings, not a volumetric triangulation.
Since you may not use CGAL’s triangulate_face() / triangulate_faces() / triangulate_polygons() functions, you must implement the triangulation yourself.
We recommend the following approach (since it works for polygons with holes):
- For each polygon (surface) of the building’s LoD2.2 geometry, compute its best-fitting plane using principal component analysis.
- Project the polygon’s vertices to 2D using that plane (CGAL’s
Plane_3::to_2d()). - Create a constrained 2D triangulation containing all of the polygon’s edges as constraints.
- Label each triangle of the 2D triangulation as interior or exterior using the odd-even rule: starting from the infinite face (which is exterior), traverse neighbours; crossing an odd number of constraints means interior, an even number means exterior.
- Extract the interior triangles back to 3D using the plane’s
to_3d().
Observe that the orientation must be preserved, and validity (with val3dity) must be maintained: if a LoD2.2 geometry is valid in the input file, it should also be valid in the output file.
3. Volume
You have to calculate the volume of each "Building" (their LoD2.2 geometry) and this value needs to stored as a new attribute called "geo1004_volume").
If \(E\) is the exterior boundary/envelope of a building, then we define its volume by \(Vol(E)\).
You have to implement the method yourself, you are thus not allowed to use an off-the-shelf solution that you find somewhere or the CGAL::volume() functions.
Moreover, we prescribe the method that needs to be used, which is a generalisation of the method that you learned in GEO1002. You need to decompose the solid of the building into tetrahedra, and sum the (signed) volume of each.

Observe that the 3DBAG (which you use) already has an attribute b3_volume_lod22; I guess your aim is to verify that those values are correct.
4. Total area of the RoofSurfaces
Also, for each "Building", you need to add one attribute (called "geo1004_total_roof_area") that gives the total area, in square metres, of the surfaces with semantics "RoofSurface".
Requirements for the program
It should take one argument (the input CityJSON file), and output in the same folder as the file a new file called where _out is added before .city.json (so myfile.city.json becomes myfile_out.city.json).
The demo code for hw02 shows how this can be done; feel free to use that code and modify it. You can also start from scratch, it’s up to you.
Requirements for the CityJSON output
- use CityJSON version 2.0
- the file you produce must be valid according to the CityJSON schema v2.0 (use
cjvalto verify this). - The only difference allowed is that the order of the properties doesn’t need to be the same since your library might not allow you to have control over this.
- Be careful about the types and names of the attributes, they are shown below.
Some tips
- You need to consider that the buildings in a 3D city model will sometimes not be geometrically valid (in the wild, they seldom are in practice), and you cannot assume so. So, your algorithms must deal with this (and not crash because of invalid input).
- the CityJSON file you read as input will be syntactically valid and of version 2.0, and always be structured as the 3DBAG files (with 3 LoDs).
- the validator cjval can be used locally to verify the syntax of the files you create, it’s faster and simpler to use than the web-app.
- you can verify that your area and volume calculations are correct with different tools, but MeshLab is probably the simplest:
Filters / Quality Measure and Computations / Compute Geometric/Topological Measures. - You can visualise your files (geometry + attributes) using ninja.cityjson.org, QGIS (using the CityJSON Loader plugin), or azul (macOS only).
- To view and understand errors it’s best to use CJLoupe.
- CGAL has many packages that can be of great help: explore!
Report
The short report should only discuss, for each of the parts (triangulation, area, and volume), the algorithm you used, the engineering decisions you took (why did you do it this way or not another way?), and the difficulties/issues you faced and how you solved them. And add a short section who-did-what (we will check the commits also).
We expect a report of about 4 pages max (including figures).
Marking
| Criterion | Points |
|---|---|
| report (quality/readability/etc) | 2 |
| file valid + rules respected | 1 |
| triangulation | 3 |
| volume | 2 |
| area of RoofSurfaces | 2 |
What to submit and how to submit it
Everything will be done with GitHub. You have to create a private GitHub repository where all the code, output data, and the report go.
Before the deadline, you need to invite @hugoledoux, @kenohori, and @GinaStavropoulou as collaborators to the repository (how to do this).
After that, email me (h.ledoux@tudelft.nl) with (one email per team):
- the URL of the repository,
- the name and student number of each member.
Do not add or modify anything after the deadline! You will get penalised for late submission.
The structure of your repository should be as follows (if you have more files before submission, just delete them from the final version please):
├── report
│ └── report.pdf
│ └── (report.tex if used, if you want)
│ └── (report.docx if used, if you want)
├── data
│ └── nextbk_2b.city.json
│ └── 9-284-556_out.city.json
├── cpp
│ └── src/
│ └── main.cpp
│ └── (others *.cpp or *.h if needed)
│ └── include/
│ └── json.hpp
│ └── CMakeLists.txt (it should work with the code you submitted)
├── README.md
The /data folder contains the output you obtain with your code for the 3DBAG tile 9-284-556
In the README.md, add the name and student number of each of the members.
[last updated: 2026-05-13 10:57]
