Task: Collect Open 3D City Model Datasets
Objective: Create configuration files for open 3D city model datasets to build a comprehensive global STAC catalog.
Deliverable: Multiple YAML configuration files (one per dataset) in the collections/ directory.
Target Deadline: 📅 March 6th, 2026 (negotiable - see Section 2.1)
Table of Contents
- Background
- Prerequisites
- Timeline & Deadline
- Head Start: Existing Draft Configs
- Understanding the Config File
- Data Sources
- Programmatic Data Collection
- Step-by-Step Workflow
- Common Scenarios
- Quality Checklist
- Naming Conventions
- Submitting Your Work
- Troubleshooting
- Working with Uncertainty
- Quick Reference
1. Background
What is STAC?
STAC (SpatioTemporal Asset Catalog) is a specification for describing geospatial data. Think of it as a standardized way to catalog and search for spatial datasets.
What are we building?
We're creating a global catalog of open 3D city models. These are digital representations of cities in 3D, including buildings, roads, vegetation, and more. Common formats include:
- CityJSON - A JSON format for 3D city models
- CityGML - An XML/GML format for 3D city models
Why this matters
Currently, finding open 3D city model datasets is difficult - they're scattered across various websites, portals, and repositories. By creating a standardized STAC catalog, we make it easy for:
- Researchers to discover and access 3D city data
- Developers to build applications using this data
- Cities to share their 3D models with the world
Your role
You'll be finding open 3D city model datasets and creating configuration files that describe each dataset. These configs will be used to automatically generate STAC metadata.
2. Prerequisites
What you need
- Text editor - Any code editor (VS Code, Sublime, etc.) or even a simple text editor
- Web browser - To access data sources and verify URLs
- Basic YAML understanding - YAML is a simple configuration format (see examples below)
Helpful but not required
- Familiarity with geospatial data concepts
- Understanding of coordinate reference systems (CRS)
- Knowledge of open data licenses
Setup
- Clone or access this repository
- Navigate to the project directory
- Create a
collections/ directory if it doesn't exist:
2.1 Timeline & Deadline
Target Deadline: March 6th, 2026 📅
This is a soft target - the deadline is negotiable based on what you discover.
Suggested Timeline
| Week |
Focus Area |
Target Output |
| Week 1 |
Explore sources, collect URLs |
15-20 dataset configs |
| Week 2 |
Continue collection, fill metadata |
15-20 more dataset configs |
| Week 3 |
Finalize configs, validate, open PR |
Complete PR with all configs |
Scope is Flexible
The exact number of datasets is not fixed. What matters more:
- ✅ Quality over quantity - 20 well-documented configs > 50 poorly documented ones
- ✅ Geographic diversity - Datasets from different countries/regions
- ✅ Format variety - Mix of CityJSON and CityGML datasets
- ✅ URLs are real - Links point to actual data files, not just portals
💡 Work Smart: Use AI Tools!
This is not an assignment or exam - it's a real task where the goal is to get it done efficiently. You're encouraged to:
-
✅ Use AI assistants (ChatGPT, Claude, etc.) to help with:
- Writing scraping scripts
- Extracting metadata from web pages
- Generating YAML configs from template
- Debugging automation code
- Researching licenses and data sources
-
✅ Leverage existing tools:
- Browser extensions for web scraping
- GitHub Copilot for code generation
- AI-powered research tools
-
✅ Focus on outcomes, not process:
- The sooner you finish, the better
- No one will audit your workflow
- Results matter more than how you got them
Example AI Prompts:
"Write a Python script to scrape all .city.json download links from https://3d.bk.tudelft.nl/opendata/opencities/"
"Extract license information from this webpage text and convert to SPDX identifier: [paste text]"
"Generate a YAML config for a CityJSON dataset with these properties: title=..., url=..., license=..."
Bottom line: If AI can help you finish this task faster and easier, use it. We care about the catalog, not how you build it.
Negotiable Aspects
If you're running into challenges, we can adjust:
| Issue |
Adjustment Option |
| Can't find enough datasets |
Reduce target count, focus on quality |
| Datasets require complex access |
Document in comments, mark for follow-up |
| License information unclear |
Use proprietary + comments, move forward |
| Technical scraping difficulties |
Switch to manual collection for remaining |
| Need more time |
Extend deadline by 1-2 weeks |
Communication
If you anticipate missing the deadline:
- Don't wait until the last day - communicate early
- Share what you have - open PR with partial collection
- Explain blockers - what's slowing you down?
- Propose adjustment - new deadline, reduced scope, or different approach
Remember: This task is about building a useful catalog, not hitting an arbitrary number. A PR with 30 well-documented datasets opened on March 8th is better than 50 rushed configs opened on March 6th.
2.2 Head Start: Existing Draft Configs 🚀
Good news! Draft configuration files already exist in the opendata/ directory that you can build upon.
What's Already There
The opendata/ directory contains 27 draft collection configs covering datasets from:
| Region |
Config Files |
Count |
| Netherlands |
netherlands-3d-bag, rotterdam, the-hague |
3 |
| Germany |
berlin, dresden, hamburg, potsdam, ingolstadt, north-rhine-westphalia |
6 |
| Finland |
helsinki, espoo, vantaa |
3 |
| Austria |
vienna, linz, luxembourg |
3 |
| France |
lyon |
1 |
| Belgium |
brussels, namur |
2 |
| North America |
new-york-doitt, new-york-tum, montreal, american-cities |
4 |
| Asia |
singapore, japan-plateau |
2 |
| Other |
estonia, various-cityjson |
2 |
Plus: catalog-config.yaml for aggregating all collections
Your Task: Verify & Complete
Instead of starting from scratch, your job is to:
- Review existing configs - Check each file in
opendata/
- Verify accuracy - Are URLs correct? Is license info accurate?
- Fill gaps - Add missing metadata, fix incorrect information
- Test URLs - Verify download links actually work
- Add new datasets - Extend the collection with additional sources beyond these 27
Quick Start with Existing Configs
# List all existing configs
ls opendata/*.yaml
# Review a specific config
cat opendata/vienna-config.yaml
# Validate an existing config
cityjson-stac collection --config opendata/vienna-config.yaml --dry-run
# Validate all existing configs at once
for file in opendata/*-config.yaml; do
echo "Validating $file..."
cityjson-stac collection --config "$file" --dry-run
done
What to Check in Each Config
For each existing config, verify:
| Field |
What to Check |
inputs URLs |
Do the download links work? |
license |
Is the SPDX identifier correct? |
providers |
Are organization names and URLs accurate? |
description |
Is it detailed and accurate? |
extent |
Is the bounding box reasonable? (may be empty for auto-detection) |
keywords |
Are they relevant and useful? |
Common Issues to Fix
Look for and correct:
- 🔗 Broken URLs - Links that redirect or return 404
- 📜 Wrong licenses - Generic
proprietary when actual license is known
- 🏢 Missing providers - Incomplete organization information
- 📝 Vague descriptions - "3D city model" without details
- 🌍 Empty metadata - Missing geographic or temporal info
Example: Review Process
# 1. Pick a config to review
vi opendata/vienna-config.yaml
# 2. Check the URL works
curl -I https://data.data.gv.at/cat/vienna.city.json
# 3. Verify license on source page
# Visit the provider's website and confirm
# 4. Test validation
cityjson-stac collection --config opendata/vienna-config.yaml --dry-run
# 5. Fix any issues found
# Edit the config file
# 6. Re-validate
cityjson-stac collection --config opendata/vienna-config.yaml --dry-run
Bonus: Extend the Collection
Once you've verified the existing configs, add more datasets from:
- Additional cities in the TU Delft portal
- Other open data portals (municipal, national)
- Research repositories
- CityGML/CityJSON community sites
Summary
You're not starting from zero! You have:
- ✅ 27 draft configs to verify and improve
- ✅ A working template structure
- ✅ Coverage of major datasets already identified
Your job is quality assurance + expansion, not blank-slate creation.
3. Understanding the Config File
Each dataset needs a YAML configuration file. Here's the complete structure:
Required Fields
Every config file MUST have:
id: unique-identifier
title: Human Readable Title
description: Detailed description
license: SPDX-LICENSE-IDENTIFIER
providers:
- name: Organization Name
roles:
- producer
inputs:
- https://example.com/data.city.json
Complete Field Reference
| Field |
Required? |
Description |
Example |
id |
Yes |
Unique identifier for the collection |
3dbag-delft-2023 |
title |
Yes |
Human-readable name |
3DBAG Delft |
description |
Yes |
Detailed description (use | for multiline) |
See below |
license |
Yes |
SPDX license identifier |
CC-BY-4.0 |
keywords |
No |
List of tags for categorization |
["3d city model", "buildings"] |
providers |
Yes |
List of organizations involved |
See below |
inputs |
Yes |
Data source URLs or API endpoints |
See below |
extent.spatial |
No |
Bounding box and CRS |
See below |
extent.temporal |
No |
Time range of data |
See below |
links |
No |
Related URLs (license, about, etc.) |
See below |
summaries |
No |
Custom metadata fields |
Various |
Provider Roles
Each provider should have at least one role:
| Role |
When to Use |
producer |
Organization that created the data |
licensor |
Organization that manages the license |
processor |
Organization that processed/transformed the data |
host |
Organization that hosts the data |
curator |
Organization that maintains the data catalog |
Common License Identifiers
| License |
SPDX ID |
When to Use |
| Creative Commons Attribution 4.0 |
CC-BY-4.0 |
Free to use with attribution |
| Creative Commons Zero |
CC0-1.0 |
Public domain, no attribution required |
| Open Data Commons Attribution |
ODC-BY-1.0 |
Open data with attribution |
| MIT License |
MIT |
Permissive software license |
| Not specified |
proprietary |
Unknown or proprietary license |
Find more: https://spdx.org/licenses/
5. Data Sources
Primary Sources to Explore
1. Awesome CityGML (GitHub)
🔗 https://github.com/OloOcki/awesome-citygml#World
A curated list of open 3D city model datasets organized by country. This is your main source.
How to use:
- Navigate to different country sections
- Click through to dataset pages
- Extract metadata from those pages
2. TU Delft Open Cities
🔗 https://3d.bk.tudelft.nl/opendata/opencities/
Academic repository of open 3D city models maintained by TU Delft.
How to use:
- Browse by city or country
- Check for download links or API documentation
- Note any special access requirements
Additional Discovery
Look for datasets in:
- Municipal open data portals - Search "[city name] open data 3d city model"
- National geospatial portals - Many countries have national clearinghouses
- Research repositories - University datasets often available
- CityGML/CityJSON communities - Forums and community sites
What to Include
✅ Include datasets that are:
- Openly accessible (no payment required)
- In CityJSON or CityGML format
- Documented with a license
- From verifiable sources
❌ Don't include:
- Commercial datasets requiring payment
- Unpublished or internal datasets
- Broken or inaccessible links
- Datasets with unclear licensing
5.1 Programmatic Data Collection
⚠️ Important: Manually collecting URLs for dozens of datasets is time-consuming and error-prone. Consider using programmatic approaches to automate data discovery and URL extraction.
Why Automate?
- Scale: You'll likely process 50+ datasets
- Accuracy: Prevents copy-paste errors
- Maintainability: Easy to update when sources change
- Reproducibility: Can verify and re-run collection process
Recommended Approaches
1. Web Scraping
For datasets with predictable page structures:
# Example: Scrape TU Delft Open Cities portal
import requests
from bs4 import BeautifulSoup
url = "https://3d.bk.tudelft.nl/opendata/opencities/"
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
# Extract download links
for link in soup.select('a[href$=".city.json"]'):
title = link.text.strip()
href = link['href']
print(f"{title}: {href}")
Tools:
- Python:
requests, beautifulsoup4, scrapy
- JavaScript:
playwright, puppeteer
2. API Queries
For portals with APIs:
# Example: Query a CKAN-based open data portal
import requests
base_url = "https://data.example.com/api/3/action/package_search"
params = {
'q': 'citygml OR cityjson',
'rows': 100,
'start': 0
}
response = requests.get(base_url, params=params)
data = response.json()
for result in data['result']['results']:
name = result['name']
title = result['title']
download_url = result['resources'][0]['url']
# Generate YAML config...
3. GitHub Scraping
For GitHub-hosted datasets:
# Example: Find CityJSON files in a repository
import requests
import re
# Search GitHub code for .city.json files
api_url = "https://api.github.com/search/code"
params = {
'q': 'extension:city.json language:JSON',
'per_page': 100
}
headers = {
'Accept': 'application/vnd.github.v3+json',
# Add your token: 'Authorization': 'token YOUR_TOKEN'
}
response = requests.get(api_url, params=params, headers=headers)
results = response.json()
for item in results['items']:
print(f"{item['name']}: {item['html_url']}")
GitHub Search Query:
extension:city.json OR extension:gml stars:>10 pushed:>2020
4. Directory Listing Parsing
For web-accessible directories:
# Example: Parse Apache directory listing
import requests
from lxml import html
url = "https://example.com/data/cityjson/"
response = requests.get(url)
tree = html.fromstring(response.content)
# Extract all .city.json links
for link in tree.xpath('//a[contains(@href, ".city.json")]'):
filename = link.text
full_url = url + filename
print(f"{filename}: {full_url}")
Automation Workflow
-
Discovery Script
python scripts/discover_datasets.py > sources/raw_urls.txt
-
Metadata Extraction
# Parse source pages for metadata (license, provider, etc.)
python scripts/extract_metadata.py sources/raw_urls.txt > configs/
-
Config Generation
# Auto-generate YAML configs
python scripts/generate_configs.py sources/metadata.json --output collections/
-
Validation
# Validate all generated configs
for file in collections/*.yaml; do
cityjson-stac collection --config "$file" --dry-run
done
Example: End-to-End Script
#!/usr/bin/env python3
"""
Collect 3D city model datasets from Awesome CityGML list
"""
import requests
from bs4 import BeautifulSoup
import yaml
SOURCES = {
'awesome_citygml': 'https://github.com/OloOcki/awesome-citygml',
'tudelft_opencities': 'https://3d.bk.tudelft.nl/opendata/opencities/',
}
def scrape_github_readme(url):
"""Extract dataset links from GitHub README"""
# Implementation...
pass
def scrape_tudelft_portal(url):
"""Extract datasets from TU Delft portal"""
# Implementation...
pass
def generate_config(dataset):
"""Generate YAML config from dataset info"""
config = {
'id': dataset['id'],
'title': dataset['title'],
'description': dataset['description'],
'license': dataset.get('license', 'proprietary'),
'providers': dataset['providers'],
'inputs': dataset['urls'],
'keywords': dataset.get('keywords', [])
}
return config
def main():
all_datasets = []
for source_name, source_url in SOURCES.items():
print(f"Scraping {source_name}...")
if 'github' in source_url:
datasets = scrape_github_readme(source_url)
else:
datasets = scrape_tudelft_portal(source_url)
all_datasets.extend(datasets)
# Generate YAML configs
for dataset in all_datasets:
config = generate_config(dataset)
filename = f"collections/{dataset['id']}.yaml"
with open(filename, 'w') as f:
yaml.dump(config, f, default_flow_style=False)
print(f"Generated {filename}")
if __name__ == '__main__':
main()
Best Practices
- Respect rate limits - Add delays between requests
- Cache responses - Save scraped data to avoid re-fetching
- Handle errors gracefully - Log failures, don't crash on single errors
- Validate inputs - Check URLs are accessible before generating configs
- Document sources - Keep track of where data came from
- Version control - Commit scraping scripts alongside configs
Tools & Libraries
| Task |
Python |
JavaScript |
| HTTP requests |
requests, httpx |
axios, node-fetch |
| HTML parsing |
beautifulsoup4, lxml |
cheerio, jsdom |
| Web scraping |
scrapy |
puppeteer, playwright |
| YAML handling |
pyyaml |
js-yaml |
| GitHub API |
PyGithub |
@octokit/rest |
7. Step-by-Step Workflow
Follow these steps for each dataset you discover:
Step 1: Identify the Dataset
- Visit the dataset page
- Verify it's a 3D city model (CityJSON/CityGML)
- Check the file format and size
- Determine how to access the data:
- Direct download - Simple URL to a file
- API endpoint - Requires API calls
- Data portal - Web interface or multiple files
Step 2: Extract Metadata
Collect the following information from the dataset page:
Basic Information:
- Title (official name of the dataset)
- Description (what's included, coverage, completeness)
- License (look for "license", "terms of use", or "data rights")
Provider Information:
- Organization name (who created/published the data)
- Organization URL
- Contact information (if available)
Data Access:
- Download URL(s)
- API documentation (if applicable)
- Authentication requirements (API keys, etc.)
Spatial/Temporal Coverage:
- Geographic area (city name, country)
- Bounding box coordinates (if provided)
- Year or date range of the data
Step 3: Create the Config File
- Create a new YAML file in the
collections/ directory
- Use the appropriate template from Section 8
- Fill in all required fields
- Add comments for any special access requirements
- Save with the appropriate filename (see Section 10)
Step 4: Validate the Config
Use the --dry-run flag to validate your config:
cityjson-stac collection --config collections/your-file.yaml --dry-run
Expected output:
✓ Config file is valid YAML
✓ All required fields present
✓ URLs are well-formed
✓ License is valid SPDX identifier
✓ Provider information complete
If there are errors, fix them and re-run the validation.
Step 5: Test the URL (Optional but Recommended)
Verify the data URL is accessible:
# For direct downloads
curl -I https://example.com/data.city.json
# For API endpoints
curl https://api.example.com/endpoint
Step 6: Submit the File
Once validated:
- Place the YAML file in the
collections/ directory
- Ensure it follows naming conventions
- Submit all files for review
8. Common Scenarios
Scenario 1: Simple Direct Download
Use when: A single downloadable file (e.g., .city.json, .zip)
Example:
id: 3dbag-delft-2023
title: 3DBAG Delft
description: |
Complete 3D building model of Delft, Netherlands.
Contains all buildings with LoD2.2 geometry including
building parts and semantic surfaces.
license: CC-BY-4.0
keywords:
- 3d city model
- buildings
- netherlands
- delft
- lod2
providers:
- name: 3DBAG
url: https://3dbag.nl
roles:
- producer
- licensor
description: 3D Basisregistratie Adressen en Gebouwen
inputs:
- https://example.com/delft.city.json
Scenario 2: API Endpoint
Use when: Data is accessed via an API (not direct download)
Example:
id: citylab-karlsruhe-2022
title: CityLab Karlsruhe 3D City Model
description: |
3D city model of Karlsruhe accessible via API.
Includes buildings, vegetation, and transportation
features at multiple Levels of Detail.
license: CC-BY-4.0
keywords:
- 3d city model
- germany
- karlsruhe
- api
providers:
- name: CityLab Karlsruhe
url: https://citylab.example.de
roles:
- producer
- host
description: Municipal 3D geoinformation lab
inputs:
- https://api.example.com/v1/cityjson endpoint
# Documentation:
# API Docs: https://api.example.com/docs
# Example call: GET /v1/cityjson?bbox=8.3,48.9,8.5,49.1
# Returns: CityJSON FeatureCollection
# Authentication: None required
# Rate limit: 100 requests/minute
links:
- rel: about
href: https://api.example.com/docs
type: text/html
title: API Documentation
Scenario 3: Multiple Files
Use when: Dataset split across multiple files (districts, tiles, etc.)
Example:
id: opendata-zurich-2023
title: Open Data Zurich 3D
description: |
Complete 3D city model of Zurich organized by district.
Each district is provided as a separate CityJSON file.
Includes buildings (LoD2), vegetation, and transportation.
license: CC0-1.0
keywords:
- 3d city model
- switzerland
- zurich
- lod2
providers:
- name: City of Zurich
url: https://www.stadt-zuerich.ch
roles:
- producer
- licensor
description: City of Zurich open data portal
inputs:
- https://example.com/citydata/district-01.city.json
- https://example.com/citydata/district-02.city.json
- https://example.com/citydata/district-03.city.json
- https://example.com/citydata/district-04.city.json
- https://example.com/citydata/district-05.city.json
- https://example.com/citydata/district-06.city.json
- https://example.com/citydata/district-07.city.json
- https://example.com/citydata/district-08.city.json
- https://example.com/citydata/district-09.city.json
- https://example.com/citydata/district-10.city.json
- https://example.com/citydata/district-11.city.json
- https://example.com/citydata/district-12.city.json
Scenario 4: CityGML Format
Use when: Dataset is in CityGML format (not CityJSON)
Example:
id: opendata-nyc-2021
title: New York City 3D Building Model
description: |
3D building model of New York City in CityGML format.
Contains approximately 1 million building footprints
with height information and building attributes.
license: CC-BY-4.0
keywords:
- 3d city model
- citygml
- usa
- new york
- buildings
providers:
- name: NYC Planning
url: https://www1.nyc.gov/site/planning
roles:
- producer
- licensor
description: NYC Department of City Planning
inputs:
- https://example.com/newyork.gml.zip
# Additional Notes:
# - Format: CityGML 2.0
# - Compression: ZIP containing GML files
# - Projection: EPSG:2263 (NAD83 / New York Long Island)
# - Size: ~500 MB compressed
Scenario 5: Unknown/Missing Metadata
Use when: Some information is not available
Example:
id: unknown-montreal-2020
title: Montreal 3D City Model
description: |
3D city model of Montreal, Canada. Exact temporal coverage
and coordinate reference system not specified in source.
Includes buildings and transportation networks.
license: CC-BY-4.0
keywords:
- 3d city model
- canada
- montreal
providers:
- name: City of Montreal
url: https://ville.montreal.qc.ca
roles:
- producer
description: Municipal open data portal
inputs:
- https://data.example.com/montreal.city.json
# Note: CRS will be auto-detected from data during processing
# Note: Year estimated from metadata (actual year may vary)
9. Quality Checklist
Before submitting each config file, verify:
Content Quality
Data Access
Formatting
Validation
Validation Command
# Validate a single file
cityjson-stac collection --config collections/your-file.yaml --dry-run
# Validate all files at once (bash loop)
for file in collections/*.yaml; do
echo "Validating $file..."
cityjson-stac collection --config "$file" --dry-run
done
10. Naming Conventions
Config File Names
Format: <city-or-region>-<provider>.yaml
Rules:
- Use lowercase letters
- Separate words with hyphens (
-)
- Include geographic identifier (city or region)
- Include source/provider identifier
- Use
.yaml extension
Examples:
| Dataset |
File Name |
| Delft from 3DBAG |
delft-3dbag.yaml |
| Rotterdam from Open Data |
rotterdam-opendata.yaml |
| Singapore from URA |
singapore-ura.yaml |
| Zurich from Statistics Office |
zurich-statistics.yaml |
| New York City from Planning Dept |
nyc-planning.yaml |
Collection IDs
Format: <provider>-<city>-<year> (when year is known)
Rules:
- Use lowercase
- Include provider/source name
- Include city name
- Include year if known
- Use hyphens to separate parts
Examples:
| Dataset |
Collection ID |
| 3DBAG Delft 2023 |
3dbag-delft-2023 |
| Open Data Rotterdam 2022 |
opendata-rotterdam-2022 |
| URA Singapore 2024 |
ura-singapore-2024 |
| Unknown year |
provider-city (omit year) |
11. Submitting Your Work
Deliverables
Submit:
- Multiple YAML configuration files
- One file per dataset
- All files in
collections/ directory
- All files validated with
--dry-run
Optional but helpful:
- Summary document listing collected datasets
- Notes on any issues encountered
- Suggestions for additional sources
How to Submit
Option 1: Direct Hand-off
- Place files in
collections/ directory
- Notify that files are ready for review
- Wait for feedback
Option 2: Pull Request
- Create a new git branch
- Commit your config files
- Create a pull request
- Address review feedback
What Happens Next
- Review - Your configs will be reviewed for completeness and accuracy
- Testing - Each config will be tested to ensure it works with
cityjson-stac
- Integration - Valid configs will be added to the catalog
- Processing - STAC metadata will be generated from your configs
Definition of Done ✅
You have successfully completed this task when:
- YAML files created - Multiple config files in
collections/ directory
- URLs populated - As many actual data file URLs as possible are filled in
- Basic validation - Configs pass
--dry-run validation (exit code 0)
- Pull request opened - PR submitted to https://github.com/HideBa/city3d-stac
Repository Access:
- You'll be invited as a collaborator to the repository
- Create a new branch from
main: git checkout -b collect-datasets
- Commit your configs:
git add collections/ && git commit -m "feat: add dataset configs"
- Push and create PR:
git push origin collect-datasets
Acceptance Criteria:
- ✅ Config files follow the naming convention (Section 10)
- ✅ All required fields present (
id, title, description, license, providers, inputs)
- ✅ URLs point to actual data files (not just portal homepages)
- ✅
--dry-run validation passes for each config
- ✅ Pull request description summarizes the datasets added
Remember: Done is better than perfect! See Section 12.1 for guidance.
Example Pull Request Template
When you open your PR, use a template like this:
PR Title: feat: add 15 open 3D city model datasets
PR Description Body:
- Summary of datasets added (number, geographic distribution)
- Table listing each dataset with: city, country, provider, format, license, status
- Notes about any issues or uncertainties
- Confirmation that
--dry-run validation passed
Example Summary Table:
| City |
Country |
Provider |
Format |
License |
Status |
| Delft |
NL |
3DBAG |
CityJSON |
CC-BY-4.0 |
✅ Verified |
| Rotterdam |
NL |
Open Data |
CityJSON |
CC0-1.0 |
✅ Verified |
| Zurich |
CH |
City Zurich |
CityGML |
CC-BY-4.0 |
⚠️ URL untested |
---
## 12. Troubleshooting
### Common Issues
**Issue:** `--dry-run` validation fails
- **Solution:** Check YAML syntax, ensure all required fields are present
**Issue:** URL is not accessible
- **Solution:** Verify the URL is correct, check if it requires authentication
**Issue:** Can't find license information
- **Solution:** Use `proprietary` and document in comments that license is unclear
**Issue:** API endpoint requires authentication
- **Solution:** Document authentication requirements in comments, include API docs link
**Issue:** Multiple conflicting sources for same city
- **Solution:** Create separate configs for each, include version/year in description
### Getting Help
If you encounter issues not covered here:
1. Check the project README and documentation
2. Review example config files in `examples/`
3. Ask a team member for clarification
4. Document the issue for future reference
---
## 12.1 Working with Uncertainty
**⚠️ Important Principle:** Done is better than perfect.
### Don't Spend Too Much Time
- **5-minute rule:** If you can't find information within 5 minutes, move on
- **Good enough is acceptable:** Partial information is better than no config at all
- **Iterate later:** Configs can be updated and improved in future PRs
- **Focus on scale:** It's better to have 50 decent configs than 5 perfect ones
### Using Comments for Uncertain Information
YAML supports comments with `#`. Use them liberally to document uncertainty:
```yaml
id: montreal-3d-2020
title: Montreal 3D City Model
description: |
3D city model of Montreal, Canada.
NOTE: Exact coverage area unclear from source page.
license: CC-BY-4.0 # NOTE: License mentioned on page but not explicitly linked
providers:
- name: City of Montreal
url: https://ville.montreal.qc.ca
roles:
- producer
# NOTE: Contact information not available
inputs:
- https://example.com/montreal.city.json # NOTE: URL not tested, may require authentication
# Additional notes:
# - Year estimated from metadata (actual year may vary)
# - CRS not specified in source documentation
# - File size unknown
# - Portal may require registration for download
Common Uncertainty Scenarios
| Scenario |
What to Do |
| License unclear |
Use proprietary + comment: # License not specified, assumed restrictive |
| Year unknown |
Estimate from metadata + comment: # Year estimated from page copyright |
| URL not accessible |
Include URL anyway + comment: # NOTE: URL not verified, may be broken |
| CRS not specified |
Leave blank + comment: # CRS will be auto-detected from data |
| Provider contact missing |
Omit + comment: # Contact information not available |
| Multiple format versions available |
Choose one + comment: # NOTE: Other formats available on source page |
| API authentication unclear |
Document what you found + comment: # NOTE: Auth requirements unclear |
Comment Guidelines
DO use comments for:
- Information you're uncertain about
- Assumptions you're making
- Things you couldn't verify
- Alternative URLs or formats you found
- Notes about data quality or completeness
- Authentication or access requirements
DON'T stress about:
- Finding every single data URL for multi-file datasets (get the main ones)
- Verifying every URL works (sample check is fine)
- Perfect descriptions (good enough is okay)
- Complete metadata (fill what you can, comment the rest)
- Formatting inconsistencies (focus on content)
Example: Good Enough Config
id: rotterdam-opendata-2022
title: Rotterdam 3D City Model
description: |
3D city model of Rotterdam, Netherlands.
NOTE: Description translated from Dutch, may not be exact.
Contains buildings and terrain data.
license: CC-BY-4.0 # NOTE: Confirming exact license version
providers:
- name: City of Rotterdam
url: https://www.rotterdam.nl
roles:
- producer
# NOTE: Additional providers may exist
inputs:
- https://example.com/rotterdam.city.json # Main dataset
# NOTE: Additional district files available but not collected
# TODO:
# - Verify license with official source
# - Add district file URLs if needed
# - Confirm data year (metadata unclear)
Review Process
During review, unclear information will be:
- Identified - Reviewers will check comments and notes
- Clarified - Team will investigate uncertain items
- Resolved - Configs will be updated or marked for future improvement
Your comments help reviewers understand:
- What you tried to find but couldn't
- What assumptions you made
- What needs verification
Remember: A config with notes about uncertainty is more valuable than no config at all!
13. Quick Reference
Essential Fields
id: unique-id
title: Dataset Title
description: Detailed description
license: CC-BY-4.0
providers:
- name: Organization
roles: [producer]
inputs:
- https://example.com/data.json
Common Licenses
CC-BY-4.0 - Creative Commons Attribution
CC0-1.0 - Public Domain
ODC-BY-1.0 - Open Data Commons
proprietary - Unknown/restricted
Provider Roles
producer - Created the data
licensor - Manages the license
processor - Transformed the data
host - Hosts the data
Validation Command
cityjson-stac collection --config collections/file.yaml --dry-run
Appendix A: Field Types Reference
Strings
Simple text values:
title: Delft 3D City Model
license: CC-BY-4.0
Multiline Strings
Use | for literal multiline:
description: |
This is a long description
that spans multiple lines.
Formatting is preserved.
Lists
Use - for list items:
keywords:
- 3d city model
- buildings
- netherlands
Nested Objects
Indent with 2 spaces:
providers:
- name: Organization
url: https://example.com
roles:
- producer
- licensor
Comments
Use # for comments:
inputs:
- https://example.com/data.json # Main dataset
# API endpoint: https://api.example.com/v1/cityjson
Appendix B: Resources
STAC Specification
CityJSON / CityGML
Licenses
Coordinate Reference Systems
Good luck with your data collection! 🏙️
If you have questions, don't hesitate to ask. Your contribution will help make 3D city data more accessible to everyone.
Task: Collect Open 3D City Model Datasets
Objective: Create configuration files for open 3D city model datasets to build a comprehensive global STAC catalog.
Deliverable: Multiple YAML configuration files (one per dataset) in the
collections/directory.Target Deadline: 📅 March 6th, 2026 (negotiable - see Section 2.1)
Table of Contents
1. Background
What is STAC?
STAC (SpatioTemporal Asset Catalog) is a specification for describing geospatial data. Think of it as a standardized way to catalog and search for spatial datasets.
What are we building?
We're creating a global catalog of open 3D city models. These are digital representations of cities in 3D, including buildings, roads, vegetation, and more. Common formats include:
Why this matters
Currently, finding open 3D city model datasets is difficult - they're scattered across various websites, portals, and repositories. By creating a standardized STAC catalog, we make it easy for:
Your role
You'll be finding open 3D city model datasets and creating configuration files that describe each dataset. These configs will be used to automatically generate STAC metadata.
2. Prerequisites
What you need
Helpful but not required
Setup
collections/directory if it doesn't exist:2.1 Timeline & Deadline
Target Deadline: March 6th, 2026 📅
This is a soft target - the deadline is negotiable based on what you discover.
Suggested Timeline
Scope is Flexible
The exact number of datasets is not fixed. What matters more:
💡 Work Smart: Use AI Tools!
This is not an assignment or exam - it's a real task where the goal is to get it done efficiently. You're encouraged to:
✅ Use AI assistants (ChatGPT, Claude, etc.) to help with:
✅ Leverage existing tools:
✅ Focus on outcomes, not process:
Example AI Prompts:
Bottom line: If AI can help you finish this task faster and easier, use it. We care about the catalog, not how you build it.
Negotiable Aspects
If you're running into challenges, we can adjust:
proprietary+ comments, move forwardCommunication
If you anticipate missing the deadline:
Remember: This task is about building a useful catalog, not hitting an arbitrary number. A PR with 30 well-documented datasets opened on March 8th is better than 50 rushed configs opened on March 6th.
2.2 Head Start: Existing Draft Configs 🚀
Good news! Draft configuration files already exist in the
opendata/directory that you can build upon.What's Already There
The
opendata/directory contains 27 draft collection configs covering datasets from:netherlands-3d-bag,rotterdam,the-hagueberlin,dresden,hamburg,potsdam,ingolstadt,north-rhine-westphaliahelsinki,espoo,vantaavienna,linz,luxembourglyonbrussels,namurnew-york-doitt,new-york-tum,montreal,american-citiessingapore,japan-plateauestonia,various-cityjsonPlus:
catalog-config.yamlfor aggregating all collectionsYour Task: Verify & Complete
Instead of starting from scratch, your job is to:
opendata/Quick Start with Existing Configs
What to Check in Each Config
For each existing config, verify:
inputsURLslicenseprovidersdescriptionextentkeywordsCommon Issues to Fix
Look for and correct:
proprietarywhen actual license is knownExample: Review Process
Bonus: Extend the Collection
Once you've verified the existing configs, add more datasets from:
Summary
You're not starting from zero! You have:
Your job is quality assurance + expansion, not blank-slate creation.
3. Understanding the Config File
Each dataset needs a YAML configuration file. Here's the complete structure:
Required Fields
Every config file MUST have:
Complete Field Reference
id3dbag-delft-2023title3DBAG Delftdescription|for multiline)licenseCC-BY-4.0keywords["3d city model", "buildings"]providersinputsextent.spatialextent.temporallinkssummariesProvider Roles
Each provider should have at least one role:
producerlicensorprocessorhostcuratorCommon License Identifiers
CC-BY-4.0CC0-1.0ODC-BY-1.0MITproprietaryFind more: https://spdx.org/licenses/
5. Data Sources
Primary Sources to Explore
1. Awesome CityGML (GitHub)
🔗 https://github.com/OloOcki/awesome-citygml#World
A curated list of open 3D city model datasets organized by country. This is your main source.
How to use:
2. TU Delft Open Cities
🔗 https://3d.bk.tudelft.nl/opendata/opencities/
Academic repository of open 3D city models maintained by TU Delft.
How to use:
Additional Discovery
Look for datasets in:
What to Include
✅ Include datasets that are:
❌ Don't include:
5.1 Programmatic Data Collection
Why Automate?
Recommended Approaches
1. Web Scraping
For datasets with predictable page structures:
Tools:
requests,beautifulsoup4,scrapyplaywright,puppeteer2. API Queries
For portals with APIs:
3. GitHub Scraping
For GitHub-hosted datasets:
GitHub Search Query:
4. Directory Listing Parsing
For web-accessible directories:
Automation Workflow
Discovery Script
python scripts/discover_datasets.py > sources/raw_urls.txtMetadata Extraction
Config Generation
Validation
Example: End-to-End Script
Best Practices
Tools & Libraries
requests,httpxaxios,node-fetchbeautifulsoup4,lxmlcheerio,jsdomscrapypuppeteer,playwrightpyyamljs-yamlPyGithub@octokit/rest7. Step-by-Step Workflow
Follow these steps for each dataset you discover:
Step 1: Identify the Dataset
Step 2: Extract Metadata
Collect the following information from the dataset page:
Basic Information:
Provider Information:
Data Access:
Spatial/Temporal Coverage:
Step 3: Create the Config File
collections/directoryStep 4: Validate the Config
Use the
--dry-runflag to validate your config:Expected output:
If there are errors, fix them and re-run the validation.
Step 5: Test the URL (Optional but Recommended)
Verify the data URL is accessible:
Step 6: Submit the File
Once validated:
collections/directory8. Common Scenarios
Scenario 1: Simple Direct Download
Use when: A single downloadable file (e.g.,
.city.json,.zip)Example:
Scenario 2: API Endpoint
Use when: Data is accessed via an API (not direct download)
Example:
Scenario 3: Multiple Files
Use when: Dataset split across multiple files (districts, tiles, etc.)
Example:
Scenario 4: CityGML Format
Use when: Dataset is in CityGML format (not CityJSON)
Example:
Scenario 5: Unknown/Missing Metadata
Use when: Some information is not available
Example:
9. Quality Checklist
Before submitting each config file, verify:
Content Quality
id,title,description,license,providers,inputs)Data Access
Formatting
-for items)\|or>correctlyValidation
--dry-runvalidationValidation Command
10. Naming Conventions
Config File Names
Format:
<city-or-region>-<provider>.yamlRules:
-).yamlextensionExamples:
delft-3dbag.yamlrotterdam-opendata.yamlsingapore-ura.yamlzurich-statistics.yamlnyc-planning.yamlCollection IDs
Format:
<provider>-<city>-<year>(when year is known)Rules:
Examples:
3dbag-delft-2023opendata-rotterdam-2022ura-singapore-2024provider-city(omit year)11. Submitting Your Work
Deliverables
Submit:
collections/directory--dry-runOptional but helpful:
How to Submit
Option 1: Direct Hand-off
collections/directoryOption 2: Pull Request
What Happens Next
cityjson-stacDefinition of Done ✅
You have successfully completed this task when:
collections/directory--dry-runvalidation (exit code 0)Repository Access:
main:git checkout -b collect-datasetsgit add collections/ && git commit -m "feat: add dataset configs"git push origin collect-datasetsAcceptance Criteria:
id,title,description,license,providers,inputs)--dry-runvalidation passes for each configRemember: Done is better than perfect! See Section 12.1 for guidance.
Example Pull Request Template
When you open your PR, use a template like this:
PR Title:
feat: add 15 open 3D city model datasetsPR Description Body:
--dry-runvalidation passedExample Summary Table:
--dry-runvalidation passesCommon Uncertainty Scenarios
proprietary+ comment:# License not specified, assumed restrictive# Year estimated from page copyright# NOTE: URL not verified, may be broken# CRS will be auto-detected from data# Contact information not available# NOTE: Other formats available on source page# NOTE: Auth requirements unclearComment Guidelines
DO use comments for:
DON'T stress about:
Example: Good Enough Config
Review Process
During review, unclear information will be:
Your comments help reviewers understand:
Remember: A config with notes about uncertainty is more valuable than no config at all!
13. Quick Reference
Essential Fields
Common Licenses
CC-BY-4.0- Creative Commons AttributionCC0-1.0- Public DomainODC-BY-1.0- Open Data Commonsproprietary- Unknown/restrictedProvider Roles
producer- Created the datalicensor- Manages the licenseprocessor- Transformed the datahost- Hosts the dataValidation Command
Appendix A: Field Types Reference
Strings
Simple text values:
Multiline Strings
Use
|for literal multiline:Lists
Use
-for list items:Nested Objects
Indent with 2 spaces:
Comments
Use
#for comments:Appendix B: Resources
STAC Specification
CityJSON / CityGML
Licenses
Coordinate Reference Systems
Good luck with your data collection! 🏙️
If you have questions, don't hesitate to ask. Your contribution will help make 3D city data more accessible to everyone.