hoi4-agent-tools 2.5.3 → 2.7.0
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/CHANGELOG.md +13 -0
- package/README.md +5 -5
- package/dist/hoi4_agent_tools/gui/assets.js +15 -3
- package/dist/hoi4_agent_tools/gui/layout.js +270 -75
- package/dist/hoi4_agent_tools/gui/renderer.js +185 -22
- package/dist/hoi4_agent_tools/gui/source-graph.js +40 -11
- package/dist/hoi4_agent_tools/gui/studio.js +35 -7
- package/dist/hoi4_agent_tools/map/catalog.js +398 -0
- package/dist/hoi4_agent_tools/map/index.js +1 -0
- package/dist/hoi4_agent_tools/map/operations.js +337 -30
- package/dist/hoi4_agent_tools/map/render.js +40 -147
- package/dist/hoi4_agent_tools/map/service.js +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js +85 -9
- package/dist/hoi4_agent_tools/schemas/map.js +69 -2
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/gui.md +5 -1
- package/docs/map.md +60 -23
- package/package.json +1 -1
- package/schemas/configuration.schema.json +1 -1
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/custom-weighted-pool.schema.json +1 -1
- package/schemas/event-feature-manifest.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +1 -1
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/map-operation.schema.json +228 -74
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/probability-analysis-result.schema.json +1 -1
- package/schemas/probability-compare-input.schema.json +1 -1
- package/schemas/probability-evaluate-input.schema.json +1 -1
- package/schemas/probability-inspect-input.schema.json +1 -1
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +1 -1
- package/schemas/probability-sequence-input.schema.json +1 -1
- package/schemas/probability-simulate-input.schema.json +1 -1
- package/schemas/probability-sweep-input.schema.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { compareCodeUnits } from '../core/canonical.js';
|
|
2
|
+
import { nodeLocation } from '../core/source/parser.js';
|
|
3
|
+
function localisedValues(entries) {
|
|
4
|
+
return entries
|
|
5
|
+
.map(({ file, entry }) => ({
|
|
6
|
+
language: entry.language,
|
|
7
|
+
value: entry.value,
|
|
8
|
+
sourcePath: file.displayPath,
|
|
9
|
+
line: entry.line,
|
|
10
|
+
loadOrder: file.loadOrder,
|
|
11
|
+
}))
|
|
12
|
+
.sort((left, right) => compareCodeUnits(left.language, right.language) ||
|
|
13
|
+
left.loadOrder - right.loadOrder ||
|
|
14
|
+
compareCodeUnits(left.sourcePath, right.sourcePath) ||
|
|
15
|
+
left.line - right.line)
|
|
16
|
+
.map(({ loadOrder: _loadOrder, ...value }) => value);
|
|
17
|
+
}
|
|
18
|
+
function resolvedName(entriesByKey, keys, fallback) {
|
|
19
|
+
for (const key of keys) {
|
|
20
|
+
const values = localisedValues(entriesByKey.get(key) ?? []);
|
|
21
|
+
if (values.length === 0)
|
|
22
|
+
continue;
|
|
23
|
+
const english = values.filter(({ language }) => language === 'l_english').at(-1);
|
|
24
|
+
return { key, displayName: english?.value ?? values.at(-1)?.value ?? fallback, values };
|
|
25
|
+
}
|
|
26
|
+
const key = keys[0] ?? fallback;
|
|
27
|
+
return { key, displayName: fallback, values: [] };
|
|
28
|
+
}
|
|
29
|
+
function aggregateGeometry(index, provinceIds) {
|
|
30
|
+
const geometries = provinceIds.flatMap((id) => {
|
|
31
|
+
const geometry = index.raster?.geometry.get(id);
|
|
32
|
+
return geometry === undefined ? [] : [geometry];
|
|
33
|
+
});
|
|
34
|
+
if (geometries.length === 0 || index.raster === undefined)
|
|
35
|
+
return null;
|
|
36
|
+
const pixelCount = geometries.reduce((total, { pixelCount: count }) => total + count, 0);
|
|
37
|
+
if (pixelCount <= 0)
|
|
38
|
+
return null;
|
|
39
|
+
const centerX = geometries.reduce((total, geometry) => total + geometry.centerX * geometry.pixelCount, 0) /
|
|
40
|
+
pixelCount;
|
|
41
|
+
const centerY = geometries.reduce((total, geometry) => total + geometry.centerY * geometry.pixelCount, 0) /
|
|
42
|
+
pixelCount;
|
|
43
|
+
return {
|
|
44
|
+
pixelCount,
|
|
45
|
+
minX: geometries.reduce((value, geometry) => Math.min(value, geometry.minX), Infinity),
|
|
46
|
+
minY: geometries.reduce((value, geometry) => Math.min(value, geometry.minY), Infinity),
|
|
47
|
+
maxX: geometries.reduce((value, geometry) => Math.max(value, geometry.maxX), -Infinity),
|
|
48
|
+
maxY: geometries.reduce((value, geometry) => Math.max(value, geometry.maxY), -Infinity),
|
|
49
|
+
centerX,
|
|
50
|
+
centerY,
|
|
51
|
+
mapX: centerX,
|
|
52
|
+
mapZ: index.raster.height - 1 - centerY,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function sortedObject(values) {
|
|
56
|
+
return Object.fromEntries([...values].sort(([left], [right]) => compareCodeUnits(left, right)));
|
|
57
|
+
}
|
|
58
|
+
export function buildMapCatalog(index) {
|
|
59
|
+
const raster = index.raster;
|
|
60
|
+
const width = raster?.width ?? 0;
|
|
61
|
+
const height = raster?.height ?? 0;
|
|
62
|
+
const localisationEntriesByKey = new Map();
|
|
63
|
+
for (const entry of index.localisationEntries) {
|
|
64
|
+
const values = localisationEntriesByKey.get(entry.entry.key) ?? [];
|
|
65
|
+
values.push(entry);
|
|
66
|
+
localisationEntriesByKey.set(entry.entry.key, values);
|
|
67
|
+
}
|
|
68
|
+
const portsByProvince = new Map(index.ports.map((port) => [port.provinceId, port]));
|
|
69
|
+
const supplyNodeLevelsByProvince = new Map();
|
|
70
|
+
for (const node of index.supplyNodes) {
|
|
71
|
+
const levels = supplyNodeLevelsByProvince.get(node.provinceId) ?? [];
|
|
72
|
+
levels.push(node.level);
|
|
73
|
+
supplyNodeLevelsByProvince.set(node.provinceId, levels);
|
|
74
|
+
}
|
|
75
|
+
const buildingPositionsByProvince = new Map();
|
|
76
|
+
for (const [ordinal, position] of index.buildingPositions.entries()) {
|
|
77
|
+
const provinceId = index.provinceAtMapCoordinate(position.x, position.z);
|
|
78
|
+
if (provinceId === undefined)
|
|
79
|
+
continue;
|
|
80
|
+
const ordinals = buildingPositionsByProvince.get(provinceId) ?? [];
|
|
81
|
+
ordinals.push(ordinal);
|
|
82
|
+
buildingPositionsByProvince.set(provinceId, ordinals);
|
|
83
|
+
}
|
|
84
|
+
const unitPositionsByProvince = new Map();
|
|
85
|
+
for (const [ordinal, position] of index.unitPositions.entries()) {
|
|
86
|
+
const ordinals = unitPositionsByProvince.get(position.provinceId) ?? [];
|
|
87
|
+
ordinals.push(ordinal);
|
|
88
|
+
unitPositionsByProvince.set(position.provinceId, ordinals);
|
|
89
|
+
}
|
|
90
|
+
const railwayOrdinalsByProvince = new Map();
|
|
91
|
+
for (const [ordinal, railway] of index.railways.entries()) {
|
|
92
|
+
for (const provinceId of new Set(railway.provinces)) {
|
|
93
|
+
const ordinals = railwayOrdinalsByProvince.get(provinceId) ?? [];
|
|
94
|
+
ordinals.push(ordinal);
|
|
95
|
+
railwayOrdinalsByProvince.set(provinceId, ordinals);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const specialAdjacencyOrdinalsByProvince = new Map();
|
|
99
|
+
for (const [ordinal, adjacency] of index.adjacencies.entries()) {
|
|
100
|
+
for (const provinceId of new Set([adjacency.from, adjacency.to, adjacency.through])) {
|
|
101
|
+
if (provinceId < 0)
|
|
102
|
+
continue;
|
|
103
|
+
const ordinals = specialAdjacencyOrdinalsByProvince.get(provinceId) ?? [];
|
|
104
|
+
ordinals.push(ordinal);
|
|
105
|
+
specialAdjacencyOrdinalsByProvince.set(provinceId, ordinals);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const provinces = [...index.definitions]
|
|
109
|
+
.sort((left, right) => left.id - right.id)
|
|
110
|
+
.map((definition) => {
|
|
111
|
+
const id = definition.id;
|
|
112
|
+
const port = portsByProvince.get(id);
|
|
113
|
+
return {
|
|
114
|
+
kind: 'province',
|
|
115
|
+
id,
|
|
116
|
+
name: resolvedName(localisationEntriesByKey, [`PROV${id}`, `VICTORY_POINTS_${id}`], `Province ${id}`),
|
|
117
|
+
definition: {
|
|
118
|
+
color: definition.color,
|
|
119
|
+
type: definition.type,
|
|
120
|
+
coastal: definition.coastal,
|
|
121
|
+
terrain: definition.terrain,
|
|
122
|
+
continent: definition.continent,
|
|
123
|
+
sourcePath: definition.document.file.displayPath,
|
|
124
|
+
line: definition.line,
|
|
125
|
+
},
|
|
126
|
+
geometry: aggregateGeometry(index, [id]),
|
|
127
|
+
stateIds: index.stateForProvince(id).map(({ id: stateId }) => stateId),
|
|
128
|
+
regionIds: index.regionForProvince(id).map(({ id: regionId }) => regionId),
|
|
129
|
+
neighborProvinceIds: [...(raster?.adjacency.get(id) ?? [])].sort((a, b) => a - b),
|
|
130
|
+
victoryPoints: index.victoryPointsByProvince
|
|
131
|
+
.get(id)
|
|
132
|
+
?.map(({ stateId, value }) => ({ stateId, value })) ?? [],
|
|
133
|
+
provinceBuildings: index.provinceBuildingsByProvince.get(id)?.map(({ stateId, buildings }) => ({
|
|
134
|
+
stateId,
|
|
135
|
+
buildings: sortedObject(buildings),
|
|
136
|
+
})) ?? [],
|
|
137
|
+
port: port === undefined
|
|
138
|
+
? null
|
|
139
|
+
: {
|
|
140
|
+
stateId: port.stateId,
|
|
141
|
+
level: port.level,
|
|
142
|
+
coastal: port.coastal,
|
|
143
|
+
adjacentSeaProvinceIds: [...port.adjacentSeaProvinceIds],
|
|
144
|
+
},
|
|
145
|
+
supplyNodeLevels: supplyNodeLevelsByProvince.get(id) ?? [],
|
|
146
|
+
railwayOrdinals: railwayOrdinalsByProvince.get(id) ?? [],
|
|
147
|
+
specialAdjacencyOrdinals: specialAdjacencyOrdinalsByProvince.get(id) ?? [],
|
|
148
|
+
buildingPositionOrdinals: buildingPositionsByProvince.get(id) ?? [],
|
|
149
|
+
unitPositionOrdinals: unitPositionsByProvince.get(id) ?? [],
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
const states = [...index.states]
|
|
153
|
+
.sort((left, right) => left.id - right.id)
|
|
154
|
+
.map((state) => ({
|
|
155
|
+
kind: 'state',
|
|
156
|
+
id: state.id,
|
|
157
|
+
name: resolvedName(localisationEntriesByKey, [state.name], state.name),
|
|
158
|
+
geometry: aggregateGeometry(index, state.provinces),
|
|
159
|
+
capital: state.capital ?? null,
|
|
160
|
+
manpower: state.manpower,
|
|
161
|
+
category: state.category,
|
|
162
|
+
provinceIds: [...state.provinces].sort((a, b) => a - b),
|
|
163
|
+
regionIds: [
|
|
164
|
+
...new Set(state.provinces.flatMap((provinceId) => index.regionForProvince(provinceId).map(({ id }) => id))),
|
|
165
|
+
].sort((a, b) => a - b),
|
|
166
|
+
resources: sortedObject(state.resources),
|
|
167
|
+
owner: state.owner ?? null,
|
|
168
|
+
controller: state.controller ?? null,
|
|
169
|
+
cores: [...state.cores].sort(compareCodeUnits),
|
|
170
|
+
claims: [...state.claims].sort(compareCodeUnits),
|
|
171
|
+
victoryPoints: state.victoryPoints
|
|
172
|
+
.map(({ provinceId, value }) => ({ provinceId, value }))
|
|
173
|
+
.sort((left, right) => left.provinceId - right.provinceId || left.value - right.value),
|
|
174
|
+
stateBuildings: sortedObject(state.stateBuildings),
|
|
175
|
+
provinceBuildings: Object.fromEntries([...state.provinceBuildings]
|
|
176
|
+
.sort(([left], [right]) => left - right)
|
|
177
|
+
.map(([provinceId, buildings]) => [String(provinceId), sortedObject(buildings)])),
|
|
178
|
+
sourcePath: state.file.displayPath,
|
|
179
|
+
sourceLine: nodeLocation(state.document, state.assignment).start.line,
|
|
180
|
+
}));
|
|
181
|
+
const strategicRegions = [...index.regions]
|
|
182
|
+
.sort((left, right) => left.id - right.id)
|
|
183
|
+
.map((region) => ({
|
|
184
|
+
kind: 'strategic-region',
|
|
185
|
+
id: region.id,
|
|
186
|
+
name: resolvedName(localisationEntriesByKey, [region.name], region.name),
|
|
187
|
+
geometry: aggregateGeometry(index, region.provinces),
|
|
188
|
+
provinceIds: [...region.provinces].sort((a, b) => a - b),
|
|
189
|
+
stateIds: [
|
|
190
|
+
...new Set(region.provinces.flatMap((provinceId) => index.stateForProvince(provinceId).map(({ id }) => id))),
|
|
191
|
+
].sort((a, b) => a - b),
|
|
192
|
+
navalTerrain: region.navalTerrain ?? null,
|
|
193
|
+
sourcePath: region.file.displayPath,
|
|
194
|
+
sourceLine: nodeLocation(region.document, region.assignment).start.line,
|
|
195
|
+
}));
|
|
196
|
+
const normalAdjacencies = [...(raster?.adjacency ?? [])]
|
|
197
|
+
.flatMap(([from, neighbors]) => [...neighbors].flatMap((to) => (from < to ? [{ from, to }] : [])))
|
|
198
|
+
.sort((left, right) => left.from - right.from || left.to - right.to);
|
|
199
|
+
return {
|
|
200
|
+
schemaVersion: 2,
|
|
201
|
+
dimensions: { width, height },
|
|
202
|
+
coordinateSystems: {
|
|
203
|
+
pixel: { origin: 'top-left', xDirection: 'right', yDirection: 'down' },
|
|
204
|
+
map: { origin: 'bottom-left', xDirection: 'right', zDirection: 'up' },
|
|
205
|
+
},
|
|
206
|
+
counts: {
|
|
207
|
+
provinces: provinces.length,
|
|
208
|
+
states: states.length,
|
|
209
|
+
strategicRegions: strategicRegions.length,
|
|
210
|
+
normalAdjacencies: normalAdjacencies.length,
|
|
211
|
+
specialAdjacencies: index.adjacencies.length,
|
|
212
|
+
supplyNodes: index.supplyNodes.length,
|
|
213
|
+
railways: index.railways.length,
|
|
214
|
+
ports: index.ports.length,
|
|
215
|
+
victoryPoints: [...index.victoryPointsByProvince.values()].reduce((count, entries) => count + entries.length, 0),
|
|
216
|
+
buildingPositions: index.buildingPositions.length,
|
|
217
|
+
unitPositions: index.unitPositions.length,
|
|
218
|
+
weatherPositions: index.weatherPositions.length,
|
|
219
|
+
entityLocators: index.entityLocators.length,
|
|
220
|
+
},
|
|
221
|
+
provinces,
|
|
222
|
+
states,
|
|
223
|
+
strategicRegions,
|
|
224
|
+
normalAdjacencies,
|
|
225
|
+
specialAdjacencies: index.adjacencies.map((adjacency, ordinal) => ({
|
|
226
|
+
ordinal,
|
|
227
|
+
from: adjacency.from,
|
|
228
|
+
to: adjacency.to,
|
|
229
|
+
type: adjacency.type,
|
|
230
|
+
through: adjacency.through,
|
|
231
|
+
startX: adjacency.startX,
|
|
232
|
+
startY: adjacency.startY,
|
|
233
|
+
stopX: adjacency.stopX,
|
|
234
|
+
stopY: adjacency.stopY,
|
|
235
|
+
rule: adjacency.rule,
|
|
236
|
+
comment: adjacency.comment,
|
|
237
|
+
sourcePath: adjacency.document.file.displayPath,
|
|
238
|
+
line: adjacency.line,
|
|
239
|
+
})),
|
|
240
|
+
supplyNodes: index.supplyNodes.map((node, ordinal) => ({
|
|
241
|
+
ordinal,
|
|
242
|
+
level: node.level,
|
|
243
|
+
provinceId: node.provinceId,
|
|
244
|
+
sourcePath: node.document.file.displayPath,
|
|
245
|
+
line: node.line,
|
|
246
|
+
})),
|
|
247
|
+
railways: index.railways.map((railway, ordinal) => ({
|
|
248
|
+
ordinal,
|
|
249
|
+
level: railway.level,
|
|
250
|
+
declaredCount: railway.declaredCount,
|
|
251
|
+
provinceIds: [...railway.provinces],
|
|
252
|
+
sourcePath: railway.document.file.displayPath,
|
|
253
|
+
line: railway.line,
|
|
254
|
+
})),
|
|
255
|
+
ports: index.ports.map(({ stateId, provinceId, level, coastal, adjacentSeaProvinceIds }) => ({
|
|
256
|
+
stateId,
|
|
257
|
+
provinceId,
|
|
258
|
+
level,
|
|
259
|
+
coastal,
|
|
260
|
+
adjacentSeaProvinceIds: [...adjacentSeaProvinceIds].sort((left, right) => left - right),
|
|
261
|
+
})),
|
|
262
|
+
victoryPoints: [...index.victoryPointsByProvince.values()]
|
|
263
|
+
.flatMap((entries) => entries.map(({ stateId, provinceId, value }) => ({ stateId, provinceId, value })))
|
|
264
|
+
.sort((left, right) => left.stateId - right.stateId ||
|
|
265
|
+
left.provinceId - right.provinceId ||
|
|
266
|
+
left.value - right.value),
|
|
267
|
+
buildingPositions: index.buildingPositions.map((position, ordinal) => ({
|
|
268
|
+
ordinal,
|
|
269
|
+
stateId: position.stateId,
|
|
270
|
+
building: position.building,
|
|
271
|
+
x: position.x,
|
|
272
|
+
y: position.y,
|
|
273
|
+
z: position.z,
|
|
274
|
+
rotation: position.rotation,
|
|
275
|
+
adjacentSeaProvince: position.adjacentSeaProvince,
|
|
276
|
+
sourcePath: position.document.file.displayPath,
|
|
277
|
+
line: position.line,
|
|
278
|
+
})),
|
|
279
|
+
unitPositions: index.unitPositions.map((position, ordinal) => ({
|
|
280
|
+
ordinal,
|
|
281
|
+
provinceId: position.provinceId,
|
|
282
|
+
type: position.type,
|
|
283
|
+
x: position.x,
|
|
284
|
+
y: position.y,
|
|
285
|
+
z: position.z,
|
|
286
|
+
rotation: position.rotation,
|
|
287
|
+
offset: position.offset,
|
|
288
|
+
sourcePath: position.document.file.displayPath,
|
|
289
|
+
line: position.line,
|
|
290
|
+
})),
|
|
291
|
+
weatherPositions: index.weatherPositions.map((position, ordinal) => ({
|
|
292
|
+
ordinal,
|
|
293
|
+
strategicRegionId: position.strategicRegionId,
|
|
294
|
+
x: position.x,
|
|
295
|
+
y: position.y,
|
|
296
|
+
z: position.z,
|
|
297
|
+
size: position.size,
|
|
298
|
+
sourcePath: position.document.file.displayPath,
|
|
299
|
+
line: position.line,
|
|
300
|
+
})),
|
|
301
|
+
entityLocators: [...index.entityLocators]
|
|
302
|
+
.sort((left, right) => compareCodeUnits(left.entity, right.entity) || compareCodeUnits(left.name, right.name))
|
|
303
|
+
.map(({ entity, name, position, file }) => ({
|
|
304
|
+
entity,
|
|
305
|
+
name,
|
|
306
|
+
position: [...position],
|
|
307
|
+
sourcePath: file.displayPath,
|
|
308
|
+
})),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function searchScore(query, id, key, displayName, alternatives) {
|
|
312
|
+
const idText = String(id);
|
|
313
|
+
const normalizedKey = key.toLocaleLowerCase('en-US');
|
|
314
|
+
const normalizedName = displayName.toLocaleLowerCase('en-US');
|
|
315
|
+
const normalizedAlternatives = alternatives.map((value) => value.toLocaleLowerCase('en-US'));
|
|
316
|
+
if (query === idText ||
|
|
317
|
+
query === normalizedKey ||
|
|
318
|
+
query === normalizedName ||
|
|
319
|
+
normalizedAlternatives.includes(query))
|
|
320
|
+
return 0;
|
|
321
|
+
if (idText.startsWith(query) ||
|
|
322
|
+
normalizedKey.startsWith(query) ||
|
|
323
|
+
normalizedName.startsWith(query) ||
|
|
324
|
+
normalizedAlternatives.some((value) => value.startsWith(query)))
|
|
325
|
+
return 1;
|
|
326
|
+
if (normalizedKey.includes(query) ||
|
|
327
|
+
normalizedName.includes(query) ||
|
|
328
|
+
normalizedAlternatives.some((value) => value.includes(query)))
|
|
329
|
+
return 2;
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
export function searchMapCatalog(catalog, query, limit = 100) {
|
|
333
|
+
const normalized = query.trim().toLocaleLowerCase('en-US');
|
|
334
|
+
if (normalized === '')
|
|
335
|
+
return [];
|
|
336
|
+
const entries = [
|
|
337
|
+
...catalog.provinces.map(({ id, name, geometry }) => ({
|
|
338
|
+
kind: 'province',
|
|
339
|
+
id,
|
|
340
|
+
key: name.key,
|
|
341
|
+
displayName: name.displayName,
|
|
342
|
+
alternatives: name.values.map(({ value }) => value),
|
|
343
|
+
geometry,
|
|
344
|
+
})),
|
|
345
|
+
...catalog.states.map(({ id, name, geometry }) => ({
|
|
346
|
+
kind: 'state',
|
|
347
|
+
id,
|
|
348
|
+
key: name.key,
|
|
349
|
+
displayName: name.displayName,
|
|
350
|
+
alternatives: name.values.map(({ value }) => value),
|
|
351
|
+
geometry,
|
|
352
|
+
})),
|
|
353
|
+
...catalog.strategicRegions.map(({ id, name, geometry }) => ({
|
|
354
|
+
kind: 'strategic-region',
|
|
355
|
+
id,
|
|
356
|
+
key: name.key,
|
|
357
|
+
displayName: name.displayName,
|
|
358
|
+
alternatives: name.values.map(({ value }) => value),
|
|
359
|
+
geometry,
|
|
360
|
+
})),
|
|
361
|
+
];
|
|
362
|
+
return entries
|
|
363
|
+
.flatMap((entry) => {
|
|
364
|
+
const score = searchScore(normalized, entry.id, entry.key, entry.displayName, entry.alternatives);
|
|
365
|
+
if (score === undefined)
|
|
366
|
+
return [];
|
|
367
|
+
const { alternatives: _alternatives, ...result } = entry;
|
|
368
|
+
return [{ ...result, score }];
|
|
369
|
+
})
|
|
370
|
+
.sort((left, right) => left.score - right.score || compareCodeUnits(left.kind, right.kind) || left.id - right.id)
|
|
371
|
+
.slice(0, Math.max(0, limit));
|
|
372
|
+
}
|
|
373
|
+
export function lookupMapCoordinate(index, coordinate) {
|
|
374
|
+
const raster = index.raster;
|
|
375
|
+
const pixel = coordinate.kind === 'pixel'
|
|
376
|
+
? { x: Math.floor(coordinate.x), y: Math.floor(coordinate.y) }
|
|
377
|
+
: raster === undefined
|
|
378
|
+
? null
|
|
379
|
+
: { x: Math.floor(coordinate.x), y: raster.height - 1 - Math.floor(coordinate.z) };
|
|
380
|
+
if (raster === undefined ||
|
|
381
|
+
pixel === null ||
|
|
382
|
+
pixel.x < 0 ||
|
|
383
|
+
pixel.y < 0 ||
|
|
384
|
+
pixel.x >= raster.width ||
|
|
385
|
+
pixel.y >= raster.height) {
|
|
386
|
+
return { coordinate, pixel: null, provinceId: null, stateIds: [], regionIds: [] };
|
|
387
|
+
}
|
|
388
|
+
const provinceId = raster.provinceIds[pixel.y * raster.width + pixel.x] ?? -1;
|
|
389
|
+
if (provinceId < 0)
|
|
390
|
+
return { coordinate, pixel, provinceId: null, stateIds: [], regionIds: [] };
|
|
391
|
+
return {
|
|
392
|
+
coordinate,
|
|
393
|
+
pixel,
|
|
394
|
+
provinceId,
|
|
395
|
+
stateIds: index.stateForProvince(provinceId).map(({ id }) => id),
|
|
396
|
+
regionIds: index.regionForProvince(provinceId).map(({ id }) => id),
|
|
397
|
+
};
|
|
398
|
+
}
|