GCS Data Catalog - Master Index
This skill provides immediate access to Landbruget.dk's GCS data lake containing 18+ Danish agricultural datasets.
Quick Access
GCS Bucket: Set via GCS_BUCKET environment variable (see .env)
Medallion Architecture:
bronze/ - Raw data exactly as received
silver/ - Cleaned, validated, standardized
gold/ - Analysis-ready, joined datasets
Setup Code
import os
import pyarrow.parquet as pq
from google.cloud import storage
# Initialize GCS client
client = storage.Client()
bucket_name = os.environ.get('GCS_BUCKET') # Set in .env
bucket = client.bucket(bucket_name)
# Read parquet from GCS
def read_gcs_parquet(gcs_path: str):
"""Read parquet file from GCS path like 'silver/subsidies/*/data.parquet'"""
import io
blob = bucket.blob(gcs_path)
buffer = io.BytesIO()
blob.download_to_file(buffer)
buffer.seek(0)
return pq.read_table(buffer).to_pandas()
Data Categories (Frontend-Aligned)
| Category |
Danish Name |
Skill Path |
Key Join |
Metrics |
| Finance |
Økonomi |
gcs-data-catalog/okonomi/ |
cvr_number |
3 |
| Agricultural Land |
Landbrugsareal |
gcs-data-catalog/landbrugsareal/ |
field_id, cvr_number |
4 |
| Environment |
Miljø |
gcs-data-catalog/miljo/ |
geometry, field_id |
8 |
| Livestock |
Husdyr |
gcs-data-catalog/husdyr/ |
chr_number |
6 |
| Employees |
Medarbejdere |
gcs-data-catalog/medarbejdere/ |
cvr_number |
5 |
Key Identifiers
| Identifier |
Format |
Description |
Validation |
| CVR |
8 digits |
Company registration number |
^\d{8}$ |
| CHR |
6 digits |
Central Husbandry Register (herd ID) |
^\d{6}$ |
| BFE |
Variable |
Cadastral parcel number |
varies |
| field_id |
String |
Field identifier from FVM |
varies |
| field_uuid |
UUID |
Unique field identifier |
UUID format |
Dataset Quick Reference
Økonomi (Finance)
| Dataset |
Path |
Rows |
Key Columns |
| Subsidies |
silver/subsidies/ |
554K |
cvr_number, tilskudsberetigt |
| CVR Enrichment |
gold/cvr_enrichment/*/ |
varies |
cvr_number, company data |
| Property Owners |
silver/property_owners/ |
8.2M |
CVRNummer, owner info |
Landbrugsareal (Agricultural Land)
| Dataset |
Path |
Rows |
Key Columns |
| FVM Marker (fields) |
silver/fvm_marker_{year}/ |
617K/year |
field_id, cvr_number, crop_code, geometry |
| Field Production |
gold/field_production_{year}/ |
617K/year |
field_id, yield_estimate, crop_type |
| Agricultural Blocks |
silver/agricultural_blocks_{year}/ |
varies |
block_id, geometry |
| Cadastral |
silver/cadastral/ |
2.16M |
bfe_number, geometry |
Miljø (Environment)
| Dataset |
Path |
Rows |
Key Columns |
| Pesticide Disaggregation |
gold/pesticide_disaggregation_{year}/ |
1.52M |
cvr_number, PesticideName, DosageQuantity |
| NLES5 Nitrogen |
gold/nles5_nitrogen_*/ |
500K |
field_id, nitrogen_washout_kg_ha |
| BNBO Status |
silver/bnbo_status/ |
5.4K |
geometry, status_bnbo |
| Wetlands |
silver/wetlands/ |
1.7M |
geometry, toerv_pct |
Husdyr (Livestock)
| Dataset |
Path |
Rows |
Key Columns |
| Svineflytning |
silver/svineflytning/*/movements.parquet |
1.27M |
sender_chr_number, receiver_chr_number, total_animals |
| CHR Movements |
bronze/chr/*/chr_dyr_movement_summaries.parquet |
124K |
reporting_herd_number, animal_count |
| Animal Welfare |
silver/animal welfare/ |
varies |
chr_number |
Medarbejdere (Employees)
| Dataset |
Path |
Rows |
Key Columns |
| Arbejdstilsynet |
gold/arbejdstilsynet_inspections/ |
536 |
cvr_number, decision, severity_score |
| Work Permits |
silver/work permits/ |
varies |
cvr_number |
| Worker Safety |
silver/worker safety/ |
varies |
cvr_number |
Common Queries
List Available Years for a Dataset
gsutil ls gs://$GCS_BUCKET/silver/fvm_marker_*/
Check Dataset Schema
import os
import pyarrow.parquet as pq
from google.cloud import storage
import io
client = storage.Client()
bucket_name = os.environ.get('GCS_BUCKET')
bucket = client.bucket(bucket_name)
# Get first parquet file and read schema
blob = bucket.blob('silver/subsidies/2025-01-10T00:00:26.377177/data.parquet')
buffer = io.BytesIO()
blob.download_to_file(buffer)
buffer.seek(0)
schema = pq.read_schema(buffer)
print(schema)
Query Specific CVR
df = read_gcs_parquet('silver/subsidies/2025-01-10T00:00:26.377177/data.parquet')
company_data = df[df['cvr_number'] == '31373077']
Cross-Dataset Joins
CVR-based joins (most common)
# Join subsidies with pesticides on CVR
subsidies = read_gcs_parquet('silver/subsidies/*/data.parquet')
pesticides = read_gcs_parquet('gold/pesticide_disaggregation_2024/*/data.parquet')
merged = subsidies.merge(pesticides, on='cvr_number', how='inner')
Field-based joins
# Join field production with nitrogen estimates
field_prod = read_gcs_parquet('gold/field_production_2024/*/data.parquet')
nitrogen = read_gcs_parquet('gold/nles5_nitrogen_2024/*/data.parquet')
merged = field_prod.merge(nitrogen, on=['field_id', 'cvr_number'], how='inner')
CHR-based joins
# Join movements with animal welfare
movements = read_gcs_parquet('silver/svineflytning/*/movements.parquet')
welfare = read_gcs_parquet('silver/animal welfare/*/data.parquet')
# Join on sender or receiver CHR
Data Update Schedule
| Layer |
Frequency |
Notes |
| Bronze |
Weekly (Mondays 2AM UTC) |
Immutable, timestamped |
| Silver |
After bronze update |
Cleaned, validated |
| Gold |
After silver update |
Analysis-ready |
Related Skills
- okonomi/ - Financial data: subsidies, property values
- landbrugsareal/ - Field and crop data: FVM marker, production
- miljo/ - Environmental data: pesticides, nitrogen, BNBO
- husdyr/ - Livestock data: CHR, movements, welfare
- medarbejdere/ - Employee data: inspections, safety
Troubleshooting
Authentication
# Check GCS access
gcloud auth application-default login
gsutil ls gs://$GCS_BUCKET/
Large Files
For datasets > 1GB, use DuckDB or chunked reading:
import duckdb
# Query directly without loading into memory
result = duckdb.query("""
SELECT cvr_number, SUM(area_ha) as total_area
FROM 'gs://$GCS_BUCKET/gold/field_production_2024/*/data.parquet'
GROUP BY cvr_number
""").df()
CRS Conversion
All geometry is stored in EPSG:4326 (WGS84). For Danish coordinates (EPSG:25832):
import geopandas as gpd
gdf = gdf.to_crs('EPSG:25832') # Convert to UTM 32N
1---2name: gcs-data-catalog3description: Activates when querying Danish agricultural data from GCS. Use this skill for: data discovery, finding datasets, understanding schemas, querying parquet files, joining datasets on CVR/CHR/BFE identifiers. Keywords: data, catalog, datasets, GCS, parquet, schema, query, DuckDB, pyarrow4---5
6# GCS Data Catalog - Master Index
7
8This skill provides immediate access to Landbruget.dk's GCS data lake containing 18+ Danish agricultural datasets.
9
10## Quick Access
11
12**GCS Bucket**: Set via `GCS_BUCKET` environment variable (see `.env`)
13
14**Medallion Architecture**:
15- `bronze/` - Raw data exactly as received
16- `silver/` - Cleaned, validated, standardized
17- `gold/` - Analysis-ready, joined datasets
18
19## Setup Code
20
21```python
22import os
23import pyarrow.parquet as pq
24from google.cloud import storage
25
26# Initialize GCS client
27client = storage.Client()
28bucket_name = os.environ.get('GCS_BUCKET') # Set in .env
29bucket = client.bucket(bucket_name)
30
31# Read parquet from GCS
32def read_gcs_parquet(gcs_path: str):
33 """Read parquet file from GCS path like 'silver/subsidies/*/data.parquet'"""
34 import io
35 blob = bucket.blob(gcs_path)
36 buffer = io.BytesIO()
37 blob.download_to_file(buffer)
38 buffer.seek(0)
39 return pq.read_table(buffer).to_pandas()
40```
41
42## Data Categories (Frontend-Aligned)
43
44| Category | Danish Name | Skill Path | Key Join | Metrics |
45|----------|-------------|------------|----------|---------|
46| Finance | Økonomi | `gcs-data-catalog/okonomi/` | cvr_number | 3 |
47| Agricultural Land | Landbrugsareal | `gcs-data-catalog/landbrugsareal/` | field_id, cvr_number | 4 |
48| Environment | Miljø | `gcs-data-catalog/miljo/` | geometry, field_id | 8 |
49| Livestock | Husdyr | `gcs-data-catalog/husdyr/` | chr_number | 6 |
50| Employees | Medarbejdere | `gcs-data-catalog/medarbejdere/` | cvr_number | 5 |
51
52## Key Identifiers
53
54| Identifier | Format | Description | Validation |
55|------------|--------|-------------|------------|
56| **CVR** | 8 digits | Company registration number | `^\d{8}$` |
57| **CHR** | 6 digits | Central Husbandry Register (herd ID) | `^\d{6}$` |
58| **BFE** | Variable | Cadastral parcel number | varies |
59| **field_id** | String | Field identifier from FVM | varies |
60| **field_uuid** | UUID | Unique field identifier | UUID format |
61
62## Dataset Quick Reference
63
64### Økonomi (Finance)
65| Dataset | Path | Rows | Key Columns |
66|---------|------|------|-------------|
67| Subsidies | `silver/subsidies/` | 554K | cvr_number, tilskudsberetigt |
68| CVR Enrichment | `gold/cvr_enrichment/*/` | varies | cvr_number, company data |
69| Property Owners | `silver/property_owners/` | 8.2M | CVRNummer, owner info |
70
71### Landbrugsareal (Agricultural Land)
72| Dataset | Path | Rows | Key Columns |
73|---------|------|------|-------------|
74| FVM Marker (fields) | `silver/fvm_marker_{year}/` | 617K/year | field_id, cvr_number, crop_code, geometry |
75| Field Production | `gold/field_production_{year}/` | 617K/year | field_id, yield_estimate, crop_type |
76| Agricultural Blocks | `silver/agricultural_blocks_{year}/` | varies | block_id, geometry |
77| Cadastral | `silver/cadastral/` | 2.16M | bfe_number, geometry |
78
79### Miljø (Environment)
80| Dataset | Path | Rows | Key Columns |
81|---------|------|------|-------------|
82| Pesticide Disaggregation | `gold/pesticide_disaggregation_{year}/` | 1.52M | cvr_number, PesticideName, DosageQuantity |
83| NLES5 Nitrogen | `gold/nles5_nitrogen_*/` | 500K | field_id, nitrogen_washout_kg_ha |
84| BNBO Status | `silver/bnbo_status/` | 5.4K | geometry, status_bnbo |
85| Wetlands | `silver/wetlands/` | 1.7M | geometry, toerv_pct |
86
87### Husdyr (Livestock)
88| Dataset | Path | Rows | Key Columns |
89|---------|------|------|-------------|
90| Svineflytning | `silver/svineflytning/*/movements.parquet` | 1.27M | sender_chr_number, receiver_chr_number, total_animals |
91| CHR Movements | `bronze/chr/*/chr_dyr_movement_summaries.parquet` | 124K | reporting_herd_number, animal_count |
92| Animal Welfare | `silver/animal welfare/` | varies | chr_number |
93
94### Medarbejdere (Employees)
95| Dataset | Path | Rows | Key Columns |
96|---------|------|------|-------------|
97| Arbejdstilsynet | `gold/arbejdstilsynet_inspections/` | 536 | cvr_number, decision, severity_score |
98| Work Permits | `silver/work permits/` | varies | cvr_number |
99| Worker Safety | `silver/worker safety/` | varies | cvr_number |
100
101## Common Queries
102
103### List Available Years for a Dataset
104```bash
105gsutil ls gs://$GCS_BUCKET/silver/fvm_marker_*/
106```
107
108### Check Dataset Schema
109```python
110import os
111import pyarrow.parquet as pq
112from google.cloud import storage
113import io
114
115client = storage.Client()
116bucket_name = os.environ.get('GCS_BUCKET')
117bucket = client.bucket(bucket_name)
118
119# Get first parquet file and read schema
120blob = bucket.blob('silver/subsidies/2025-01-10T00:00:26.377177/data.parquet')
121buffer = io.BytesIO()
122blob.download_to_file(buffer)
123buffer.seek(0)
124schema = pq.read_schema(buffer)
125print(schema)
126```
127
128### Query Specific CVR
129```python
130df = read_gcs_parquet('silver/subsidies/2025-01-10T00:00:26.377177/data.parquet')
131company_data = df[df['cvr_number'] == '31373077']
132```
133
134## Cross-Dataset Joins
135
136### CVR-based joins (most common)
137```python
138# Join subsidies with pesticides on CVR
139subsidies = read_gcs_parquet('silver/subsidies/*/data.parquet')
140pesticides = read_gcs_parquet('gold/pesticide_disaggregation_2024/*/data.parquet')
141merged = subsidies.merge(pesticides, on='cvr_number', how='inner')
142```
143
144### Field-based joins
145```python
146# Join field production with nitrogen estimates
147field_prod = read_gcs_parquet('gold/field_production_2024/*/data.parquet')
148nitrogen = read_gcs_parquet('gold/nles5_nitrogen_2024/*/data.parquet')
149merged = field_prod.merge(nitrogen, on=['field_id', 'cvr_number'], how='inner')
150```
151
152### CHR-based joins
153```python
154# Join movements with animal welfare
155movements = read_gcs_parquet('silver/svineflytning/*/movements.parquet')
156welfare = read_gcs_parquet('silver/animal welfare/*/data.parquet')
157# Join on sender or receiver CHR
158```
159
160## Data Update Schedule
161
162| Layer | Frequency | Notes |
163|-------|-----------|-------|
164| Bronze | Weekly (Mondays 2AM UTC) | Immutable, timestamped |
165| Silver | After bronze update | Cleaned, validated |
166| Gold | After silver update | Analysis-ready |
167
168## Related Skills
169
170- **okonomi/** - Financial data: subsidies, property values
171- **landbrugsareal/** - Field and crop data: FVM marker, production
172- **miljo/** - Environmental data: pesticides, nitrogen, BNBO
173- **husdyr/** - Livestock data: CHR, movements, welfare
174- **medarbejdere/** - Employee data: inspections, safety
175
176## Troubleshooting
177
178### Authentication
179```bash
180# Check GCS access
181gcloud auth application-default login
182gsutil ls gs://$GCS_BUCKET/
183```
184
185### Large Files
186For datasets > 1GB, use DuckDB or chunked reading:
187```python
188import duckdb
189# Query directly without loading into memory
190result = duckdb.query("""
191 SELECT cvr_number, SUM(area_ha) as total_area
192 FROM 'gs://$GCS_BUCKET/gold/field_production_2024/*/data.parquet'
193 GROUP BY cvr_number
194""").df()
195```
196
197### CRS Conversion
198All geometry is stored in EPSG:4326 (WGS84). For Danish coordinates (EPSG:25832):
199```python
200import geopandas as gpd
201gdf = gdf.to_crs('EPSG:25832') # Convert to UTM 32N
202```