Skip to content

feat: implement est_width parsing and sidebar display - #289

Open
Momototoro wants to merge 6 commits into
FixMyBerlin:developfrom
Momototoro:dev-est_width
Open

feat: implement est_width parsing and sidebar display#289
Momototoro wants to merge 6 commits into
FixMyBerlin:developfrom
Momototoro:dev-est_width

Conversation

@Momototoro

@Momototoro Momototoro commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

So hier jetzt das Erste:
https://github.com/FixMyBerlin/private-issues/issues/280
Ist hauptsaechlich gevibecoded also ich verstehe auch nicht alles weil ich mich auch nicht tief genug in Tilda eingearbeitet habe.
Habe aber den Implementation plan immer schoen durch einen Grill Prozess durch geschickt.

PR Review: dev-est_width

Branch Summary

  • Base Branch: develop
  • Core Changes: Implements robust parsing of street widths from OSM (handling units like cm and m), falls back to est_width when the default width tag is missing, logs parsing errors to a dedicated database table, and displays the width's confidence and source in the sidebar inspector and export files.

Changed Files (19 total)

File Status Description
parse_width.lua New Pure utility to parse width strings (e.g. converting 250 cm -> 2.5 m) and normalizing decimals.
width_errors.lua New Logic and table definition (width_errors) to log unparsable width tags.
RoadClassification.lua Modified Integrates parser fallback from width -> est_width, setting width_confidence and width_source.
roads_bikelanes.lua Modified Adds width_confidence and width_source columns to roads/roadsPathClasses tables.
ExcludeByWidth.lua Modified Migrates to use the new parse_width utility.
RoadWidth.lua Modified Refactored fallback width calculations to use the new parser.
TagsTableRowCompositWidth.tsx Modified Updates the width row to render the source and confidence inline inside parentheses.
translationsWdith.const.ts Modified Adds translation keys for width_confidence (Geschätzt, Direkt gemessen) and common sources.
ConditionalFormattedValue.tsx Modified Configures source-independent translations for width_confidence and width_source.
export.$regionSlug.$tableName.ts Modified Dynamically queries and includes width_confidence and width_source columns in exports.
ParseWidth.test.lua New Lua unit tests for the width parsing utility.
RoadClassification.test.lua New Lua unit tests validating fallback logic and confidence assignment.
Various .gen files Modified Rebuild of auto-generated topic docs and translation mappings.

Implementation Details & Code Highlights

1. Robust Unit Parsing (parse_width.lua)

We normalize comma decimals and convert non-meter units like cm or km to meter floats:

local val, unit = osm2pgsql.split_unit(clean_value, 'm')
if val then
  if unit == 'cm' then
    return val / 100
  elseif unit == 'm' then
    return val
  elseif unit == 'km' then
    return val * 1000
  end
end

2. OSM Metadata Fallback (RoadClassification.lua)

We fallback to est_width if width is empty/unparsable, and assign high vs low confidence categories:

if object_tags.width then
  local parsed = parse_width(object_tags.width, object, 'width')
  if parsed then
    width = parsed
    width_confidence = "high"
  end
end
if not width and object_tags.est_width then
  local parsed = parse_width(object_tags.est_width, object, 'est_width')
  if parsed then
    width = parsed
    width_confidence = "low"
  end
end
result_tags.width_confidence = width_confidence
result_tags.width_source = SANITIZE_TAGS.safe_string(object_tags['source:width'])

3. Database Schema Migration (roads_bikelanes.lua)

The parsed attributes are saved into dedicated columns on the roads and roadsPathClasses tables:

local roadsTable = osm2pgsql.define_table({
  name = 'roads',
  columns = {
    -- ...
    { column = 'width_confidence', type = 'text' },
    { column = 'width_source', type = 'text' },
  }
})

4. Inline Inspector Display (TagsTableRowCompositWidth.tsx)

In the sidebar inspector, we display the source and the confidence value inline in parentheses inside a collapsible disclosure:

