176 lines
5.8 KiB
HTML
176 lines
5.8 KiB
HTML
|
|
<!doctype html>
|
|||
|
|
<html lang="zh-CN">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="utf-8" />
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|||
|
|
<title>Task 3 Fig.1 (CartoDB + Leaflet)</title>
|
|||
|
|
<link
|
|||
|
|
rel="stylesheet"
|
|||
|
|
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
|||
|
|
crossorigin=""
|
|||
|
|
/>
|
|||
|
|
<style>
|
|||
|
|
html,
|
|||
|
|
body,
|
|||
|
|
#map {
|
|||
|
|
height: 100%;
|
|||
|
|
margin: 0;
|
|||
|
|
}
|
|||
|
|
.legend {
|
|||
|
|
background: rgba(255, 249, 240, 0.95);
|
|||
|
|
padding: 10px 12px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.15);
|
|||
|
|
font: 12px/1.3 system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
|
|||
|
|
border: 1px solid rgba(188, 108, 37, 0.25);
|
|||
|
|
}
|
|||
|
|
.legend .title {
|
|||
|
|
font-weight: 700;
|
|||
|
|
margin-bottom: 6px;
|
|||
|
|
}
|
|||
|
|
.legend .row {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
gap: 12px;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
.legend .swatch {
|
|||
|
|
width: 12px;
|
|||
|
|
height: 12px;
|
|||
|
|
border-radius: 10px;
|
|||
|
|
display: inline-block;
|
|||
|
|
margin-right: 6px;
|
|||
|
|
border: 1px solid rgba(255, 255, 255, 0.95);
|
|||
|
|
vertical-align: -1px;
|
|||
|
|
}
|
|||
|
|
.legend .muted {
|
|||
|
|
color: rgba(0, 0, 0, 0.65);
|
|||
|
|
margin-top: 6px;
|
|||
|
|
}
|
|||
|
|
.legend .line {
|
|||
|
|
height: 2px;
|
|||
|
|
background: rgba(188, 108, 37, 0.75);
|
|||
|
|
border-radius: 2px;
|
|||
|
|
margin-top: 6px;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<div id="map"></div>
|
|||
|
|
|
|||
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" crossorigin=""></script>
|
|||
|
|
<script src="./fig1_points.js"></script>
|
|||
|
|
<script>
|
|||
|
|
const points = window.FIG1_POINTS || [];
|
|||
|
|
const links = window.FIG1_LINKS || [];
|
|||
|
|
if (!points.length) {
|
|||
|
|
alert("FIG1_POINTS 为空:请先运行 task3/08_visualize.py 生成 fig1_points.js,或确认该文件与本页面同目录。");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const map = L.map("map", { preferCanvas: true });
|
|||
|
|
|
|||
|
|
// CartoDB basemap (Positron)
|
|||
|
|
L.tileLayer("https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", {
|
|||
|
|
subdomains: "abcd",
|
|||
|
|
maxZoom: 20,
|
|||
|
|
attribution:
|
|||
|
|
'© <a href="https://www.openstreetmap.org/copyright">OSM</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
|||
|
|
}).addTo(map);
|
|||
|
|
|
|||
|
|
const byId = new Map(points.map((p) => [p.site_id, p]));
|
|||
|
|
const muValues = points.map((p) => p.mu);
|
|||
|
|
const muMin = Math.min(...muValues);
|
|||
|
|
const muMax = Math.max(...muValues);
|
|||
|
|
|
|||
|
|
const COLORS = {
|
|||
|
|
// warmer + higher contrast
|
|||
|
|
paired: "#e76f51",
|
|||
|
|
unpaired: "#6c757d",
|
|||
|
|
link: "#bc6c25",
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
function clamp01(x) {
|
|||
|
|
return Math.max(0, Math.min(1, x));
|
|||
|
|
}
|
|||
|
|
function lerp(a, b, t) {
|
|||
|
|
return a + (b - a) * t;
|
|||
|
|
}
|
|||
|
|
function radiusForMu(mu) {
|
|||
|
|
const t = clamp01((mu - muMin) / (muMax - muMin || 1));
|
|||
|
|
return lerp(5, 20, t);
|
|||
|
|
}
|
|||
|
|
function colorForPaired(isPaired) {
|
|||
|
|
return isPaired ? COLORS.paired : COLORS.unpaired;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const siteLayer = L.featureGroup();
|
|||
|
|
for (const p of points) {
|
|||
|
|
const marker = L.circleMarker([p.lat, p.lng], {
|
|||
|
|
radius: radiusForMu(p.mu),
|
|||
|
|
color: "rgba(255,255,255,0.96)",
|
|||
|
|
weight: 1.35,
|
|||
|
|
fillColor: colorForPaired(p.is_paired),
|
|||
|
|
fillOpacity: 0.92,
|
|||
|
|
});
|
|||
|
|
marker.bindPopup(
|
|||
|
|
`<div style="font: 13px/1.35 system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;">\n` +
|
|||
|
|
`<div style="font-weight:700;margin-bottom:6px;">${p.site_id}. ${p.site_name}</div>` +
|
|||
|
|
`<div><b>mu</b>: ${p.mu.toFixed(1)}</div>` +
|
|||
|
|
`<div><b>sigma</b>: ${p.sigma.toFixed(1)}</div>` +
|
|||
|
|
`<div><b>k</b>: ${p.k}</div>` +
|
|||
|
|
`<div><b>paired</b>: ${p.is_paired ? "yes" : "no"}</div>` +
|
|||
|
|
`</div>`
|
|||
|
|
);
|
|||
|
|
siteLayer.addLayer(marker);
|
|||
|
|
}
|
|||
|
|
siteLayer.addTo(map);
|
|||
|
|
|
|||
|
|
const linkLayer = L.featureGroup();
|
|||
|
|
for (const e of links) {
|
|||
|
|
const a = byId.get(e.site_i_id);
|
|||
|
|
const b = byId.get(e.site_j_id);
|
|||
|
|
if (!a || !b) continue;
|
|||
|
|
const line = L.polyline(
|
|||
|
|
[
|
|||
|
|
[a.lat, a.lng],
|
|||
|
|
[b.lat, b.lng],
|
|||
|
|
],
|
|||
|
|
{
|
|||
|
|
color: COLORS.link,
|
|||
|
|
weight: 2.6,
|
|||
|
|
opacity: 0.5,
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
line.bindPopup(
|
|||
|
|
`<div style="font: 13px/1.35 system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;">\n` +
|
|||
|
|
`<div style="font-weight:700;margin-bottom:6px;">Pair</div>` +
|
|||
|
|
`<div>${e.site_i_id}. ${e.site_i_name}</div>` +
|
|||
|
|
`<div>${e.site_j_id}. ${e.site_j_name}</div>` +
|
|||
|
|
`<div><b>distance</b>: ${e.distance.toFixed(2)} mi</div>` +
|
|||
|
|
`</div>`
|
|||
|
|
);
|
|||
|
|
linkLayer.addLayer(line);
|
|||
|
|
}
|
|||
|
|
linkLayer.addTo(map);
|
|||
|
|
|
|||
|
|
const all = L.featureGroup([siteLayer, linkLayer]);
|
|||
|
|
map.fitBounds(all.getBounds().pad(0.12));
|
|||
|
|
|
|||
|
|
const legend = L.control({ position: "bottomleft" });
|
|||
|
|
legend.onAdd = function () {
|
|||
|
|
const pairedCount = points.filter((p) => p.is_paired).length;
|
|||
|
|
const div = L.DomUtil.create("div", "legend");
|
|||
|
|
div.innerHTML =
|
|||
|
|
`<div class="title">Task 3 Fig.1 配对地图(交互)</div>` +
|
|||
|
|
`<div class="row"><span><span class="swatch" style="background:${COLORS.paired}"></span>已配对站点</span><span>${pairedCount}</span></div>` +
|
|||
|
|
`<div class="row"><span><span class="swatch" style="background:${COLORS.unpaired}"></span>未配对站点</span><span>${points.length - pairedCount}</span></div>` +
|
|||
|
|
`<div class="muted">点大小: mu(线性缩放)</div>` +
|
|||
|
|
`<div class="line"></div>` +
|
|||
|
|
`<div class="muted">连线: 34条配对(点击查看距离)</div>`;
|
|||
|
|
return div;
|
|||
|
|
};
|
|||
|
|
legend.addTo(map);
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|