Leaflet

一个开源的 JavaScript 库
用于移动友好的交互式地图

← 教程

交互式色阶地图

这是一个关于如何使用 色阶地图GeoJSON 和一些 自定义控件 创建一个彩色交互式美国各州人口密度地图的案例研究(希望这能说服所有剩余的没有使用 Leaflet 的主要新闻和政府网站开始使用它)。

本教程的灵感来自 德州论坛报美国参议院决选结果地图(也是由 Leaflet 提供支持),由 Ryan Murphy 创建。

查看此独立示例。

数据来源

我们将创建一个美国各州人口密度可视化。由于数据量(州形状和每个州的密度值)并不大,最方便和简单的方法是使用 GeoJSON 来存储和显示它。

我们 GeoJSON 数据中的每个要素 (us-states.js) 将看起来像这样

{
	"type": "Feature",
	"properties": {
		"name": "Alabama",
		"density": 94.65
	},
	"geometry": ...
	...
}

州形状的 GeoJSON 由 Mike BostockD3 的创始人)友情分享,并从 维基百科上的这篇文章 中扩展了密度值,这些值基于 2011 年 7 月 1 日从 美国人口普查局 收集的数据,并分配给 statesData JS 变量。

基本州地图

让我们在地图上显示我们的州数据

var map = L.map('map').setView([37.8, -96], 4);

var tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
	maxZoom: 19,
	attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);

L.geoJson(statesData).addTo(map);
查看此独立示例。

添加一些颜色

现在我们需要根据人口密度对各州进行着色。为地图选择合适的颜色可能很棘手,但有一个很棒的工具可以帮助我们——ColorBrewer。使用从该工具中获得的值,我们创建一个函数,该函数根据人口密度返回颜色

function getColor(d) {
	return d > 1000 ? '#800026' :
	       d > 500  ? '#BD0026' :
	       d > 200  ? '#E31A1C' :
	       d > 100  ? '#FC4E2A' :
	       d > 50   ? '#FD8D3C' :
	       d > 20   ? '#FEB24C' :
	       d > 10   ? '#FED976' :
	                  '#FFEDA0';
}

接下来,我们为 GeoJSON 图层定义一个样式函数,以便它的 fillColor 取决于 feature.properties.density 属性,还稍微调整了外观并添加了虚线边框以增加美观。

function style(feature) {
	return {
		fillColor: getColor(feature.properties.density),
		weight: 2,
		opacity: 1,
		color: 'white',
		dashArray: '3',
		fillOpacity: 0.7
	};
}

L.geoJson(statesData, {style: style}).addTo(map);

现在看起来好多了!

查看此独立示例。

添加交互

现在让我们在鼠标悬停在州上时以某种方式使其在视觉上突出显示。首先,我们将为图层 mouseover 事件定义一个事件监听器

function highlightFeature(e) {
	var layer = e.target;

	layer.setStyle({
		weight: 5,
		color: '#666',
		dashArray: '',
		fillOpacity: 0.7
	});

	layer.bringToFront();
}

在这里,我们通过 e.target 获取到被悬停的图层,并为该图层设置一个粗灰色的边框作为我们的高亮效果,同时将其置于最前面,以防止边框与附近的州发生冲突。

接下来,我们将定义 mouseout 事件发生时会发生什么

function resetHighlight(e) {
	geojson.resetStyle(e.target);
}

方便的 geojson.resetStyle 方法将重置图层样式为其默认状态(由我们的 style 函数定义)。为了使此方法起作用,请确保我们的 GeoJSON 图层可以通过 geojson 变量访问,方法是在我们的监听器之前定义它,并在稍后将图层分配给它

var geojson;
// ... our listeners
geojson = L.geoJson(...);

作为一个额外的补充,让我们定义一个 click 监听器,该监听器会放大到该州

function zoomToFeature(e) {
	map.fitBounds(e.target.getBounds());
}

现在,我们将使用 onEachFeature 选项在我们的州图层上添加监听器

function onEachFeature(feature, layer) {
	layer.on({
		mouseover: highlightFeature,
		mouseout: resetHighlight,
		click: zoomToFeature
	});
}

geojson = L.geoJson(statesData, {
	style: style,
	onEachFeature: onEachFeature
}).addTo(map);

这使得各州在悬停时能很好地突出显示,并使我们能够在监听器中添加其他交互。

自定义信息控件

我们可以在点击时使用常用的弹出窗口来显示有关不同州的信息,但我们将选择不同的方式——在鼠标悬停在州上时,在一个 自定义控件 中显示信息。

以下是我们的控件代码

var info = L.control();

info.onAdd = function (map) {
	this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
	this.update();
	return this._div;
};

// method that we will use to update the control based on feature properties passed
info.update = function (props) {
	this._div.innerHTML = '<h4>US Population Density</h4>' +  (props ?
		'<b>' + props.name + '</b><br />' + props.density + ' people / mi<sup>2</sup>'
		: 'Hover over a state');
};

info.addTo(map);

当用户悬停在州上时,我们需要更新控件,因此我们也将修改我们的监听器,如下所示

function highlightFeature(e) {
	...
	info.update(layer.feature.properties);
}

function resetHighlight(e) {
	...
	info.update();
}

控件还需要一些 CSS 样式才能看起来漂亮

.info {
	padding: 6px 8px;
	font: 14px/16px Arial, Helvetica, sans-serif;
	background: white;
	background: rgba(255,255,255,0.8);
	box-shadow: 0 0 15px rgba(0,0,0,0.2);
	border-radius: 5px;
}
.info h4 {
	margin: 0 0 5px;
	color: #777;
}

自定义图例控件

创建带有图例的控件更容易,因为它是不变的,不会随着州的悬停而改变。JavaScript 代码

var legend = L.control({position: 'bottomright'});

legend.onAdd = function (map) {

	var div = L.DomUtil.create('div', 'info legend'),
		grades = [0, 10, 20, 50, 100, 200, 500, 1000],
		labels = [];

	// loop through our density intervals and generate a label with a colored square for each interval
	for (var i = 0; i < grades.length; i++) {
		div.innerHTML +=
			'<i style="background:' + getColor(grades[i] + 1) + '"></i> ' +
			grades[i] + (grades[i + 1] ? '&ndash;' + grades[i + 1] + '<br>' : '+');
	}

	return div;
};

legend.addTo(map);

控件的 CSS 样式(我们还重用了之前定义的 info 类)

.legend {
	line-height: 18px;
	color: #555;
}
.legend i {
	width: 18px;
	height: 18px;
	float: left;
	margin-right: 8px;
	opacity: 0.7;
}

享受本页面顶部的结果,或在 单独页面 上查看。