@truelies/osm-dybuf 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/osm_dybuf.d.mts +1 -1
- package/osm_dybuf.mjs +1 -4
- package/package.json +1 -1
- package/schema_ids.d.mts +5 -0
- package/schema_ids.mjs +64 -2
package/README.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
Gridex `osm.dybuf` parser utilities shared by the exporter, inspection scripts, and Next.js frontend.
|
|
4
4
|
This package wraps the shared schema tables (`schema_ids`) and exposes a high-level `parseOsmDybuf` helper.
|
|
5
5
|
It depends on the published [`dybuf`](https://www.npmjs.com/package/dybuf) runtime (≥ 0.4.2).
|
|
6
|
-
Feature IDs are packed as `feature_id = sub_id * 64 + main_id` (both 0–63).
|
|
6
|
+
Feature IDs are packed as `feature_id = sub_id * 64 + main_id` (both 0–63). The parser now returns numeric `featureId`; use `schema_ids` helpers to decode debug strings or subtypes when needed.
|
|
7
|
+
`schema_ids` also exports `packFeatureId(...)`, `unpackFeatureId(...)`, and `labelFeatureId(...)` for subtype-aware features such as future `label` overlays.
|
|
7
8
|
Current road groups are `road_express`, `road_major`, `road_minor`, `road_local`, and `road_trail`.
|
|
8
9
|
|
|
9
10
|
## Current Compatibility Status
|
|
@@ -22,7 +23,10 @@ Current frontend-facing schema notes:
|
|
|
22
23
|
|
|
23
24
|
- `admin_boundary` is retained in schema IDs for compatibility, but new exports no longer send boundary lines to the frontend.
|
|
24
25
|
- `place_label` remains in schema IDs for compatibility, but current exports no longer emit place-name labels to the frontend.
|
|
25
|
-
- `
|
|
26
|
+
- `label` main feature ID is now reserved for future overlay labels; `sub_id` is intended to carry `label kind` (`worship`, `station`, `custom`, etc.).
|
|
27
|
+
- Current exporter-oriented subtypes are `label_worship` and `label_station`, both derived from existing `building` tasks. Consumers should group all future `label_*` subtypes by `unpackFeatureId(featureId).mainKey === "label"` if they want a single overlay layer.
|
|
28
|
+
- Breaking change in `0.4.x`: `GridFeature.featureId` is numeric. Consumers should map it with `ID_TO_FEATURE_KIND[String(featureId)]` or `unpackFeatureId(featureId)` when string/debug output is needed.
|
|
29
|
+
- `road_express` covers motorway corridors plus `trunk` ways tagged with `motorroad=yes` (for example many Taiwan expressway mainlines).
|
|
26
30
|
- `water_wetland` separates marsh / bog / fen / wetland polygons from deeper open water.
|
|
27
31
|
- Current project max level is `13`; newer exports no longer emit `lv14~16`.
|
|
28
32
|
|
|
@@ -37,6 +41,9 @@ npm install @truelies/osm-dybuf
|
|
|
37
41
|
import { parseOsmDybuf } from "@truelies/osm-dybuf";
|
|
38
42
|
import {
|
|
39
43
|
FEATURE_KIND_IDS,
|
|
44
|
+
ID_TO_FEATURE_KIND,
|
|
45
|
+
MAIN_FEATURE_IDS,
|
|
46
|
+
unpackFeatureId,
|
|
40
47
|
REGION_NUMERIC_CODES,
|
|
41
48
|
} from "@truelies/osm-dybuf/schema_ids";
|
|
42
49
|
|
|
@@ -46,6 +53,10 @@ const parsed = parseOsmDybuf(data, cell);
|
|
|
46
53
|
|
|
47
54
|
if (parsed.format === "flat_v1") {
|
|
48
55
|
console.log(parsed.features.length);
|
|
56
|
+
console.log(parsed.features[0].featureId); // numeric feature id
|
|
57
|
+
console.log(ID_TO_FEATURE_KIND[String(parsed.features[0].featureId)]);
|
|
58
|
+
console.log(unpackFeatureId(parsed.features[0].featureId));
|
|
59
|
+
console.log((parsed.features[0].featureId & 0x3f) === MAIN_FEATURE_IDS.label);
|
|
49
60
|
} else if (parsed.format === "v2_indexed") {
|
|
50
61
|
console.log(parsed.entries.length);
|
|
51
62
|
} else {
|
|
@@ -67,6 +78,11 @@ if (parsed.format === "flat_v1") {
|
|
|
67
78
|
--level 8 --londex 213 --latdex 161 --limit 5
|
|
68
79
|
```
|
|
69
80
|
|
|
81
|
+
Feature ID usage notes:
|
|
82
|
+
- Use `FEATURE_KIND_IDS.*` for exact feature comparison, for example `featureId === FEATURE_KIND_IDS.waterbody`.
|
|
83
|
+
- Use `MAIN_FEATURE_IDS.*` with `featureId & 0x3f` when you want to group all subtypes under the same main feature, for example `(featureId & 0x3f) === MAIN_FEATURE_IDS.label`.
|
|
84
|
+
- Use `unpackFeatureId(featureId)` for debug output or subtype-aware logic where readability matters more than raw hot-path comparisons.
|
|
85
|
+
|
|
70
86
|
### Grid helpers (grid_index)
|
|
71
87
|
|
|
72
88
|
`grid_index.mjs` exposes Gridex helpers (integers, pole triangles) aligned with the exporter:
|
|
@@ -123,4 +139,4 @@ Unit test example (`tests/grid_index.test.mjs`) covers:
|
|
|
123
139
|
- `region_numeric_codes.json`: latest region ID table (generated via `scripts/dump_region_codes.py`)
|
|
124
140
|
- `inspect_dybuf.mjs`: CLI tool that uses the package locally
|
|
125
141
|
|
|
126
|
-
When schema IDs or DyBuf layouts change in the exporter, remember to bump the version here and re-publish/install so frontends and tools stay in sync. Version `0.
|
|
142
|
+
When schema IDs or DyBuf layouts change in the exporter, remember to bump the version here and re-publish/install so frontends and tools stay in sync. Version `0.4.1` aligns the package with the current `lv0~13` export range, the current frontend feature set (`water_wetland`, `building`, derived `label_*`, no exported `place_label`), and the numeric `featureId` parser contract.
|
package/osm_dybuf.d.mts
CHANGED
package/osm_dybuf.mjs
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
} from "dybuf";
|
|
7
7
|
import {
|
|
8
8
|
ID_TO_ENTITY_TYPE,
|
|
9
|
-
ID_TO_FEATURE_KIND,
|
|
10
9
|
ID_TO_GEOMETRY_KIND,
|
|
11
10
|
ID_TO_SEGMENT_ROLE,
|
|
12
11
|
SEGMENT_ROLE_ID_UNSET,
|
|
@@ -194,15 +193,13 @@ function decodePayload(payloadBytes, cell) {
|
|
|
194
193
|
const features = [];
|
|
195
194
|
for (let i = 0; i < featureCount; i += 1) {
|
|
196
195
|
const featureNumericId = readVarUint(buf);
|
|
197
|
-
const featureId =
|
|
198
|
-
ID_TO_FEATURE_KIND[String(featureNumericId)] ?? `feature:${featureNumericId}`;
|
|
199
196
|
const entryCount = readVarUint(buf);
|
|
200
197
|
const elements = [];
|
|
201
198
|
for (let j = 0; j < entryCount; j += 1) {
|
|
202
199
|
const entryBytes = readVarBytes(buf);
|
|
203
200
|
elements.push(decodeGridPayload(entryBytes, cell));
|
|
204
201
|
}
|
|
205
|
-
features.push({ featureId, elements });
|
|
202
|
+
features.push({ featureId: featureNumericId, elements });
|
|
206
203
|
}
|
|
207
204
|
if (buf.position() !== buf.limit()) {
|
|
208
205
|
throw new Error(
|
package/package.json
CHANGED
package/schema_ids.d.mts
CHANGED
|
@@ -3,6 +3,11 @@ export declare const ID_TO_ENTITY_TYPE: Readonly<Record<number, string>>;
|
|
|
3
3
|
export declare const GEOMETRY_KIND_IDS: Readonly<Record<string, number>>;
|
|
4
4
|
export declare const ID_TO_GEOMETRY_KIND: Readonly<Record<number, string>>;
|
|
5
5
|
export declare const MAIN_FEATURE_IDS: Readonly<Record<string, number>>;
|
|
6
|
+
export declare const LABEL_KIND_SUBTYPE_IDS: Readonly<Record<string, number>>;
|
|
7
|
+
export declare function packFeatureId(mainKey: string, subId: number): number;
|
|
8
|
+
export declare function unpackFeatureId(featureNumericId: number): { mainKey: string; subId: number };
|
|
9
|
+
export declare function labelFeatureId(labelKind?: string): number;
|
|
10
|
+
export declare function labelKindFromFeatureId(featureNumericId: number): string;
|
|
6
11
|
export declare const FEATURE_KIND_IDS: Readonly<Record<string, number>>;
|
|
7
12
|
export declare const ID_TO_FEATURE_KIND: Readonly<Record<number, string>>;
|
|
8
13
|
export declare const REGION_NUMERIC_CODES: Readonly<Record<string, number>>;
|
package/schema_ids.mjs
CHANGED
|
@@ -33,6 +33,7 @@ export const MAIN_FEATURE_IDS = Object.freeze({
|
|
|
33
33
|
waterway: 8,
|
|
34
34
|
place_label: 9,
|
|
35
35
|
building: 10,
|
|
36
|
+
label: 11,
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
const FEATURE_KIND_CODES = Object.freeze({
|
|
@@ -45,6 +46,11 @@ const FEATURE_KIND_CODES = Object.freeze({
|
|
|
45
46
|
waterbody: ["waterbody", 0],
|
|
46
47
|
water_wetland: ["waterbody", 1],
|
|
47
48
|
building: ["building", 0],
|
|
49
|
+
label: ["label", 0],
|
|
50
|
+
label_worship: ["label", 1],
|
|
51
|
+
label_station: ["label", 2],
|
|
52
|
+
label_custom: ["label", 3],
|
|
53
|
+
label_admin: ["label", 4],
|
|
48
54
|
landuse: ["landuse", 0],
|
|
49
55
|
park: ["park", 0],
|
|
50
56
|
road_express: ["road", 0],
|
|
@@ -58,11 +64,62 @@ const FEATURE_KIND_CODES = Object.freeze({
|
|
|
58
64
|
place_label: ["place_label", 0],
|
|
59
65
|
});
|
|
60
66
|
|
|
67
|
+
export const LABEL_KIND_SUBTYPE_IDS = Object.freeze({
|
|
68
|
+
generic: 0,
|
|
69
|
+
worship: 1,
|
|
70
|
+
station: 2,
|
|
71
|
+
custom: 3,
|
|
72
|
+
admin: 4,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export function packFeatureId(mainKey, subId) {
|
|
76
|
+
const mainId = MAIN_FEATURE_IDS[mainKey];
|
|
77
|
+
if (mainId === undefined) {
|
|
78
|
+
throw new Error(`Unknown feature main key: ${mainKey}`);
|
|
79
|
+
}
|
|
80
|
+
if (subId < 0 || subId >= 64) {
|
|
81
|
+
throw new Error(`Feature sub id must be 0-63 (got ${subId})`);
|
|
82
|
+
}
|
|
83
|
+
return subId * 64 + mainId;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function unpackFeatureId(featureNumericId) {
|
|
87
|
+
const numericId = Number(featureNumericId);
|
|
88
|
+
const mainId = numericId & 0x3f;
|
|
89
|
+
const subId = numericId >> 6;
|
|
90
|
+
const mainKey =
|
|
91
|
+
Object.entries(MAIN_FEATURE_IDS).find(([, value]) => value === mainId)?.[0] ?? null;
|
|
92
|
+
if (!mainKey) {
|
|
93
|
+
throw new Error(`Unknown feature main id ${mainId} in feature id ${numericId}`);
|
|
94
|
+
}
|
|
95
|
+
return { mainKey, subId };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function labelFeatureId(labelKind = "generic") {
|
|
99
|
+
const normalized = String(labelKind).trim().toLowerCase();
|
|
100
|
+
const subId = LABEL_KIND_SUBTYPE_IDS[normalized];
|
|
101
|
+
if (subId === undefined) {
|
|
102
|
+
throw new Error(`Unknown label kind: ${labelKind}`);
|
|
103
|
+
}
|
|
104
|
+
return packFeatureId("label", subId);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function labelKindFromFeatureId(featureNumericId) {
|
|
108
|
+
const { mainKey, subId } = unpackFeatureId(featureNumericId);
|
|
109
|
+
if (mainKey !== "label") {
|
|
110
|
+
throw new Error(`Feature id ${featureNumericId} is not a label feature`);
|
|
111
|
+
}
|
|
112
|
+
return (
|
|
113
|
+
Object.entries(LABEL_KIND_SUBTYPE_IDS).find(([, value]) => value === subId)?.[0] ??
|
|
114
|
+
"generic"
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
61
118
|
export const FEATURE_KIND_IDS = Object.freeze(
|
|
62
119
|
Object.fromEntries(
|
|
63
120
|
Object.entries(FEATURE_KIND_CODES).map(([kind, [main, sub]]) => [
|
|
64
121
|
kind,
|
|
65
|
-
|
|
122
|
+
packFeatureId(main, sub),
|
|
66
123
|
])
|
|
67
124
|
)
|
|
68
125
|
);
|
|
@@ -70,7 +127,12 @@ export const FEATURE_KIND_IDS = Object.freeze(
|
|
|
70
127
|
export const REGION_NUMERIC_CODES = Object.freeze(REGION_CODES_RAW);
|
|
71
128
|
|
|
72
129
|
export const ID_TO_FEATURE_KIND = Object.freeze(
|
|
73
|
-
|
|
130
|
+
{
|
|
131
|
+
...Object.fromEntries(Object.entries(FEATURE_KIND_IDS).map(([k, v]) => [String(v), k])),
|
|
132
|
+
...Object.fromEntries(
|
|
133
|
+
Object.values(LABEL_KIND_SUBTYPE_IDS).map((subId) => [String(packFeatureId("label", subId)), "label"])
|
|
134
|
+
),
|
|
135
|
+
}
|
|
74
136
|
);
|
|
75
137
|
|
|
76
138
|
export const SEGMENT_ROLE_ID_UNSET = 0x00;
|