2869 字
14 分鐘

[主題地圖]川普全球關稅戰統計地圖

關鍵字: 川普關稅、台灣關稅、關稅戰

[主題地圖]川普全球關稅統計地圖

1. 地圖展示

<script src="https://cdn.tailwindcss.com"></script>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
/* Tooltip 基本樣式 - 深色 */
#tooltip {
position: absolute;
opacity: 0;
background-color: #1f2937; /* 深灰 */
color: #e5e7eb; /* 淺灰文字 */
border: 1px solid #4b5563; /* 中灰邊框 */
padding: 10px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
pointer-events: none;
transition: opacity 0.2s ease-in-out, left 0.1s linear, top 0.1s linear;
z-index: 10;
max-width: 200px;
}
#tooltip.visible {
opacity: 1;
}
/* Tooltip 內部文字顏色 */
#tooltip .font-bold.text-blue-600 {
color: #fca5a5; /* 淺紅色 */
}
#tooltip .text-gray-800 {
color: #f9fafb; /* 更亮的灰色 */
}
#tooltip .text-gray-700 {
color: #d1d5db; /* 稍暗的淺灰 */
}
/* SVG 文本選取禁用 */
svg text::selection {
background: none;
}
/* 強制 Tailwind 深色模式 (如果瀏覽器預設淺色) */
:root {
color-scheme: dark;
}
</style>
<div class="flex flex-col items-center">
<h2 class="text-3xl font-bold mb-6 text-white-100">川普關稅戰統計地圖</h2>
<div id="chart-container" class="relative bg-gray-800 rounded-xl p-6 shadow-lg border border-gray-700 mb-8">
<svg id="bubble-chart" width="800" height="450" viewBox="0 0 800 450">
<title>關稅泡泡圖</title>
<desc>顯示各國關稅稅率的泡泡圖,泡泡大小和顏色代表稅率高低。</desc>
</svg>
<div id="tooltip"></div>
</div>
<div class="w-full max-w-3xl space-y-8">
<div class="flex flex-col md:flex-row justify-between bg-gray-800 p-6 rounded-xl shadow-md border border-gray-700 gap-6">
<div id="color-legend">
<h3 class="text-lg font-semibold mb-3 text-gray-100">關稅稅率 (顏色)</h3>
<div class="flex items-center flex-wrap gap-x-4 gap-y-2">
</div>
</div>
<div id="size-legend">
<h3 class="text-lg font-semibold mb-3 text-gray-100">關稅稅率 (大小)</h3>
<div class="flex items-end flex-wrap gap-x-4 gap-y-2">
</div>
</div>
</div>
<div id="top-countries" class="bg-gray-800 p-6 rounded-xl shadow-md border border-gray-700">
<h3 class="text-xl font-semibold mb-5 text-center text-gray-100">關稅最高的五個國家</h3>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-6 md:gap-8">
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const tariffData = [
{country: "中國", tariff: 34, region: "亞洲", x: 650, y: 180, originalName: "China"},
{country: "歐盟", tariff: 20, region: "歐洲", x: 450, y: 150, originalName: "European Union"},
{country: "越南", tariff: 46, region: "亞洲", x: 670, y: 210, originalName: "Vietnam"},
{country: "台灣", tariff: 32, region: "亞洲", x: 690, y: 190, originalName: "Taiwan"},
{country: "日本", tariff: 24, region: "亞洲", x: 720, y: 170, originalName: "Japan"},
{country: "印度", tariff: 26, region: "亞洲", x: 580, y: 210, originalName: "India"},
{country: "南韓", tariff: 25, region: "亞洲", x: 700, y: 175, originalName: "South Korea"},
{country: "泰國", tariff: 36, region: "亞洲", x: 650, y: 220, originalName: "Thailand"},
{country: "瑞士", tariff: 31, region: "歐洲", x: 430, y: 160, originalName: "Switzerland"},
{country: "印尼", tariff: 32, region: "亞洲", x: 650, y: 250, originalName: "Indonesia"},
{country: "馬來西亞", tariff: 24, region: "亞洲", x: 640, y: 230, originalName: "Malaysia"},
{country: "柬埔寨", tariff: 49, region: "亞洲", x: 655, y: 215, originalName: "Cambodia"},
{country: "英國", tariff: 10, region: "歐洲", x: 410, y: 145, originalName: "United Kingdom"},
{country: "南非", tariff: 30, region: "非洲", x: 480, y: 300, originalName: "South Africa"},
{country: "巴西", tariff: 10, region: "南美洲", x: 320, y: 280, originalName: "Brazil"},
{country: "孟加拉", tariff: 37, region: "亞洲", x: 600, y: 200, originalName: "Bangladesh"},
{country: "新加坡", tariff: 10, region: "亞洲", x: 645, y: 240, originalName: "Singapore"},
{country: "以色列", tariff: 17, region: "中東", x: 510, y: 200, originalName: "Israel"},
{country: "菲律賓", tariff: 17, region: "亞洲", x: 680, y: 220, originalName: "Philippines"},
{country: "智利", tariff: 10, region: "南美洲", x: 300, y: 320, originalName: "Chile"},
{country: "澳洲", tariff: 10, region: "大洋洲", x: 700, y: 310, originalName: "Australia"},
{country: "巴基斯坦", tariff: 29, region: "亞洲", x: 560, y: 200, originalName: "Pakistan"},
{country: "土耳其", tariff: 10, region: "中東", x: 490, y: 180, originalName: "Turkey"},
{country: "斯里蘭卡", tariff: 44, region: "亞洲", x: 590, y: 230, originalName: "Sri Lanka"},
{country: "哥倫比亞", tariff: 10, region: "南美洲", x: 290, y: 260, originalName: "Colombia"},
{country: "秘魯", tariff: 10, region: "南美洲", x: 280, y: 270, originalName: "Peru"},
{country: "尼加拉瓜", tariff: 18, region: "中美洲", x: 260, y: 230, originalName: "Nicaragua"},
{country: "挪威", tariff: 15, region: "歐洲", x: 430, y: 130, originalName: "Norway"},
{country: "哥斯大黎加", tariff: 10, region: "中美洲", x: 255, y: 240, originalName: "Costa Rica"},
{country: "約旦", tariff: 20, region: "中東", x: 520, y: 190, originalName: "Jordan"},
{country: "多明尼加", tariff: 10, region: "加勒比海", x: 280, y: 220, originalName: "Dominican Republic"},
{country: "阿聯酋", tariff: 10, region: "中東", x: 540, y: 210, originalName: "United Arab Emirates"},
{country: "紐西蘭", tariff: 10, region: "大洋洲", x: 760, y: 330, originalName: "New Zealand"},
{country: "阿根廷", tariff: 10, region: "南美洲", x: 310, y: 320, originalName: "Argentina"},
{country: "厄瓜多", tariff: 10, region: "南美洲", x: 270, y: 260, originalName: "Ecuador"},
{country: "瓜地馬拉", tariff: 10, region: "中美洲", x: 250, y: 220, originalName: "Guatemala"},
{country: "宏都拉斯", tariff: 10, region: "中美洲", x: 255, y: 225, originalName: "Honduras"},
{country: "馬達加斯加", tariff: 47, region: "非洲", x: 520, y: 290, originalName: "Madagascar"},
{country: "緬甸", tariff: 44, region: "亞洲", x: 630, y: 210, originalName: "Myanmar"},
{country: "突尼西亞", tariff: 28, region: "非洲", x: 440, y: 190, originalName: "Tunisia"},
{country: "哈薩克", tariff: 27, region: "亞洲", x: 560, y: 150, originalName: "Kazakhstan"},
{country: "塞爾維亞", tariff: 37, region: "歐洲", x: 460, y: 170, originalName: "Serbia"},
{country: "埃及", tariff: 10, region: "非洲", x: 490, y: 210, originalName: "Egypt"},
{country: "沙烏地阿拉伯", tariff: 10, region: "中東", x: 530, y: 220, originalName: "Saudi Arabia"},
{country: "薩爾瓦多", tariff: 10, region: "中美洲", x: 245, y: 230, originalName: "El Salvador"},
{country: "象牙海岸", tariff: 21, region: "非洲", x: 420, y: 250, originalName: "Côte d'Ivoire"},
{country: "寮國", tariff: 48, region: "亞洲", x: 640, y: 205, originalName: "Laos"},
{country: "波札那", tariff: 37, region: "非洲", x: 470, y: 290, originalName: "Botswana"},
{country: "千里達及托巴哥", tariff: 10, region: "加勒比海", x: 290, y: 235, originalName: "Trinidad and Tobago"},
{country: "摩洛哥", tariff: 10, region: "非洲", x: 410, y: 200, originalName: "Morocco"},
{country: "阿爾及利亞", tariff: 30, region: "非洲", x: 425, y: 210, originalName: "Algeria"},
{country: "阿曼", tariff: 10, region: "中東", x: 550, y: 220, originalName: "Oman"},
{country: "烏拉圭", tariff: 10, region: "南美洲", x: 320, y: 310, originalName: "Uruguay"},
{country: "美國", tariff: 0, region: "北美洲", x: 230, y: 170, originalName: "United States"},
{country: "墨西哥", tariff: 20, region: "北美洲", x: 220, y: 210, originalName: "Mexico"},
{country: "加拿大", tariff: 10, region: "北美洲", x: 230, y: 130, originalName: "Canada"},
{country: "俄羅斯", tariff: 20, region: "歐亞", x: 520, y: 130, originalName: "Russia"},
{country: "留尼旺", tariff: 73, region: "非洲", x: 530, y: 300, originalName: "Reunion"},
{country: "賴索托", tariff: 50, region: "非洲", x: 480, y: 310, originalName: "Lesotho"},
{country: "法國", tariff: 20, region: "歐洲", x: 425, y: 155, originalName: "France"},
{country: "德國", tariff: 20, region: "歐洲", x: 440, y: 145, originalName: "Germany"},
{country: "義大利", tariff: 20, region: "歐洲", x: 450, y: 165, originalName: "Italy"},
{country: "西班牙", tariff: 20, region: "歐洲", x: 410, y: 170, originalName: "Spain"},
{country: "奈及利亞", tariff: 14, region: "非洲", x: 440, y: 240, originalName: "Nigeria"},
{country: "肯亞", tariff: 10, region: "非洲", x: 510, y: 260, originalName: "Kenya"},
{country: "烏克蘭", tariff: 10, region: "歐洲", x: 480, y: 150, originalName: "Ukraine"},
{country: "伊朗", tariff: 10, region: "中東", x: 550, y: 190, originalName: "Iran"},
{country: "伊拉克", tariff: 39, region: "中東", x: 540, y: 200, originalName: "Iraq"}
];
const svg = d3.select("#bubble-chart");
const width = +svg.attr("width");
const height = +svg.attr("height");
const tooltip = d3.select("#tooltip");
// --- 紅色漸層顏色比例尺 ---
const colorScale = d3.scaleLinear()
.domain([0, 10, 25, 50, 75]) // 保持不變
.range(['#fee2e2', '#fca5a5', '#ef4444', '#b91c1c', '#7f1d1d']) // 紅色系 (從淺到深)
.clamp(true);
const sizeScale = d3.scaleSqrt()
.domain([0, 75])
.range([5, 28]);
// --- SVG 背景調整 ---
svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", "#1f2937") // 深灰色背景
.attr("rx", 10)
.attr("ry", 10);
const gridGroup = svg.append("g").attr("class", "grid-lines");
// --- 網格線顏色調整 ---
const gridColor = "#4b5563"; // 中灰色
for (let x = 100; x <= 700; x += 100) {
gridGroup.append("line")
.attr("x1", x).attr("y1", 50)
.attr("x2", x).attr("y2", 400)
.attr("stroke", gridColor).attr("stroke-width", 0.5).attr("stroke-dasharray", "3,3"); // 變細一點
}
for (let y = 100; y <= 350; y += 50) {
gridGroup.append("line")
.attr("x1", 100).attr("y1", y)
.attr("x2", 700).attr("y2", y)
.attr("stroke", gridColor).attr("stroke-width", 0.5).attr("stroke-dasharray", "3,3"); // 變細一點
}
const regionLabels = [
{ x: 230, y: 90, text: "北美洲" }, { x: 445, y: 100, text: "歐洲" },
{ x: 650, y: 120, text: "亞洲" }, { x: 520, y: 170, text: "中東" },
{ x: 450, y: 350, text: "非洲" }, { x: 230, y: 200, text: "中美洲" },
{ x: 280, y: 340, text: "南美洲" }, { x: 730, y: 340, text: "大洋洲" }
];
// --- 地區標籤顏色調整 ---
svg.append("g")
.attr("class", "region-labels")
.selectAll("text")
.data(regionLabels)
.join("text")
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("class", "font-semibold text-sm")
.style("font-size", "14px")
.style("fill", "#9ca3af") // 淺灰色標籤
.text(d => d.text);
const bubbles = svg.append("g")
.attr("class", "bubbles")
.selectAll("g")
.data(tariffData)
.join("g")
.attr("transform", d => `translate(${d.x}, ${d.y})`)
.style("cursor", "pointer")
.on("mouseover", function(event, d) {
tooltip.transition().duration(100).style("opacity", 1).attr("class", "visible");
tooltip.html(`
<p class="font-bold text-lg text-gray-800">${d.country}</p> {/* Tooltip 樣式已在 <style> 中調整 */}
<p class="text-base text-gray-700">關稅: <span class="font-bold text-blue-600">${d.tariff}%</span></p>
<p class="text-base text-gray-700">地區: ${d.region}</p>
`);
const chartRect = document.getElementById('chart-container').getBoundingClientRect();
const tooltipRect = tooltip.node().getBoundingClientRect();
let left = event.pageX + 15;
let top = event.pageY - 15;
if (left + tooltipRect.width > chartRect.right) left = event.pageX - tooltipRect.width - 15;
if (top + tooltipRect.height > chartRect.bottom) top = event.pageY - tooltipRect.height - 15;
if (top < chartRect.top) top = event.pageY + 15;
tooltip.style("left", `${left}px`).style("top", `${top}px`);
d3.select(this).select("circle")
.transition().duration(150)
.attr("stroke-width", 1.5)
.attr("stroke", "#f3f4f6") // 懸停時用亮邊框
.attr("opacity", 1);
})
.on("mouseout", function(event, d) {
tooltip.transition().duration(200).style("opacity", 0).attr("class", "");
d3.select(this).select("circle")
.transition().duration(150)
.attr("stroke-width", 0.5)
.attr("stroke", "#6b7280") // 恢復深色邊框
.attr("opacity", 0.9);
});
// --- 泡泡邊框顏色調整 ---
bubbles.append("circle")
.attr("r", d => sizeScale(d.tariff))
.attr("fill", d => colorScale(d.tariff)) // 使用新的紅色比例尺
.attr("stroke", "#6b7280") // 深灰色邊框
.attr("stroke-width", 0.5)
.attr("opacity", 0.9);
const labelGroup = svg.append("g")
.attr("class", "labels")
.style("pointer-events", "none");
const getLabelPosition = (country) => {
const manualPositions = { /* ...保持不變... */
"中國": "right", "越南": "right", "柬埔寨": "left", "寮國": "top",
"緬甸": "left", "留尼旺": "right", "馬達加斯加": "left", "泰國": "bottom",
"斯里蘭卡": "top", "印尼": "bottom", "賴索托": "top", "台灣": "right",
"塞爾維亞": "right", "孟加拉": "left", "伊拉克": "bottom", "日本": "top",
"印度": "left", "巴西": "right", "澳洲": "right", "美國": "top",
"歐盟": "top", "俄羅斯": "top"
};
if (manualPositions[country.country]) return manualPositions[country.country];
if (country.x < 400) return "left";
return "right";
};
const getLabelCoordinates = (country, direction) => {
const radius = sizeScale(country.tariff);
const offset = 5;
switch(direction) { /* ...保持不變... */
case "top": return { x: 0, y: -radius - offset, anchor: "middle" };
case "right": return { x: radius + offset, y: 4, anchor: "start" };
case "bottom": return { x: 0, y: radius + offset + 7, anchor: "middle" };
case "left": return { x: -radius - offset, y: 4, anchor: "end" };
default: return { x: radius + offset, y: 4, anchor: "start" };
}
};
const countriesToShowLabel = tariffData.filter(d =>
d.tariff > 30 ||
["美國", "中國", "歐盟", "俄羅斯", "印度", "巴西", "日本", "澳洲"].includes(d.country)
);
countriesToShowLabel.forEach(country => {
const labelPos = getLabelPosition(country);
const coords = getLabelCoordinates(country, labelPos);
const group = labelGroup.append("g")
.attr("transform", `translate(${country.x}, ${country.y})`);
// --- 連接線顏色調整 ---
if (labelPos === "left" || labelPos === "right") {
group.append("line")
.attr("x1", labelPos === "left" ? -sizeScale(country.tariff) : sizeScale(country.tariff))
.attr("y1", 0)
.attr("x2", coords.x * 0.8)
.attr("y2", 0)
.attr("stroke", "#9ca3af") // 淺灰色線
.attr("stroke-width", 0.5)
.attr("stroke-dasharray", "2,1");
}
// --- 標籤文字顏色和描邊調整 ---
const labelColor = country.tariff > 40 ? "#f9fafb" : "#e5e7eb"; // 高關稅用亮白色,其他用淺灰
const labelWeight = country.tariff > 40 ? "bold" : "normal";
// 背景描邊 (用深色背景的顏色)
group.append("text")
.attr("x", coords.x)
.attr("y", coords.y)
.attr("text-anchor", coords.anchor)
.attr("font-size", "11px")
.attr("font-weight", labelWeight)
.attr("stroke", "#1f2937") // 深灰色描邊
.attr("stroke-width", 3)
.attr("stroke-linejoin", "round")
.text(`${country.country} (${country.tariff}%)`);
// 前景文字
group.append("text")
.attr("x", coords.x)
.attr("y", coords.y)
.attr("text-anchor", coords.anchor)
.attr("font-size", "11px")
.attr("font-weight", labelWeight)
.attr("fill", labelColor) // 使用計算出的標籤顏色
.text(`${country.country} (${country.tariff}%)`);
});
// --- 圖例文字顏色調整 ---
const legendTextColor = "text-gray-300"; // 圖例文字用稍暗的淺灰
const colorLegendContainer = d3.select("#color-legend .flex");
const colorRates = [0, 10, 25, 50, 75];
colorRates.forEach((rate, i) => {
const item = colorLegendContainer.append("div")
.attr("class", "flex flex-col items-center");
item.append("div")
.attr("class", "w-8 h-8 md:w-10 md:h-10 mb-1 md:mb-2 rounded-md shadow-sm border border-gray-600") // 加個邊框
.style("background-color", colorScale(rate)); // 自動使用新比例尺
let label = '';
if (i === 0) label = '0%';
else if (i === colorRates.length - 1) label = `${colorRates[i-1]+1}%+`;
else label = `${colorRates[i-1]+1}-${rate}%`;
item.append("span")
.attr("class", `text-xs md:text-sm ${legendTextColor} font-medium`) // 應用文字顏色
.text(label);
});
const sizeLegendContainer = d3.select("#size-legend .flex");
const sizeRates = [10, 25, 50, 75];
sizeRates.forEach(rate => {
const item = sizeLegendContainer.append("div")
.attr("class", "flex flex-col items-center");
const radius = sizeScale(rate);
const svgSize = radius * 2 + 4;
item.append("svg")
.attr("width", svgSize)
.attr("height", svgSize)
.append("circle")
.attr("cx", radius + 2)
.attr("cy", radius + 2)
.attr("r", radius)
.attr("fill", "none")
.attr("stroke", "#9ca3af") // 淺灰色圓圈
.attr("stroke-width", 1);
item.append("span")
.attr("class", `text-xs md:text-sm ${legendTextColor} font-medium mt-1`) // 應用文字顏色
.text(`${rate}%`);
});
// --- 前五名列表文字顏色調整 ---
const top5Countries = [...tariffData].sort((a, b) => b.tariff - a.tariff).slice(0, 5);
const topCountriesContainer = d3.select("#top-countries .grid");
top5Countries.forEach((country, i) => {
const item = topCountriesContainer.append("div")
.attr("class", "flex flex-col items-center");
item.append("div")
.attr("class", "w-10 h-10 md:w-12 md:h-12 rounded-full mb-2 md:mb-3 flex items-center justify-center font-bold text-white shadow-md text-sm md:text-base border border-gray-600") // 加邊框
.style("background-color", colorScale(country.tariff)) // 自動使用新比例尺
.text(i + 1);
const textDiv = item.append("div").attr("class", "text-center");
textDiv.append("p").attr("class", "text-sm md:text-base font-bold text-gray-100").text(country.country); // 亮白
textDiv.append("p").attr("class", "text-lg md:text-2xl font-bold text-red-400").text(`${country.tariff}%`); // 紅色關稅數字
textDiv.append("p").attr("class", "text-xs md:text-sm text-gray-400").text(country.region); // 灰色地區
});
});
</script>

資料來源

https://www.spatialgeolab.com/parking_online_pay/
https://www.spatialgeolab.com/spatial-speedcamera/
[主題地圖]川普全球關稅戰統計地圖
https://www.spatialgeolab.com/thrump-tariffs-map/
作者
GeoLab
發佈於
2025-04-03
許可協議
CC BY-NC-SA 4.0
© 2026 GeoLab. All Rights Reserved.
最後更新:2026/10/22