Spaces:
Sleeping
Sleeping
File size: 15,927 Bytes
3613e77 0c571a4 3613e77 0c571a4 3613e77 cdff6a6 0c571a4 cdff6a6 0c571a4 cdff6a6 0c571a4 cdff6a6 0c571a4 cdff6a6 3613e77 cdff6a6 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 cdff6a6 3613e77 cdff6a6 0c571a4 3613e77 cdff6a6 0c571a4 3613e77 cdff6a6 3613e77 cdff6a6 0c571a4 3613e77 cdff6a6 0c571a4 cdff6a6 3613e77 cdff6a6 0c571a4 cdff6a6 0c571a4 cdff6a6 0c571a4 cdff6a6 0c571a4 eeeb56e 0c571a4 eeeb56e 0c571a4 eeeb56e cdff6a6 0c571a4 cdff6a6 0c571a4 cdff6a6 eeeb56e cdff6a6 0c571a4 cdff6a6 0c571a4 cdff6a6 0c571a4 cdff6a6 0c571a4 cdff6a6 0c571a4 3613e77 cdff6a6 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 0c571a4 3613e77 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | <!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SetVL Node Graph Viewer</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="topographic-grid" aria-hidden="true"></div>
<!-- 界面文字的中英两份都在 app.js 的 I18N 表里。标了 data-i18n* 的地方由
applyLang() 替换,这里写的中文只是脚本跑起来之前的初始值。 -->
<header class="topbar">
<div class="brand">
<div class="brand-mark" aria-hidden="true">SG</div>
<h1>Node Graph Viewer<em data-i18n="brand.readonly">SetVL · 只读</em></h1>
</div>
<!-- 全库读数:一行内用竖线断开,不再单独占一条 progress-strip -->
<div class="readout" role="group" aria-label="全库统计" data-i18n-aria-label="stats.aria">
<b id="statScenes">—</b><span data-i18n="stats.scenes">场景</span>
<i aria-hidden="true"></i>
<b id="statItems">—</b><span data-i18n="stats.nodes">节点</span>
<i aria-hidden="true"></i>
<b id="statQueue">—</b><span data-i18n="stats.edges">边</span>
<i aria-hidden="true"></i>
<span class="readout-origins" id="originBreakdown" title="边按 §1-§4 分类" data-i18n-title="stats.byOrigin">
<em class="o-corridor" data-i18n="origin.corridor"><b id="countCorridor">—</b>同走廊</em>
<em class="o-road" data-i18n="origin.road_route"><b id="countRoad">—</b>路网可达</em>
<em class="o-short" data-i18n="origin.short_link"><b id="countShort">—</b>短直连</em>
<em class="o-merge" data-i18n="origin.component_merge"><b id="countMerge">—</b>分量合并</em>
</span>
</div>
<div class="origin-meter" id="originMeter" aria-hidden="true"><span id="progressBar"></span></div>
<span class="readout-label" id="progressLabel">—</span>
<button class="icon-button" id="helpButton" title="帮助与快捷键" aria-label="帮助与快捷键"
data-i18n-title="help.title" data-i18n-aria-label="help.title">?</button>
<!-- 语言切换。两个按钮各用自己的语言写,不随界面翻译——不懂另一种语言的人也认得出 -->
<div class="lang-switch" role="group" aria-label="语言 / Language">
<button type="button" id="langZh" lang="zh-CN">中文</button>
<button type="button" id="langEn" lang="en">EN</button>
</div>
</header>
<main class="app-shell">
<!-- 01 队列 --------------------------------------------------------->
<aside class="queue-panel">
<div class="panel-heading">
<p class="section-index">01 / BROWSE</p>
<h2 id="panelTitle" data-i18n="panel.scenes">场景浏览</h2>
</div>
<div class="mode-switch" role="group" aria-label="列表模式" data-i18n-aria-label="mode.aria">
<button type="button" id="modeScenes" class="active" data-i18n="mode.scenes">全部场景</button>
<button type="button" id="modeQueue" data-i18n="mode.queue">带信号的</button>
</div>
<div class="filter-stack">
<!-- 一级:搜索。常驻,最宽,视觉重量最大。 -->
<div class="search-wrap">
<svg class="search-icon" viewBox="0 0 16 16" aria-hidden="true">
<circle cx="7" cy="7" r="4.5" fill="none" stroke="currentColor" stroke-width="1.4"/>
<path d="M10.4 10.4 L14 14" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
</svg>
<input id="searchInput" type="search" placeholder="scene_id 片段、城市…" data-i18n-placeholder="search.scenes">
</div>
<!-- 二级:最常用的两个,压成一行紧凑控件 -->
<div class="filter-row">
<label class="ctl"><span>City</span>
<select id="cityFilter"><option value="" data-i18n="opt.all">全部</option></select>
</label>
<label class="ctl"><span>Sort</span>
<select id="orderFilter">
<option value="scene_id">ID</option>
<option value="nodes_desc" data-i18n="order.nodesDesc">节点多→少</option>
<option value="nodes_asc" data-i18n="order.nodesAsc">节点少→多</option>
</select>
</label>
<button type="button" class="filter-toggle" id="filterToggle"
aria-expanded="false" aria-controls="filterDrawer" data-i18n="filter.toggle">
筛选<b class="filter-count" id="filterCount" hidden>0</b>
<svg viewBox="0 0 10 6" aria-hidden="true"><path d="M1 1l4 4 4-4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</button>
</div>
<!-- 已激活的筛选以 token 回显:抽屉收起时状态依然可见、可单独撤销 -->
<div class="token-row" id="activeTokens" hidden></div>
<!-- 三级:抽屉。默认收起,把纵向空间让给列表 -->
<div class="filter-drawer" id="filterDrawer" hidden>
<div class="drawer-group" id="sceneFilters">
<p class="drawer-title" data-i18n="drawer.scenes">场景</p>
<div class="filter-row">
<label class="ctl"><span>Min nodes</span>
<input id="minNodes" type="number" min="1" max="38" placeholder="—">
</label>
<label class="ctl"><span data-i18n="f.edited">状态</span>
<select id="editedFilter">
<option value="" data-i18n="opt.all">全部</option>
<option value="1" data-i18n="edited.yes">已人工改过</option>
<option value="0" data-i18n="edited.no">未改动</option>
</select>
</label>
</div>
<div class="chip-row" id="sceneChips">
<label class="chip"><input id="fOrphan" type="checkbox"><span data-i18n="chip.orphan">含孤儿节点</span></label>
<label class="chip"><input id="fNoted" type="checkbox"><span data-i18n="chip.noted">含带标记的边</span></label>
</div>
<div class="filter-row">
<label class="ctl"><span data-i18n="f.origin">含某类边</span>
<select id="originFilter">
<option value="" data-i18n="opt.all">全部</option>
<option value="corridor" data-i18n="sec.corridor">§1 同走廊</option>
<option value="road_route" data-i18n="sec.road_route">§2 路网可达</option>
<option value="short_link" data-i18n="sec.short_link">§3 短直连</option>
<option value="component_merge" data-i18n="sec.component_merge">§4 分量合并</option>
</select>
</label>
</div>
</div>
<div class="drawer-group" id="queueFilters" hidden>
<p class="drawer-title" data-i18n="drawer.queue">信号队列</p>
<div class="filter-row">
<label class="ctl"><span>Kind</span>
<select id="kindFilter">
<option value="" data-i18n="kind.all">节点 + 边</option>
<option value="node" data-i18n="kind.node">只看节点</option>
<option value="edge" data-i18n="kind.edge">只看边</option>
</select>
</label>
</div>
<div class="filter-row">
<label class="ctl"><span data-i18n="f.signal">标记</span>
<select id="signalFilter">
<option value="" data-i18n="sig.all">全部标记</option>
<option value="orphan" data-i18n="sig.orphan">§6 孤儿节点</option>
<option value="water_crossed" data-i18n="sig.water">§0 跨水</option>
<option value="building_crossed" data-i18n="sig.building">§0 穿建筑</option>
<option value="no_road_path" data-i18n="sig.noRoad">无道路通路</option>
<option value="long_detour" data-i18n="sig.detour">§4 红边绕远</option>
</select>
</label>
<label class="ctl"><span data-i18n="f.reviewed">已复核</span>
<select id="showReviewedSel">
<option value="" data-i18n="rev.exclude">不含</option>
<option value="1" data-i18n="rev.include">含已复核</option>
</select>
</label>
</div>
</div>
<button type="button" class="drawer-reset" id="filterReset" data-i18n="filter.reset">清空全部筛选</button>
</div>
</div>
<div class="list-meter"><span id="listCount">—</span></div>
<div class="queue-list" id="queueList" role="listbox" aria-label="Review queue"></div>
<div class="queue-footer">
<button class="quiet-button" id="pagePrev">←</button>
<button class="quiet-button" id="previousButton"><kbd>K</kbd> Prev</button>
<button class="quiet-button" id="nextButton">Next <kbd>J</kbd></button>
<button class="quiet-button" id="pageNext">→</button>
</div>
</aside>
<!-- 02 证据 --------------------------------------------------------->
<section class="evidence-panel">
<div class="scene-header">
<div>
<div class="scene-title-row">
<h2 id="sceneTitle" data-i18n="scene.none">未选择</h2>
<span class="scene-city" id="sceneCity">—</span>
</div>
<p class="scene-id" id="sceneId">—</p>
</div>
<div class="scene-metrics">
<div><span>Nodes</span><strong id="mNodes">—</strong></div>
<div><span>Edges</span><strong id="mEdges">—</strong></div>
<div><span>Corridors</span><strong id="mCorridors">—</strong></div>
<div><span>Orphans</span><strong id="mOrphans">—</strong></div>
</div>
</div>
<div class="evidence-grid">
<article class="image-card">
<div class="image-card-head">
<span class="image-kind">Scene Graph</span>
<div class="map-card-tools">
<div class="basemap-switch" role="group" aria-label="边的画法" data-i18n-aria-label="edgeStyle.aria">
<button type="button" id="edgeStraight" class="active" title="直线连接(默认,更醒目)" data-i18n-title="edgeStyle.straight">LINE</button>
<button type="button" id="edgeRoad" title="沿道路走线" data-i18n-title="edgeStyle.road">ROAD</button>
</div>
<div class="basemap-switch" role="group" aria-label="底图切换" data-i18n-aria-label="basemap.aria">
<button type="button" id="baseSat" class="active">SAT</button>
<button type="button" id="baseMap">MAP</button>
</div>
<div class="map-controls" aria-label="电子地图缩放" data-i18n-aria-label="mapZoom.aria">
<button type="button" id="mapZoomIn" title="放大" data-i18n-title="zoom.in">+</button>
<button type="button" id="mapZoomOut" title="缩小" data-i18n-title="zoom.out">−</button>
</div>
</div>
</div>
<div class="image-stage satellite-stage" id="satelliteStage">
<img id="satelliteImage" alt="satellite">
<div class="street-map" id="streetMap" aria-hidden="true">
<div class="map-tile-layer" id="mapTileLayer"></div>
</div>
<svg class="graph-svg" id="graphSvg" viewBox="0 0 400 400" preserveAspectRatio="none"></svg>
</div>
<div class="image-caption">
<span class="legend">
<span data-i18n="sec.corridor"><i class="lg-corridor"></i>§1 同走廊</span>
<span data-i18n="sec.road_route"><i class="lg-road"></i>§2 路网可达</span>
<span data-i18n="sec.short_link"><i class="lg-short"></i>§3 短直连</span>
<span data-i18n="sec.component_merge"><i class="lg-merge"></i>§4 分量合并</span>
<span data-i18n="legend.orphan"><i class="lg-orphan"></i>孤儿</span>
</span>
</div>
</article>
<div class="ground-stack">
<article class="image-card ground-card">
<div class="image-card-head compact">
<div class="node-label anchor-label" id="anchorBadge">—</div>
<span class="image-kind" id="anchorKind">Node</span>
</div>
<div class="ground-grid" id="anchorGrid" aria-label="四个朝向的街景" data-i18n-aria-label="ground.aria"></div>
<div class="image-caption mono" id="anchorCaption">—</div>
</article>
<article class="image-card ground-card" id="targetCard">
<div class="image-card-head compact">
<div class="node-label target-label" id="targetBadge">—</div>
<span class="image-kind">Target</span>
</div>
<div class="ground-grid" id="targetGrid" aria-label="四个朝向的街景" data-i18n-aria-label="ground.aria"></div>
<div class="image-caption mono" id="targetCaption">—</div>
</article>
</div>
</div>
<div class="context-rail">
<div>
<span class="image-kind">Scene Nodes</span>
<p data-i18n="rail.hint">点击可切换到该节点</p>
</div>
<div class="node-thumbnails" id="nodeThumbnails"></div>
<div class="corridor-legend" id="corridorLegend"></div>
</div>
<section class="graph-card" aria-labelledby="graphTitle">
<div class="graph-head">
<div>
<p class="section-index">SCENE ADJACENCY</p>
<h3 id="graphTitle" data-i18n="adj.title">节点邻接矩阵</h3>
</div>
<div class="graph-counts">
<span data-i18n="adj.corridor"><b id="gCorridor">—</b> 同走廊</span>
<span data-i18n="adj.road"><b id="gRoad">—</b> 路网</span>
<span data-i18n="adj.short"><b id="gShort">—</b> 直连</span>
<span data-i18n="adj.merge"><b id="gMerge">—</b> 合并</span>
</div>
</div>
<p class="graph-rule" data-i18n="adj.rule">每一格是一条无向边,颜色对应它的来源。点击格子或右侧列表可直接检查该条边。</p>
<div class="graph-body">
<div><table class="adjacency-table" id="adjacencyTable"></table></div>
<div class="graph-edge-list" id="edgeList"></div>
</div>
</section>
</section>
<!-- 03 判定 --------------------------------------------------------->
<aside class="inspector-panel">
<div class="panel-heading inspector-heading">
<div>
<p class="section-index">03 / PROPERTIES</p>
<h2 id="decisionTitle" data-i18n="insp.title">属性</h2>
</div>
<span class="relation-id" id="itemId">—</span>
</div>
<div id="inspectorBody"><p class="empty-note" data-i18n="insp.pick">从左侧队列选一条,或在图上点节点/边。</p></div>
</aside>
</main>
<dialog id="helpDialog">
<div class="dialog-head">
<h3 data-i18n="help.heading">快捷键</h3>
<button class="icon-button" id="helpClose" aria-label="关闭" data-i18n-aria-label="help.close">×</button>
</div>
<div class="dialog-body">
<div class="shortcut-grid">
<kbd>J</kbd><span data-i18n="help.j">队列下一条</span>
<kbd>K</kbd><span data-i18n="help.k">队列上一条</span>
<kbd>B</kbd><span data-i18n="help.b">切换卫星图 / 电子地图</span>
<kbd>E</kbd><span data-i18n="help.e">切换边的画法:沿路走线 / 直线</span>
<kbd>H</kbd><span data-i18n-html="help.h">把当前节点的四个朝向依次放大看(<kbd>Shift</kbd>+<kbd>H</kbd> 反向)</span>
<span class="keys"><kbd>[</kbd> <kbd>]</kbd></span><span data-i18n="help.page">列表上一页 / 下一页</span>
<kbd>Q</kbd><span data-i18n="help.q">切换「全部场景」/「带信号的」</span>
<kbd>?</kbd><span data-i18n="help.help">打开本帮助</span>
</div>
</div>
</dialog>
<div class="lightbox" id="lightbox"><img id="lightboxImage" alt=""></div>
<div id="toast" class="toast"></div>
<script src="app.js"></script>
</body>
</html>
|