Skip to content

Database builder - #217

Open
SimonSadler wants to merge 9 commits into
mainfrom
build_database
Open

Database builder#217
SimonSadler wants to merge 9 commits into
mainfrom
build_database

Conversation

@SimonSadler

Copy link
Copy Markdown

A CLI tool to import TCTrack NetCDF files into SQLite databases.

The resulting database forms the foundation for a dashboard. However, it is also valuable as a organisational tool for TCTrack output files. The track GeoJSON conversion is useful for visualising in general GIS tools.

Variable aliases to be supported in future.

Tool is pip-installed as build-db.

See README for full details.

@SimonSadler
SimonSadler requested a review from sjavis July 1, 2026 13:57

@sjavis sjavis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good and the tests seem pretty comprehensive. I have just a few suggested changes mostly for compatibility with outputs from different tracking algorithms.

Comment thread src/tctrack/build_db/build_db.py
Comment thread src/tctrack/build_db/build_db.py
Comment on lines +77 to +80
air_pressure_at_sea_level real,
surface_altitude real,
wind_speed real,
atmosphere_relative_vorticity real

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally the database would only include the columns for the variables that are actually included in the output. Otherwise it could lead to a large number of columns as variables get added down the line. But this seems like it would not be very straightforward so probably not worth worrying about right now.

Comment on lines +195 to +196
"grid_i": netcdf_data["grid_i"][traj_idx],
"grid_j": netcdf_data["grid_j"][traj_idx],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These variables should be optional. I believe only tempest extremes outputs them.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll drop them completely as the dashboard map rendering relies solely on lat/lon. It's best to start with a minimal schema and then add to it on user demand.

Comment on lines +122 to +147
"attributes": {k: ds.getncattr(k) for k in ds.ncattrs()},
"latitude": ds.variables["latitude"][:],
"longitude": ds.variables["longitude"][:],
"grid_i": ds.variables["grid_i"][:],
"grid_j": ds.variables["grid_j"][:],
}

time_var = ds.variables["time"]
data["time_units"] = time_var.getncattr("units")
data["time_calendar"] = time_var.getncattr("calendar")
data["time"] = ds.variables["time"][:]

# Read start/end flags if present (per-trajectory booleans)
if "start_flag" in ds.variables:
data["start_flag"] = ds.variables["start_flag"][:]
if "end_flag" in ds.variables:
data["end_flag"] = ds.variables["end_flag"][:]

# Observations (optional)
for prop in [
"air_pressure_at_sea_level",
"surface_altitude",
"wind_speed",
"atmosphere_relative_vorticity",
]:
data[prop] = v[:] if (v := ds.variables.get(prop)) is not None else None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that we change this so it selects variables by matching standard_name, then falls back to using the netcdf variable name, eg. replace `ds.variables.get("wind_speed") with a function like:

def get_var(ds, variable_name: str):
    for var in ds.variables.values():
        if getattr(var, "standard_name", None) == variable_name:
            return var
    return ds.variables.get(variable_name)

get_var(ds, "wind_speed")

Alternatively it could use cf-python to do the reading instead of netcdf4.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good idea but I've never seen a file where the standard_name and the actual name differed. Do you have any examples?

Comment on lines +308 to +314
# Support layout from previous version
tctrack_parameters = json.dumps(
{
"detect": json.loads(attributes.get("detect_parameters", "{}")),
"stitch": json.loads(attributes.get("stitch_parameters", "{}")),
}
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Support layout from previous version
tctrack_parameters = json.dumps(
{
"detect": json.loads(attributes.get("detect_parameters", "{}")),
"stitch": json.loads(attributes.get("stitch_parameters", "{}")),
}
)
# Support layout from previous version
tctrack_parameters = json.dumps(
{
key[:-len("_parameters")]: json.loads(value)
for key, value in attributes.items()
if key.endswith("_parameters")
}
)

Different tracking algorithms had different names for the *_parameters attributes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants