Fix comparison baseline for JP-field migration

This commit is contained in:
OpenAI Codex
2026-03-31 15:47:09 +08:00
parent b566af331d
commit fc1bbb3673
3 changed files with 426 additions and 16 deletions

View File

@@ -0,0 +1,360 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>NavSea Fixed Compare Baseline</title>
<link href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css" rel="stylesheet" />
<style>
:root {
--bg: #f0f3ef;
--panel: rgba(248, 246, 241, 0.94);
--ink: #19252c;
--muted: #5f6d74;
--line: rgba(25, 37, 44, 0.14);
--accent: #85501d;
--accent-dark: #5f3812;
--shadow: 0 18px 36px rgba(25, 37, 44, 0.14);
}
* { box-sizing: border-box; }
html, body {
margin: 0;
height: 100%;
background:
radial-gradient(circle at top left, rgba(133, 80, 29, 0.12), transparent 30%),
radial-gradient(circle at bottom right, rgba(11, 108, 143, 0.10), transparent 28%),
var(--bg);
color: var(--ink);
font-family: "Avenir Next", "Segoe UI", "PingFang SC", "Noto Sans SC", sans-serif;
}
body { display: grid; grid-template-rows: auto 1fr; }
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 18px;
padding: 14px 18px 12px;
background: linear-gradient(180deg, rgba(248, 246, 241, 0.98), rgba(248, 246, 241, 0.88));
border-bottom: 1px solid var(--line);
box-shadow: var(--shadow);
z-index: 10;
flex-wrap: wrap;
}
.title { font-size: 18px; font-weight: 700; letter-spacing: 0.02em; }
.subtitle { margin-top: 4px; font-size: 13px; color: var(--muted); }
.toolbar-actions { display: flex; gap: 10px; align-items: center; }
button {
height: 42px; padding: 0 16px; border-radius: 12px;
border: 1px solid rgba(95, 56, 18, 0.18);
background: linear-gradient(180deg, var(--accent), var(--accent-dark));
color: #fff; font: inherit; font-weight: 700; cursor: pointer;
box-shadow: 0 10px 22px rgba(95, 56, 18, 0.18);
}
.layout { display: grid; grid-template-columns: 1fr 1fr; min-height: 0; }
.pane { position: relative; min-width: 0; border-right: 1px solid var(--line); }
.pane:last-child { border-right: 0; }
.map { position: absolute; inset: 0; }
.pane-tag {
position: absolute; top: 12px; left: 12px; z-index: 5; padding: 10px 12px;
border-radius: 12px; background: var(--panel);
border: 1px solid rgba(25, 37, 44, 0.08);
box-shadow: 0 12px 24px rgba(25, 37, 44, 0.12); backdrop-filter: blur(6px);
}
.pane-tag strong { display: block; font-size: 13px; margin-bottom: 3px; }
.pane-tag span { display: block; font-size: 12px; color: var(--muted); }
.status {
position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%);
z-index: 5; padding: 8px 12px; border-radius: 999px;
background: rgba(25, 37, 44, 0.84); color: #f7f9fb; font-size: 12px;
letter-spacing: 0.03em; max-width: calc(100% - 28px);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.layer-panel {
position: absolute;
top: 92px;
right: 12px;
z-index: 6;
width: 320px;
max-height: calc(100% - 128px);
overflow: auto;
padding: 12px;
border-radius: 14px;
background: rgba(248, 246, 241, 0.96);
border: 1px solid rgba(25, 37, 44, 0.1);
box-shadow: 0 12px 24px rgba(25, 37, 44, 0.14);
backdrop-filter: blur(8px);
}
.layer-panel h3 {
margin: 0 0 8px;
font-size: 14px;
}
.layer-panel p {
margin: 0 0 10px;
font-size: 12px;
color: var(--muted);
line-height: 1.45;
}
.layer-actions {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
.layer-actions button {
height: 34px;
padding: 0 10px;
border-radius: 10px;
font-size: 12px;
}
.layer-list {
display: grid;
gap: 6px;
}
.layer-item {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 7px 8px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.7);
}
.layer-item input {
margin-top: 2px;
}
.layer-meta {
min-width: 0;
}
.layer-id {
display: block;
font-size: 12px;
font-weight: 700;
line-height: 1.35;
word-break: break-word;
}
.layer-desc {
display: block;
font-size: 11px;
color: var(--muted);
line-height: 1.35;
word-break: break-word;
}
@media (max-width: 920px) {
.layout { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; }
.pane { border-right: 0; border-bottom: 1px solid var(--line); }
.pane:last-child { border-bottom: 0; }
.layer-panel {
width: calc(100% - 24px);
max-height: 42%;
left: 12px;
right: 12px;
}
}
</style>
</head>
<body>
<div class="toolbar">
<div>
<div class="title">NavSea Fixed Compare Baseline</div>
<div class="subtitle">这张页面现在是当前项目唯一固定使用的视觉对比页。左边固定加载原始样式 <code>style.patched.local.json</code>,右边固定加载 Domain compatible 样式 <code>style.domain-legacy-compatible-karatsu-10nm.json</code>,用于在 <code>pbf-domain-karatsu-10nm</code> 线上持续验证“去掉日文字段依赖,同时保持视觉尽量不变”。</div>
</div>
<div class="toolbar-actions">
<button id="reload-btn" type="button">重新加载</button>
</div>
</div>
<div class="layout">
<section class="pane">
<div class="pane-tag">
<strong>左侧</strong>
<span>原始样式 · style.patched.local.json</span>
</div>
<div id="map-left" class="map"></div>
</section>
<section class="pane">
<div class="pane-tag">
<strong>右侧</strong>
<span>固定基线 · Domain compatible / pbf-domain-karatsu-10nm</span>
</div>
<div id="map-right" class="map"></div>
<aside class="layer-panel">
<h3>右侧图层开关</h3>
<p>这里列出右侧 Domain 兼容样式的全部 `layer id`。你可以逐个开关,方便定位是哪一层造成差异。</p>
<div class="layer-actions">
<button id="layers-all-on" type="button">全开</button>
<button id="layers-all-off" type="button">全关</button>
</div>
<div id="layer-list" class="layer-list">样式加载后显示…</div>
</aside>
</section>
</div>
<div id="status" class="status">等待加载样式…</div>
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
<script>
const STYLE_URLS = {
left: '../style.patched.local.json',
right: './style.domain-legacy-compatible-karatsu-10nm.json'
};
const initialView = {
center: [129.9697, 33.4425],
zoom: 11,
pitch: 0,
bearing: 0
};
const statusEl = document.getElementById('status');
const reloadBtn = document.getElementById('reload-btn');
const layerListEl = document.getElementById('layer-list');
const layersAllOnBtn = document.getElementById('layers-all-on');
const layersAllOffBtn = document.getElementById('layers-all-off');
const maps = { left: null, right: null };
let syncing = false;
function setStatus(text) { statusEl.textContent = text; }
function isToggleableLayer(layer) {
return layer && layer.id && layer.type !== 'background';
}
function getVisibility(map, layerId) {
return map.getLayoutProperty(layerId, 'visibility') !== 'none';
}
function setVisibility(map, layerId, visible) {
map.setLayoutProperty(layerId, 'visibility', visible ? 'visible' : 'none');
}
function renderLayerPanel() {
const map = maps.right;
if (!map || !map.isStyleLoaded()) {
layerListEl.textContent = '样式加载后显示…';
return;
}
const style = map.getStyle();
const toggleableLayers = (style.layers || []).filter(isToggleableLayer);
layerListEl.innerHTML = '';
toggleableLayers.forEach((layer) => {
const item = document.createElement('label');
item.className = 'layer-item';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = getVisibility(map, layer.id);
checkbox.addEventListener('change', () => {
setVisibility(map, layer.id, checkbox.checked);
});
const meta = document.createElement('span');
meta.className = 'layer-meta';
const title = document.createElement('span');
title.className = 'layer-id';
title.textContent = layer.id;
const desc = document.createElement('span');
desc.className = 'layer-desc';
const src = layer.source || 'no-source';
const srcLayer = layer['source-layer'] || 'no-source-layer';
desc.textContent = `${layer.type} · ${src} · ${srcLayer}`;
meta.appendChild(title);
meta.appendChild(desc);
item.appendChild(checkbox);
item.appendChild(meta);
layerListEl.appendChild(item);
});
}
function setAllRightLayers(visible) {
const map = maps.right;
if (!map || !map.isStyleLoaded()) return;
const style = map.getStyle();
(style.layers || []).filter(isToggleableLayer).forEach((layer) => {
setVisibility(map, layer.id, visible);
});
renderLayerPanel();
}
async function fetchStyle(url) {
const response = await fetch(url, { cache: 'no-store' });
if (!response.ok) throw new Error(`加载样式失败: ${url}`);
return response.json();
}
function syncMap(sourceMap, targetMap) {
if (syncing || !targetMap) return;
syncing = true;
targetMap.jumpTo({
center: sourceMap.getCenter(),
zoom: sourceMap.getZoom(),
bearing: sourceMap.getBearing(),
pitch: sourceMap.getPitch()
});
syncing = false;
}
function attachSync(map, otherKey) {
['move', 'zoom', 'rotate', 'pitch'].forEach((eventName) => {
map.on(eventName, () => syncMap(map, maps[otherKey]));
});
}
async function applyStyle(side) {
const style = await fetchStyle(STYLE_URLS[side]);
const map = maps[side];
const camera = map ? {
center: map.getCenter(),
zoom: map.getZoom(),
bearing: map.getBearing(),
pitch: map.getPitch()
} : initialView;
if (!map) {
maps[side] = new maplibregl.Map({
container: side === 'left' ? 'map-left' : 'map-right',
style,
center: camera.center,
zoom: camera.zoom,
bearing: camera.bearing,
pitch: camera.pitch
});
maps[side].addControl(new maplibregl.NavigationControl(), 'top-right');
attachSync(maps[side], side === 'left' ? 'right' : 'left');
if (side === 'right') {
maps[side].on('style.load', renderLayerPanel);
}
} else {
map.setStyle(style, { diff: false });
map.once('style.load', () => {
map.jumpTo(camera);
if (side === 'right') renderLayerPanel();
});
}
}
async function reloadStyles() {
setStatus('正在加载左右样式…');
reloadBtn.disabled = true;
try {
await Promise.all([applyStyle('left'), applyStyle('right')]);
setStatus('样式已加载。拖动任一侧,另一侧会跟随。');
} catch (error) {
console.error(error);
setStatus(error.message || '样式加载失败');
} finally {
reloadBtn.disabled = false;
}
}
reloadBtn.addEventListener('click', reloadStyles);
layersAllOnBtn.addEventListener('click', () => setAllRightLayers(true));
layersAllOffBtn.addEventListener('click', () => setAllRightLayers(false));
reloadStyles();
</script>
</body>
</html>