Add a new static dataset to app/scripts/StaticDatasets/geojson. Infers config from prompt, asks for missing data, uses types to guide required fields.
Adds a new static dataset folder to tilda-static-data and uses the tilda-geo upload pipeline to process it. In tilda-geo, app/scripts/StaticDatasets/geojson is a symlink and must point at the matching tilda-static-data worktree before editing data files.
Extract from user prompt or ask if missing:
region-berlin, region-bb) - parent folder nameberlin-bezirke) - dataset folder name (must be valid slug). Must include region/group prefix: for region-berlin use berlin-<name>, not just <name>['infravelo'], ['berlin'])tilda-static-data worktree and symlinkStatic dataset files live in the sibling repo tilda-static-data, not in tilda-geo.
../../../../tilda-static-data/geojson is enough.tilda-static-data worktree and relink geojson there.For a branch named my-branch:
cd ../tilda-static-data
git worktree add ../tilda-static-data--my-branch my-branch
cd ../tilda-geo--my-branch/app
rm -f scripts/StaticDatasets/geojson
ln -s ../../../../tilda-static-data--my-branch/geojson scripts/StaticDatasets/geojson
Do not edit app/scripts/StaticDatasets/geojson when it points at the regular shared ../tilda-static-data checkout.
app/scripts/StaticDatasets/geojson/<GROUP_FOLDER>/<SUB_FOLDER>/
Important: /geojson is a symlink. Before creating folders, ensure it points at the matching tilda-static-data--my-branch worktree.
Create directory if group folder doesn't exist. Ensure sub-folder name follows naming convention (see Required Information above).
Run these in order on committed data files. Never reproject, round, or pretty-print coordinates in transform.ts β use ogr2ogr (2.2β2.3) and oxfmt (2.4) on the files instead. Host GDAL 3.8+ (brew install gdal); not required for bun run dev.
Move source file to:
app/scripts/StaticDatasets/geojson/<GROUP_FOLDER>/<SUB_FOLDER>/<FILENAME>.geojson
This path should resolve into ../tilda-static-data--my-branch/geojson/....
Check: Inspect the first coordinate pair after moving. For Germany, expect roughly lon 5β15, lat 47β55. Web Mercator (EPSG:3857) uses large meter values (e.g. ~1.5e6).
Fix (only if not WGS84): Reproject in place:
cd app/scripts/StaticDatasets/geojson/<GROUP_FOLDER>/<SUB_FOLDER>
ogr2ogr -f GeoJSON -t_srs EPSG:4326 -lco COORDINATE_PRECISION=8 \
<FILENAME>.geojson.tmp <FILENAME>.geojson \
&& mv <FILENAME>.geojson.tmp <FILENAME>.geojson
Check: Coordinates should have at most 8 decimal places (matches upload pipeline and API exports).
Fix (only if more than 8): Round in place (file must already be EPSG:4326):
cd app/scripts/StaticDatasets/geojson/<GROUP_FOLDER>/<SUB_FOLDER>
ogr2ogr -f GeoJSON -lco COORDINATE_PRECISION=8 \
<FILENAME>.geojson.tmp <FILENAME>.geojson \
&& mv <FILENAME>.geojson.tmp <FILENAME>.geojson
Upload runs validateProjection again on WGS84 bounds.
From app/ (skill / agents only β devs use format-on-save in the editor):
bun run format-static-datasets-geojson -- \
scripts/StaticDatasets/geojson/<GROUP_FOLDER>/<SUB_FOLDER>/*.{json,geojson}
Check: Uncompressed .geojson size after formatting.
cd app/scripts/StaticDatasets/geojson/<GROUP_FOLDER>/<SUB_FOLDER>
gzip -9 -f <FILENAME>.geojson
gzip replaces the file with <FILENAME>.geojson.gz and removes the plain .geojson (-9 max compression, -f overwrite).
Only if transformation required (not for CRS or coordinate precision β Step 2 only). Use helpers from app/scripts/StaticDatasets/geojson/_utils:
transformUtils.ts - property transformationsdefaultLayerStyles.ts - default styling helperstranslateUtils.ts - translation helpersExample:
import { FeatureCollection } from 'geojson'
export const transform = (data: FeatureCollection) => {
// Use helper functions from _utils when possible
return data
}
Critical: Research similar datasets in same group folder first.
Before writing meta.ts:
<GROUP_FOLDER>/ directorymeta.ts files (similar geometry type, similar purpose)Required fields (from app/scripts/StaticDatasets/types.ts):
regions: RegionSlug[] (required)public: boolean (required)dataSourceType: 'local' (required)attributionHtml: string (required)configs: Array with at least one config (required)Optional on export const data:
dataUpdatedNote: stringdataSourceMarkdown: stringlicence: License type (see types.ts)licenceOsmCompatible: 'licence' | 'waiver' | 'no'Config required fields:
name: stringinspector: { enabled: boolean, ... } or { enabled: false }layers: Layer[] (required)Config optional fields:
category: string | null β grouping key; titles/order are managed in Admin β Statische Datensatz-Kategorien (DB).description: stringlegends: Legend[]Style/Legend: If not specified, make best guess:
defaultLayerStyles() from _utils/defaultLayerStyles.ts for simple casesStripe / Schraffur (fill-pattern): For diagonal stripes over polygons, reuse the existing map sprite image id stripe_texture (same hatch as private ParkflΓ€chen in generated parking_areas; listed in app/public/map-style/sprite.json). Do not add a new sprite. Stack a second fill layer above the solid fill: fill-color 'rgba(0, 0, 0, 0)', fill-opacity around 0.5 (tune as needed), fill-pattern 'stripe_texture'. Skip fill-outline-color on the stripe layer if the base fill already defines an outline. Use a filter on the stripe layer when only some features should be hatched; if exports mix boolean true and string 'true', either use an any / match filter in meta.ts or normalize values in transform.ts (step 3) so the filter stays simple.
Example structure:
import { MetaData } from '../../../types'
import { defaultLayerStyles } from '../../_utils/defaultLayerStyles'
export const data: MetaData = {
regions: ['infravelo'], // From user or infer from group folder
public: true,
dataSourceType: 'local',
attributionHtml: 'Source Name', // Ask if unclear
licence: 'DL-DE/ZERO-2.0', // Infer from similar datasets
configs: [
{
name: 'Dataset Name',
categoryKey: 'berlin/misc', // Infer from similar datasets
inspector: { enabled: false }, // Default unless specified
layers: defaultLayerStyles(), // Or custom layers
},
],
}
Check app/scripts/StaticDatasets/updateStaticDatasets.ts for correct params:
--folder-filter=<SUB_FOLDER_NAME> (matches full path, so sub-folder name works)--env=dev (or staging/production; omit to be prompted)One-click command from app/ (replace <SUB_FOLDER_NAME> with actual folder name; static-datasets-update uses the repository root .env via bun --env-file=../.env, same as bun run dev):
bun run static-datasets-update -- --folder-filter=<SUB_FOLDER_NAME> --env=dev
Note: updateDownloadSources.ts is for WFS downloads (requires downloadConfig.ts). Use updateStaticDatasets.ts for local GeoJSON files.
Always run this after creating or modifying TypeScript files (meta.ts, transform.ts, or any imports). From app/ (same as Step 5):
bun run type-check-deploy
This temporarily removes the geojson symlink, runs TypeScript type-checking, and restores the symlink. It simulates the Docker build environment where symlinks aren't available, ensuring your code will compile correctly during builds.
Before completing:
transform.ts only if needed β never for CRS (Step 2); meta.ts / transform.ts via format-on-save or format-static-datasets-geojsonbun run type-check-deploy run successfully (see Step 6)app/scripts/StaticDatasets/types.tsapp/scripts/StaticDatasets/geojson/region-berlin/*/meta.tsapp/scripts/StaticDatasets/geojson/_utils/docs/Features-Parameter-Deeplinks.mdapp/scripts/StaticDatasets/updateStaticDatasets.ts