Skip to content

Commit 13ae786

Browse files
committed
Group lookups by their objc and tnam per section 10.3.3.1
1 parent aec5019 commit 13ae786

2 files changed

Lines changed: 103 additions & 23 deletions

File tree

packages/styles/src/symbolology/index.ts

Lines changed: 91 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import type {
33
BackgroundLayerSpecification,
44
ExpressionFilterSpecification,
55
ExpressionSpecification,
6+
FilterSpecification,
67
LayerSpecification,
78
} from "maplibre-gl";
89
import { LookupEntry } from "@enc-tiles/dai";
910
import { instructionsToStyles } from "../instructions/index.js";
1011
import * as filters from "../filters.js";
12+
import { groupBy } from "../utils.js";
1113

1214
export interface LayerConfig {
1315
mode: Mode;
@@ -37,33 +39,99 @@ const filterGeometryType: Record<LookupEntry["ftyp"], ExpressionSpecification> =
3739
};
3840

3941
export function build(config: LayerConfig): LayerSpecification[] {
42+
const lookupGroups = groupBy(getLookups(config), (lookup) => {
43+
return [lookup.obcl, lookup.tnam].join("|");
44+
});
45+
46+
const layers: LayerSpecification[] = Object.values(lookupGroups).flatMap(
47+
(lookups) => {
48+
if (!lookups)
49+
throw new Error(
50+
"This should never happen but TypeScript insists it can.",
51+
);
52+
53+
if (lookups.length <= 1) {
54+
return lookups.flatMap(lookupToLayers);
55+
} else {
56+
return lookupGroupToLayers(lookups);
57+
}
58+
},
59+
);
60+
61+
return [background(config), ...layers];
62+
}
63+
64+
/**
65+
* 10.3.3.1 Look-Up Table Entry Matching
66+
*
67+
* > To find the symbology instruction for a specific object, enter the look-up table with the object's
68+
* > class code and gather all lines that contain [`objc`]. If only a single line is found,
69+
* > [`attc`] must be empty and the object is always shown with the same symbology
70+
* > regardless of its description.
71+
*
72+
* > If there is more than one line in the look-up table, search for the first line each of whose attribute
73+
* > values in [`attc`] can also be found in the attribute values of the object. If more than one attribute
74+
* > value is given in the look-up table, the match to the object must be exact, in order as well as
75+
* > content.
76+
*/
77+
export function lookupGroupToLayers(
78+
lookups: LookupEntry[],
79+
): LayerSpecification[] {
80+
const [fallbackLookup, ...otherLookups] = lookups;
81+
82+
const fallbackFilter: FilterSpecification = [
83+
"!",
84+
[
85+
"any",
86+
...otherLookups.map((lookup) => {
87+
return filters.all(...filters.attributeFilters(lookup.attc));
88+
}),
89+
],
90+
];
4091
return [
41-
background(config),
42-
...getLookups(config).flatMap((lookup, i) => {
43-
return instructionsToStyles(lookup.inst).map((layer, j) => {
44-
return {
45-
...layer,
46-
filter: filters.all(
47-
filters.scaleFilter(),
48-
filterGeometryType[lookup.ftyp],
49-
...filters.attributeFilters(lookup.attc),
50-
...("filter" in layer
51-
? [layer.filter as ExpressionFilterSpecification]
52-
: []),
53-
),
54-
layout: {
55-
...layer.layout,
56-
[`${layer.type}-sort-key`]: sortKey(lookup.dpri, layer),
57-
},
58-
source: "enc",
59-
"source-layer": lookup.obcl,
60-
id: [lookup.obcl, layer.type, lookup.rcid, i, j].join("-"),
61-
};
62-
});
63-
}),
92+
...lookupToLayers(fallbackLookup!).map((layer) => ({
93+
...layer,
94+
...("filter" in layer
95+
? {
96+
filter: filters.all(
97+
fallbackFilter,
98+
layer.filter as ExpressionFilterSpecification,
99+
),
100+
}
101+
: {}),
102+
})),
103+
...otherLookups.flatMap(lookupToLayers),
64104
];
65105
}
66106

107+
let i = 0;
108+
109+
export function lookupToLayers(lookup: LookupEntry): LayerSpecification[] {
110+
return instructionsToStyles(lookup.inst).map((layer) => {
111+
return {
112+
...layer,
113+
metadata: {
114+
s52: lookup,
115+
},
116+
filter: filters.all(
117+
filters.scaleFilter(),
118+
filterGeometryType[lookup.ftyp],
119+
...filters.attributeFilters(lookup.attc),
120+
...("filter" in layer
121+
? [layer.filter as ExpressionFilterSpecification]
122+
: []),
123+
),
124+
layout: {
125+
...layer.layout,
126+
[`${layer.type}-sort-key`]: sortKey(lookup.dpri, layer),
127+
},
128+
source: "enc",
129+
"source-layer": lookup.obcl,
130+
id: [i++, lookup.obcl, lookup.ftyp].join("-"),
131+
};
132+
});
133+
}
134+
67135
function background({ mode }: LayerConfig): BackgroundLayerSpecification {
68136
return {
69137
id: "background",

packages/styles/src/utils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Polyfill of Object.groupBy
2+
export function groupBy<T>(
3+
arr: T[],
4+
callback: (item: T, index: number, items: T[]) => string,
5+
): Record<string, T[]> {
6+
return arr.reduce((acc, ...args) => {
7+
const key = callback(...args);
8+
acc[key] ??= [];
9+
acc[key].push(args[0]);
10+
return acc;
11+
}, {});
12+
}

0 commit comments

Comments
 (0)