{properties[widthConfidence] && (
  <>
    {' ('}
    <ConditionalFormattedValue
      sourceId={sourceId}
      tagKey={widthConfidence}
      tagValue={properties[widthConfidence]}
    />
    {')'}
  </>
)}

Output Example: Quelle: Orthofoto 2024 (Direkt gemessen)

@Momototoro
Momototoro requested a review from tordans June 8, 2026 09:32

@tordans tordans left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rebase against develop.

) : (
<code>{secureWidthSource}</code>
)}
{properties[widthConfidence] && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Revert

'traffic_sign:backward',
'confidence', // true key is `maxspeed_confidence`, `surface_confidence`, … but we overwrite that when passing props
'width_source',
'width_confidence',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rebase => Docs

Comment on lines +54 to +55
{ column = 'width_confidence', type = 'text' },
{ column = 'width_source', type = 'text' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Regel: Unsere Tabellen haben immer den gleichen Shape id, tags, meta, geom, minzoom

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In Tags rein

FROM information_schema.columns
WHERE table_name = '${tableName}'
AND column_name IN ('osm_id', 'osm_type')
AND column_name IN ('osm_id', 'osm_type', 'width_confidence', 'width_source')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Weg, siehe unten

@@ -0,0 +1,87 @@
describe("parse_width", function()
require('init')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

weg nach rebase

require('init')
require('osm2pgsql')

local logged_errors = {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

kann jetzt raus

Comment thread processing/topics/helper/osm2pgsql.lua Outdated
-- return osm2pgsql.define_table(_options)
-- end

function osm2pgsql.define_table(options)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

raus

@@ -0,0 +1,101 @@
describe('RoadClassification', function()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

fehlt inner describe blcok weil alels ja nicht road clas.… teste sondern with

local width = nil
local width_confidence = nil

if object_tags.width then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Refactor to local function surface_direct(tags)

Nach Rebase:

    width = parse_length(object_tags.width),
    width_source = SANITIZE_TAGS.safe_string(object_tags['source:width']),
    width_effective = parse_length(object_tags['width:effective']),

neues muster:

local FOO = derive_witdth(tags)

local result_tags = {
    width = foo.width
    width_source = FOO.source
    width_confience = foo.conficnde
}
  local surface_tags_result = SURFACE_TAGS.surface_tags_with_parent(object.tags, object._parent_tags)

…
    surface = surface_tags_result.value,
    surface_confidence = surface_tags_result.confidence,
    surface_source = surface_tags_result.source,

Comment thread processing/run-tests.sh Outdated
# Make sure we have the latest lua path at hand.
# Szenario: We just added a lua helper in a new folder but did not run the full processing, yet
bun run ./processing/run-tests-update-lua-package-paths.ts
NODE_PATH=./app/node_modules bun run ./processing/run-tests-update-lua-package-paths.ts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

raus, bun verwendne
brew install bun

@tordans
tordans marked this pull request as draft June 23, 2026 09:02
@Momototoro
Momototoro force-pushed the dev-est_width branch 2 times, most recently from 2e74a65 to 459574e Compare July 5, 2026 15:40
Momototoro pushed a commit to Momototoro/tilda-geo that referenced this pull request Jul 21, 2026
- Roads show measured vs estimated width confidence in the inspector
- Width metadata stays in tags (no extra DB columns or export hacks)
- Reuse existing parse_length and TagsTableRowValueSourceConfidence patterns
Ping FixMyBerlin#289

Co-authored-by: Cursor <cursoragent@cursor.com>
Momototoro pushed a commit to Momototoro/tilda-geo that referenced this pull request Jul 21, 2026
- Unblocks pre-push knip on feature branches
Ping FixMyBerlin#289

Co-authored-by: Cursor <cursoragent@cursor.com>
@tordans

tordans commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Taking this over with a clean rebuild on current develop: see the replacement PR (same feature, TILDA derive_* pattern, tags-only metadata, no width_errors/DB columns/export hacks/CompositWidth).

Please close this draft when convenient — thanks for the first pass and the grill process on the plan.

@tordans

tordans commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Replacement PR: #362

@tordans

tordans commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Continuing at #362

@tordans tordans closed this Aug 2, 2026
@Momototoro Momototoro reopened this Aug 5, 2026
momo and others added 6 commits August 5, 2026 10:43
Co-authored-by: Cursor <cursoragent@cursor.com>
bunx tsc was resolving to a global TypeScript 5 install on WSL instead
of the project's TypeScript 6 package, which only exposes the tsc6 bin.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Roads show measured vs estimated width confidence in the inspector
- Width metadata stays in tags (no extra DB columns or export hacks)
- Reuse existing parse_length and TagsTableRowValueSourceConfidence patterns
Ping FixMyBerlin#289

Co-authored-by: Cursor <cursoragent@cursor.com>
- Unblocks pre-push knip on feature branches
Ping FixMyBerlin#289

Co-authored-by: Cursor <cursoragent@cursor.com>
@Momototoro
Momototoro marked this pull request as ready for review August 5, 2026 10:06

@tordans tordans left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Erste Blick sieht gut aus.

Comment thread app/package.json Outdated
"@clack/prompts": "1.7.0",
"@faker-js/faker": "^10.5.0",
"@heroicons/react": "2.2.0",
"@maplibre/maplibre-gl-style-spec": "^24.8.1",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Höchstwahrscheinlich falsch, dass das entfernt wurde. Könnte daran liegen, dass du den Symlink zu dem Static Data Repo nicht aktiv hast. (Siehe bun run somethingsomethign-symlink)

})
assert.are.same(result, {
width = 5,
width_source = nil,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hier müsste jetzt IMO etwas stehen wie

Suggested change
width_source = nil,
width_source = 'tag_est_width',

Siehe die anderen _source Werte: Die _source ist immer ein Enum mit Werten für diesen Case. Bzw. manchmal ein Freitext. Hier könnten wir einen Case haben, wo es eine Mischung ist. Bin unsicher, ob wir so etwas schon haben im System, aber es müsst auch in den Docs gehen dass man sagt Entweder Enum oder Sanitized Freitext

---@param tags OsmTags
---@return DeriveWidthResultUnion
local function derive_width(tags)
local width_source = SANITIZE_TAGS.safe_string(tags['source:width'])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if width then
return {
width = width,
width_source = width_source,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
width_source = width_source,
width_source = width_source or 'tag_width',

Fallback: Enum

}
end

width = parse_length(tags.est_width)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
width = parse_length(tags.est_width)
width = parse_length(tags.est_width)
local est_width_source = SANITIZE_TAGS.safe_string(tags['source:est_width'])

https://taginfo.geofabrik.de/europe:germany/keys/source%3Aest_width

if width then
return {
width = width,
width_source = width_source,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
width_source = width_source,
width_source = est_width_source or width_source or 'est_width_tag',

Default müsste source:est_width sein; einige Mapper könnten aber trotzdem source:width verwenden; Fallback dann das Enum.

description: Wert stammt aus dem OSM-Tag `width`.
- value: low
label: Geschätzt
description: Wert stammt aus dem OSM-Tag `est_width`, weil `width` fehlt oder nicht geparst werden konnte.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
description: Wert stammt aus dem OSM-Tag `est_width`, weil `width` fehlt oder nicht geparst werden konnte.
description: Wert stammt aus dem OSM-Tag `est_width`.

Comment on lines 147 to 152
format: sanitized_strings
values:
- value: ALKIS
label: Aus ALKIS Daten ausgemessen
- value: ARCore
label: Mit dem Handy-Metermaß von StreetComplete gemessen

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, wir haben es schon so angelegt dass es sanitized_strings UND enum ist. Und haben sogar bestimmte Felder übersetzt. Kann man so machen… dann fügen wir hier einfach unsere ENUM Werte mit ein.

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