Render NOBJNM national language names - #64
Conversation
|
@beetlebugorg I tested some Chinese ENC cells and found a mismatch between the declared
OpenCPN and GDAL ran into the same problem. Their mature approach is content heuristic detection rather than trusting
References:
Suggestion: adopt the same strategy in tile57. After reading the This ensures correct parsing of Chinese, Japanese, and Korean Happy to provide a sample for verification if useful. |
|
A sample to work from would be great, thanks. |
|
@beetlebugorg What's the best way to send you the sample? Email, or something else? |
|
It'd probably be easiest if you could encrypt it with age and attach it to this PR. |
The adapter built featureName from OBJNAM alone, so a feature carrying only a national language name portrayed no name, and a feature carrying both could portray only the English one. NOBJNM now becomes a second featureName entry. OBJNAM keeps nameUsage 1, the entry GetFeatureName falls back to when no language matches, so a cell with both names portrays as it did. A cell with NOBJNM alone gives that entry nameUsage 1, so the name renders. S-57 records no language for NOBJNM, so the entry uses ISO 639-2 "und", undetermined. Selecting it needs contextParameters.PreferredLanguage, which lua_shim.c fixes at 'eng'. Making that a mariner setting is a separate change. NOBJNM feeds the complex, so the flat attribute loop skips it, as it does for INFORM and TXTDSC. Four S-57 cells bake .pmtiles byte identical to main. None of them has a NOBJNM.
Portrayal runs at bake time and GetFeatureName picks one string, so the label a host sees is fixed in the tile. Switching to the national name at runtime needs both strings there. The bake now stores NOBJNM as text_nat beside text, the way a dredged area's depth stores text_ft, and the national_names setting reads the twin through the same coalesce. depthTextField becomes labelTextField and composes both twins. A feature has at most one of them. The rules wrap a name, so the twin substitutes NOBJNM for the OBJNAM occurrence in the label rather than replacing the whole string. AnchorBerth's "Nr Shanghai" becomes "Nr 上海". A label that does not contain the feature's name gets no twin, which covers light descriptions and every other text instruction. FeatureMeta has both names because the surface sees the portrayed string alone. tile57_mariner gains national_names, appended. Three S-57 cells with no NATF data bake .pmtiles byte identical to main. A fourth, which has NATF data, grows 495 bytes.
A style coalesces text_nat on the tile path. The pixel, PDF and character surfaces read the portrayal instruction directly and never consult a style, so national_names had no effect on those outputs. They resolve it at draw time, the way drawDepthText resolves depth_unit. The substitution helper moves to render/surface.zig, where scene, pixel and ascii share it. ascii_view_test drives the real S-101 rules over a named BUAARE and reads the label off the grid: Harwich with the setting off, Harwijk with it on.
The render tools already expose mariner settings as flags, including --feet and --no-names. national_names had none, so the new setting could only be exercised through the C ABI or a unit test. On a cell with both names, an LNDARE labels "Rossett Island" by default and "Rössett Inseln" with the flag. A BUAARE in the same view with no NOBJNM stays as it is.
The pixel and character surfaces substituted the national name from FeatureMeta, which only the direct portrayal path fills. A surface reading a baked bundle saw empty names and drew the portrayed label whatever the setting said. TextStyle gains a national field. The scene fills it beside the label it emits, TileSurface bakes it as text_nat, and replay reads it back, so a surface sees the same pair on either path. This follows drawDepthText, where the raw metres are baked beside the string and the surface formats them. FeatureMeta drops name and name_nat. tile57 ascii over a baked bundle prints Rossett twice by default, and Rossett plus Rössett with the flag. The second is the LNDARE with a NOBJNM. The BUAARE of the same name has none and is unchanged.
tile-schema.md gains the text_nat property on the text layer. api/style.md gains the national_names field. rendering.md lists --national-names with the other mariner flags. limitations.md records that S-57 stores one NOBJNM and no language for it, so the converted featureName is tagged und and the setting selects the national name rather than a named language.
The reproduced tile57_mariner omitted soundings and chart_over_image, both appended to the header before this branch. The page now lists all 35 fields in header order, checked by extracting the field names from both.
The twin was built by substituting NOBJNM for OBJNAM in the label, which reads names off the s57.Feature. A native S-101 dataset keeps its names in featureName complexes, and native.zig surrogates only simple attributes onto the shell, so a chart naming its features in Inuktitut portrayed in English and the setting had no effect. PreferredLanguage was fixed at 'eng' in the driver and is now a context parameter. adapter.nationalLanguage reports the first featureName language in the cell that is not English. An S-57 cell states und, because the adapter tags NOBJNM that way. An S-101 dataset states its own ISO 639-2 code. A baked chart runs a fifth portrayal pass in that language, beside the plain, simplified and full-light-lines passes. Only the label text differs, so scene compares the two passes per text instruction and bakes the difference as text_nat. A live render portrays once, so tile57 png, pdf and ascii pass the language into the portrayal context. A native S-101 dataset naming its features in seven languages changes its labels to the Inuktitut entries with the flag, and the embedded Noto Sans draws those as boxes. An S-57 cell with both names still reads Rossett Island and Rössett Inseln. Three S-57 cells bake byte identical to main.
S-57 clause 2.4 puts general text at lexical level 0, 1 or 2, and level 2 is UCS-2. At that level the unit terminator is the two-byte code unit 0x001F, so 1F 00 separates the values in an ATTF or NATF field. parseAttrs split on the single byte, so it resumed one byte early and read every ATTL after the first from the wrong offset. Latin text in UCS-2 is all bytes under 0x80 and took the ASCII fast path, which frames it the same wrong way. DSSI states the level in NALL. A Chinese cell has been reported stating NALL 0 while writing UTF-16LE, so isDoubleByteField reads the encoding from the field itself: every value an even number of bytes, and at least one terminator followed by a NUL. A single-byte field matches only if every attribute code in it is a multiple of 256 and every value has even length. ucs2ToUtf8 decodes the values. An unpaired surrogate reads as U+FFFD rather than failing the cell. Tested against synthesized level 2 fields, CJK and Latin, and against a cell with a Latin-1 NATF, whose baked tile is unchanged. Three S-57 cells bake byte identical to main.
The pixel and PDF outputs draw labels with the bundled Noto Sans, which covers Latin, Greek and Cyrillic. A national name in another script draws as boxes. A chart naming its features in Inuktitut syllabics does this. The conversion entry had a semicolon joining two clauses.
zig fmt --check rejected src/chart.zig. The CellRef literal at the compose-tile call site had the portrayal_national field indented one level too deep.
isDoubleByteField and the UCS-2 split ran past the end of the field. At lexical level 2 the field terminator is the two-byte code unit 0x001E, and iso8211.parseFields strips a single-byte FT only, so 1E 00 is still in the field data. The scan read those two bytes as another ATTL, found no unit terminator after them, and reported the field as single byte. Both loops now stop at the FT. A Chinese cell stating NALL 0 while writing UTF-16LE reads its NOBJNM values as 富民沙路 and 合心, beside OBJNAM Fuminsha Lu and Hexin. The test uses the byte sequence such a field has.
11a6614 to
b7a362c
Compare
national_names was a boolean, so one alternative language was reachable per cell, chosen as the first non-English one the chart stated. A chart naming its features in several languages offered the rest to nobody. tile57_mariner.preferred_language is an ISO 639-2 code. adapter.languages reports the languages a chart states, capped at four, and the bake runs a portrayal pass per language, storing each label as text_<lang>. A style coalesces the mariner's code, then text_und, then the portrayed name. TextStyle.national holds the set, so a surface picks the same way whether the scene fills it or replay reads it back from a tile. An S-57 cell states und, because S-57 records no language for NOBJNM, and und answers any code the mariner asks for. The pixel outputs draw every label with the bundled Noto Sans, which covers Latin, Greek and Cyrillic, so a label in another script came out as boxes. font.Font reads a TrueType collection, because the CJK faces a system ships arrive that way, and font.fallback holds a face consulted for a codepoint the run's face has no glyph for. pushText picks a face per codepoint. The engine holds no path, so a host installs the face with setFallback and the render tools read TILE57_FONT_FALLBACK. The PDF and vector paths embed one face per label and still draw the bundled face there. The png, pdf and ascii tools take --language in place of --national-names. Three S-57 cells bake byte identical to main.
|
Thanks for the sample, that helped verify. It should be working now. I'm going to do a follow up PR that will bake the list of available languages into the tile metadata. S101 allows for multiple. |
tile57 rendered
OBJNAMnames and droppedNOBJNM, so a feature with a national language name showed only its English counterpart, and a feature with onlyNOBJNMshowed no name at all. Closes #63.The adapter converts
NOBJNMinto a secondfeatureName, and selection goes throughPreferredLanguage, which the driver had fixed ateng.tile57_mariner.preferred_languageis now an ISO 639-2 code. An S-57 cell statesund, because S-57 records no language forNOBJNM, andundanswers whatever code the mariner asks for. A native S-101 dataset states real codes, so asking for one picks that language's name.Portrayal runs at bake time, so a baked chart runs a pass per language beside the plain, simplified and full-light-lines passes. Only the label text differs, so the scene stores what changed as
text_<lang>andTextStyle.nationalholds the set for a surface, whether the scene fills it or replay reads it back.depthTextFieldbecomeslabelTextFieldand coalesces the mariner's code, thentext_und, then the portrayed name. A live render portrays once and passes the language into the portrayal context instead.S-57 clause 2.4 also allows NATF at lexical level 2. That level is UCS-2, and its unit and field terminators are two bytes.
parseAttrssplit on the single byte and read everyATTLafter the first from the wrong offset.isDoubleByteFieldreads the encoding from the field rather thanNALL, since a cell has been reported statingNALL0 while writing UTF-16LE. Thanks to @Sladewww for the report and the sample.The pixel outputs draw labels with the bundled Noto Sans, so a label in another script came out as boxes.
font.Fontreads a TrueType collection,font.fallbackholds a face consulted per codepoint, and the render tools readTILE57_FONT_FALLBACK. Nothing is bundled, so the default build keeps its size.limitations.mdrecords that, the four-language cap, and that the PDF and vector paths embed one face per label.The png, pdf and ascii tools take
--language <iso639-2>.docs/docs/api/style.mdwas also missingsoundingsandchart_over_image, appended to the header before this branch.Verified on an S-57 cell with both names, which labels
Rossett Islandby default andRössett Inselnfor any code, live and through a baked bundle. On a Chinese cell statingNALL0, whoseNOBJNMvalues read as 富民沙路 and 合心 beside Fuminsha Lu and Hexin, drawn with a fallback face. On a native S-101 dataset naming its features in seven languages, where two codes select two different sets of labels. Three S-57 cells bake byte identical to main.zig build,zig build testandzig fmt --checkpass.