CityJSONSeq (CityJSON Sequence)
Table of contents
- CityJSONSeq specifications
- CityJSONFeature
- Streaming 3D cities with CityJSONSeq
- Reading and writing CityJSONSeq with cjio
- CityJSONSeq examples
- Validating a stream
- cjfview: a small viewer for CityJSONSeq files
CityJSON Sequence—CityJSONSeq for short, or CityJSON Lines—is a format based on JSON Text Sequences and CityJSON. The idea is to decompose a (often large) CityJSON file into its features (eg each building, each bridge, each road, etc.), to create several JSON objects (of type CityJSONFeature
), and stream/store them in a JSON Text Sequence (for instance ndjson – newline delimited JSON).
CityJSONSeq specifications
We follow the specifications of ndjson – newline delimited JSON and we add 2 constraints for handling CityJSON:
- each JSON Object must conform to the JSON Data Interchange Format specifications and be written as a UTF-8 string;
- each JSON Object must be followed by a new-line (LF:
'\n'
) character, and it may be preceded by a carriage-return (CR:'\r'
); - a JSON Object must not contain the new-line or carriage-return characters;
- the first JSON Object must be of type
'CityJSON'
(see below for details); - the following JSON Objects are of type
'CityJSONFeature'
;
Suggested convention: we recommend using the extension .city.jsonl
when saving the JSON Objects to a file.
CityJSONFeature
A CityJSONFeature
object represents one feature in a CityJSON object, for instance a "Building"
(with eventually its children "BuildingPart"
and/or "BuildingInstallation"
). The idea is to decompose a large area into each of its features, and each feature is a stored as a CityJSONFeature
. Each feature is independent, and has its own list of vertices (which is thus local).
See the full specifications for a CityJSONFeature.
{
"type": "CityJSONFeature",
"id": "id-1",
"CityObjects": {
"id-1": {
"type": "Building",
"attributes": {
"roofType": "gabled roof"
},
"children": ["mypart"],
"geometry": [...]
},
"mypart": {
"type": "BuildingPart",
"parents": ["id-1"],
"children": ["mybalcony"],
"geometry": [...]
},
"mybalcony": {
"type": "BuildingInstallation",
"parents": ["mypart"],
"geometry": [...]
}
},
"vertices": [...]
}
Streaming 3D cities with CityJSONSeq
Since we want to have access to some properties, eg "transform"
and the CRS, those need to be known by the client/software parsing the stream.
The first JSON Object should therefore be of type "CityJSON"
and contain the necessary information. Notice that the properties "CityObjects"
and "vertices"
are mandatory (for the JSON Object to be valid) but should be respectively an empty JSON object and an empty array. One example would be:
{"type":"CityJSON","version":"2.0","transform": {"scale":[1.0,1.0,1.0],"translate": [0.0, 0.0, 0.0]},"metadata":{"referenceSystem":"https://www.opengis.net/def/crs/EPSG/0/7415"},"CityObjects":{},"vertices":[]}
The subsequent JSON Objects must all be of type "CityJSONFeature"
, which means a CityJSONSeq with 3 features could look like this one:
{"type":"CityJSON","version":"2.0","transform": {"scale":[1.0,1.0,1.0],"translate": [0.0, 0.0, 0.0]},"metadata":{"referenceSystem":"https://www.opengis.net/def/crs/EPSG/0/7415"},"CityObjects":{},"vertices":[]}
{"type":"CityJSONFeature","id":"a","CityObjects":{...},"vertices":[...]}
{"type":"CityJSONFeature","id":"b","CityObjects":{...},"vertices":[...]}
{"type":"CityJSONFeature","id":"c","CityObjects":{...},"vertices":[...]}
Reading and writing CityJSONSeq with cjio
The software cjio allows us to read and write CityJSONSeq from stdin/stdout (standard input/output streams).
We can create a CityJSONSeq stream (with the first line containing the metadata) this way:
cjio --suppress_msg myfile.city.json export jsonl stdout
Observe that the different operators of cjio output messages/information, and those will get in the stdout stream. To avoid this, add the flag --suppress_msg
when reading the file.
That stream can be saved to a file:
cjio --suppress_msg myfile.city.json export jsonl mystream.city.jsonl
A CityJSONSeq stream/file can be compiled to a CityJSON file by reading it from stdin
:
cat mystream.city.jsonl | cjio stdin info save myfile_2.city.json
CityJSONSeq examples
dataset | CityJSONSeq file | description |
---|---|---|
3DBAG | 3dbag_b2.city.jsonl | 2 buildings randomly selected from the 3DBAG, LoD2.2 only |
Montréal | montréal_b4.city.jsonl | 4 buildings randomly selected from the Montréal dataset |
Validating a stream
With the online validator
The official schema-validator of CityJSON accepts CityJSONSeq files, if they are structured as above (3dbag_b2.city.jsonl and montréal_b4.city.jsonl are two examples).
You can just drop those files and the validator will indicate, per line, if the CityJSONFeature
are valid, or not.
Locally with cjfval
The official schema-validator of CityJSON (called cjval) can validate CityJSONSeq streams with its binary cjfval
. Each line is individually validated and errors reported:
cjio --suppress_msg myfile.city.json export jsonl stdout | cjfval --verbose
l.1 ✅
l.2 ❌ {"attributes":{"function":"something"},"geometry":[{"boundaries":[[[[0,1,2,3]],[[4,5,0,3]],[[5,6,1,0]],[[6,7,2,1]],[[3,2,7,4]],[[7,6,5,4]]]],"lod":"1","type":"Solid"}],"type":"+99999GnericCityObject"} is not valid under any of the given schemas [path:/CityObjects/id-1] |
l.3 ✅
l.4 🟡 Vertex (0, 1000, 0) duplicated | Vertex #8 is unused |
l.5 ✅
l.6 ✅
cjfview: a small viewer for CityJSONSeq files
The cjfview GitHub repository has more details.
It reads a CityJSONSeq file from stdin.
cat ./data/b2.city.jsonl | python ./src/cjfview.py
cjio --suppress_msg Vienna_102081.city.json subset --random 5 export jsonl stdout | python ./src/cjfview.py