Filter Configuration And Map Coloring
This document describes how track filters are configured, persisted, exposed to the UI, and used for map coloring and legends.
The important distinction is:
- A filter decides which tracks are returned.
- A filter can optionally assign each returned track to a group.
- Metadata on the filter tells the UI how to organize the filter, sort groups, label groups, and choose colors.
On this page
1. Filter Storage
Filters are stored in filter_config.
Core identity and execution fields:
| Column | Description |
|---|---|
filter_domain |
Domain the filter applies to. Currently GPS_TRACK. |
filter_name |
Stable unique filter key within the domain. |
filter_type |
Execution type. Currently SQL. |
filter_category |
SYSTEM or USER. |
expression |
SQL template used to resolve matching track IDs and optional groups. |
display_name |
Human-facing filter name. |
description |
Human-facing explanation and use case. |
display_order |
Ordering within filter lists. The frontend also uses the lowest order in a group to order selector groups. |
UI and coloring metadata:
| Column | Description |
|---|---|
filter_group |
UI section for the filter selector, for example Date & Time, Performance, or People. |
group_semantics |
What the returned group values mean. |
coloring_strategy |
How colors should be assigned to groups. |
legend_sort_strategy |
How the map and preview legends should be ordered. |
preferred_palette |
Optional default palette key used when the user has not chosen a palette yet. |
group_label_template |
Optional display template for raw group values. |
ui_metadata |
Optional JSON metadata for UI hints, formatting, and optional params. |
The audit table filter_config_audit mirrors these fields. The audit trigger
must be updated whenever filter metadata columns are added.
2. Typed Metadata Values
The database stores metadata as text columns, but Java models fixed metadata
sets as enums with @Enumerated(EnumType.STRING).
Source of truth:
FilterConfigEntity
GROUP_SEMANTICS
| Value | Meaning |
|---|---|
CATEGORICAL |
Unordered names such as activity type, quality status, or person. |
DATE_BUCKET |
Date-like buckets such as year or calendar day. |
ORDINAL |
Ordered labels with a fixed known sequence, such as quarters or weekdays. |
NUMERIC_BUCKET |
Numeric buckets, usually percentile-style values. |
COLORING_STRATEGY
| Value | Meaning |
|---|---|
CATEGORICAL |
Assign distinct colors per group. Encounter order should not matter after legend sorting. |
SEQUENTIAL_GRADIENT |
Map ordered numeric group values onto a continuous palette. |
LEGEND_SORT_STRATEGY
| Value | Meaning |
|---|---|
LABEL_ASC |
Sort labels alphabetically, with numeric-aware comparison. |
NUMERIC_ASC |
Sort numeric bucket values from low to high. |
COUNT_DESC |
Sort groups by matching track count, largest first. |
These dimensions are intentionally separate. For example, an ordered weekday
filter has group_semantics = ORDINAL but still uses categorical colors. A
speed gradient filter has group_semantics = NUMERIC_BUCKET and
coloring_strategy = SEQUENTIAL_GRADIENT.
3. SQL Filter Contract
A SQL filter returns at least:
id
Grouped/coloring filters also return:
grp
The raw grp value is the stable group key used by the map layer and by group
visibility toggles. The UI can display a friendlier label using metadata, but
the raw key should remain stable.
Most GPS track filters include the standard base filter:
[[~{/GPS_TRACK/SmartBaseFilter}]]
This keeps normal date, geo area, successful-load, duplicate, and planned-track constraints consistent across filters.
4. Filter Groups
Current top-level UI groups are:
| Group | Display-order range | Typical filters |
|---|---|---|
Core |
1000-1999 |
Standard base filter. |
Activity |
2000-2999 |
Activity type and motorized/non-motorized filters. |
Date & Time |
3000-3999 |
Year, day, quarter, weekday, time of day. |
People |
4000-4999 |
Personal YAML-defined companion filters. |
Performance |
5000-5999 |
Average speed, distance, elevation gain, energy gradients. |
Quality |
6000-6999; Needs review uses 5990 |
Needs review, suspicious, duplicate, and error filters. |
User |
9000-9899 |
User-defined filters without a more specific group. |
Other |
9900+ |
Fallback for uncategorized filters. |
The frontend orders selector groups by the lowest display_order inside each
group, then uses a small known-group fallback order only for ties or missing
orders. This keeps the database metadata as the primary source of ordering
truth while still giving unknown group names stable behavior.
5. Presentation Defaults
preferred_palette describes the recommended first palette for a filter.
Legend order is a client-side choice. The frontend stores user overrides in the
client filter configuration, so changing legend order or palette does not mutate
the system filter definition.
An explicit No coloring choice stores palette.noColoring = true and survives
reloads, paused drafts, and view changes. An unset palette can still receive the
view's preferred default. Older saved empty choices with the No coloring label
are migrated to the explicit choice. Reset clears the choice; Undo restores it.
Changing views resets a custom category order to the new view's default. The view picker discloses this reset before applying the change.
If legend_sort_strategy is missing, the frontend preserves the first-seen
group order returned by the SQL query. Filters that need a specific default
order should make that order explicit in SQL. Numeric ordering is only used when
NUMERIC_ASC is selected by the user.
The frontend uses preferred_palette when a filter is selected and no palette
is active yet. It also replaces an obviously incompatible palette when switching
between categorical and gradient filters:
- gradient filter with categorical palette: switch to the filter's preferred gradient palette
- categorical filter with gradient palette: switch to the filter's preferred categorical palette
- categorical filter with an existing categorical palette: keep the user's current choice
Current categorical defaults use existing palette keys:
| Filter shape | Preferred palette |
|---|---|
| naturally small groups, such as quarters, motorized/non-motorized, quality filters | 005_COLORS |
| weekday, time-of-day, activity, people, and general user filters | 008_COLORS |
| standard base filter without groups | none |
ui_metadata.optionalParams lists params that may be left blank even when they
are rendered as editable fields, for example DATE_TIME_FROM,
DATE_TIME_TO, or gradient min/max range params.
6. Gradient Filters
Gradient filters are designed for ordered numeric buckets.
Current gradient filters:
| Filter | Metric | Unit | Params |
|---|---|---|---|
TracksByAverageSpeedGradient |
Average moving speed | km/h |
AVG_SPEED_MIN_KMH, AVG_SPEED_MAX_KMH |
TracksByDistanceGradient |
Track distance | km |
DISTANCE_MIN_KM, DISTANCE_MAX_KM |
TracksByElevationGainGradient |
Elevation gain | m |
ELEVATION_GAIN_MIN_M, ELEVATION_GAIN_MAX_M |
TracksByEnergyGradient |
Estimated net mechanical energy | kWh |
ENERGY_MIN_KWH, ENERGY_MAX_KWH |
These names and units are also exposed by the current filter API. The client
uses filterParamUnits.ts to convert display input back to the metadata unit.
The proposed public renames to meters and watt-hours have not been implemented;
see the measurement-system status.
Each gradient filter assigns the current result set to bucket values 0 to
99. The bucket is based on sorted rank within the filtered result set, not on
an absolute metric threshold. This keeps the full color range useful for the
visible tracks.
For small result sets, the SQL scales rank positions across the full 0-99
range. With two tracks, the lower track gets bucket 0 and the higher track
gets bucket 99. With one track, the bucket is 0.
Recommended metadata for gradient filters:
| Field | Value |
|---|---|
group_semantics |
NUMERIC_BUCKET |
coloring_strategy |
SEQUENTIAL_GRADIENT |
preferred_palette |
A GRADIENT_100_* palette, usually GRADIENT_100_VIRIDIS. |
group_label_template |
A metric-specific label such as Avg speed percentile {bucket}. |
The frontend maps bucket values to palette indices. A 100-color palette maps
bucket 0 to the first color and bucket 99 to the last color. Shorter
palettes are scaled proportionally.
7. Group Labels
The raw group key should remain compact and stable. Display labels are derived in the UI.
For numeric buckets, group_label_template supports:
| Placeholder | Meaning |
|---|---|
{bucket} |
Zero-padded bucket based on the configured bucket count. For 100 buckets this is 00 to 99. |
{bucketNumber} |
Raw numeric bucket without padding. |
Example:
group_label_template = "Avg speed percentile {bucket}"
grp = "1"
display label = "Avg speed percentile 01"
This keeps legends readable while preserving numeric sorting.
8. YAML-Defined Filters
Filters can also be supplied through application YAML under:
mtl.filter-configs
Example metadata:
filter-group: People
group-semantics: CATEGORICAL
coloring-strategy: CATEGORICAL
legend-sort-strategy: LABEL_ASC
preferred-palette: 008_COLORS
The YAML properties bind directly to FilterConfigEntity, so enum values must
match the Java enum names exactly.
The initializer only creates YAML filters that do not already exist in the database. Editing YAML does not update an existing row. Do not add a historical backfill unless explicitly requested; follow backend migration policy and document when recreation is required.
9. OpenAPI And Frontend Types
FilterConfigEntity is exposed through the OpenAPI schema. Because the metadata
fields are Java enums, the generated TypeScript client exposes matching enum
types.
Frontend code should use the generated enum constants instead of duplicating raw strings. This keeps UI behavior aligned with the server contract.
Relevant frontend behavior:
- the filter selector groups filters by
filter_group - the map legend and preview legend sort by the user override, otherwise preserve the first-seen group order returned by SQL
- gradient filters use
coloring_strategy = SEQUENTIAL_GRADIENT - display labels use
group_label_template - gradient help text is based on
ui_metadata
10. Key Source Files
| File | Role |
|---|---|
mtl-server/src/main/java/.../db/entity/config/FilterConfigEntity.java |
JPA entity and metadata enums. |
mtl-server/src/main/java/.../logic/grouping/sql/custom/FilterConfigInitializer.java |
Creates YAML-defined filters if missing. |
mtl-server/src/main/resources/db/changelog/changes/047.xml |
Metadata columns, audit trigger update, backfills, gradient SQL updates. |
application.yml |
User-defined filter configuration; see the examples above. |
mtl-client/src/utils/filterMetadata.ts |
Frontend metadata interpretation, label formatting, legend sorting, gradient color mapping. |
mtl-client/src/components/filter/CustomFilter.vue |
Filter selector, preview legend, palette guidance. |
mtl-client/src/components/map/Map2DRenderer.vue |
Main map rendering. |
mtl-client/src/components/filter/FilterOverview.vue |
Current filter view, criteria, categories, and review entry points. |
mtl-client/src/components/map/ColorPaletteLegend.vue |
Map legend and visibility controls. |