Fix comparison baseline for JP-field migration
This commit is contained in:
360
src/Domain/navsea-compare-legacy-domain-compatible.html
Normal file
360
src/Domain/navsea-compare-legacy-domain-compatible.html
Normal 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>
|
||||
Reference in New Issue
Block a user