GeoJSON Layer
Runnable Leaflet example: GeoJSON Layer. Open the live demo, read the complete source, and copy the file into your project. No build step or API key.
single HTML file · CDN dependencies onlyinterface may contain Chinese labels
Source
<!--
示例:GeoJSON 图层
要点:style / pointToLayer / onEachFeature 三个回调、filter 过滤、setStyle 批量改色
依赖:Leaflet 1.9.4 · jsDelivr CDN
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="robots" content="noindex,follow">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GeoJSON 图层</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css">
<style>
html,body{height:100%;margin:0}
body{display:flex;flex-direction:column;overflow:hidden;
font:13px/1.6 -apple-system,"Segoe UI","PingFang SC",sans-serif}
.bar{flex:0 0 auto;padding:8px 12px;background:#f6f7f9;border-bottom:1px solid #e3e6ea;
display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12px;color:#58606e}
.bar button{font:inherit;font-size:12px;padding:4px 10px;border-radius:4px;cursor:pointer;
border:1px solid #ccd1d9;background:#fff;color:#16181d}
.bar button:hover{background:#eef0f3}
.bar button.on{background:#1f5fd0;border-color:#1f5fd0;color:#fff}
.bar b{color:#16181d;font-weight:600}
#map{flex:1 1 auto;min-height:0}
.dot{width:13px;height:13px;border-radius:50%;border:2px solid #fff;
box-shadow:0 0 0 1px rgba(0,0,0,.25)}
</style>
</head>
<body>
<div class="bar">
<button id="filter">过滤低等级:关</button>
<button id="emphasis">强调重点:关</button>
<button id="fit">适配范围</button>
<span>要素 <b id="count">5</b></span>
<span id="hint">点击要素查看属性</span>
</div>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
(function () {
'use strict';
var map = L.map('map', { center: [39.912, 116.395], zoom: 13 });
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
maxZoom: 19,
attribution: 'Tiles © Esri'
}).addTo(map);
var data = {
type: 'FeatureCollection',
features: [
{ type: 'Feature', properties: { name: '天安门', level: 3 },
geometry: { type: 'Point', coordinates: [116.391263, 39.907359] } },
{ type: 'Feature', properties: { name: '王府井', level: 2 },
geometry: { type: 'Point', coordinates: [116.4106, 39.9147] } },
{ type: 'Feature', properties: { name: '西单', level: 1 },
geometry: { type: 'Point', coordinates: [116.3742, 39.9097] } },
{ type: 'Feature', properties: { name: '长安街', level: 2 },
geometry: { type: 'LineString',
coordinates: [[116.350, 39.9078], [116.392, 39.9080], [116.440, 39.9072]] } },
{ type: 'Feature', properties: { name: '故宫片区', level: 3 },
geometry: { type: 'Polygon',
coordinates: [[[116.388, 39.912], [116.401, 39.912],
[116.401, 39.923], [116.388, 39.923], [116.388, 39.912]]] } }
]
};
var COLORS = { 1: '#9aa3b0', 2: '#1f5fd0', 3: '#c2410c' };
var countEl = document.getElementById('count');
var layer = null;
var filterOn = false;
var emphasisOn = false;
function styleFor(feature) {
var lv = feature.properties.level || 1;
return {
color: COLORS[lv],
weight: 1 + lv,
fillColor: COLORS[lv],
fillOpacity: 0.06 + lv * 0.04,
dashArray: emphasisOn && lv < 3 ? '5 4' : null
};
}
function build() {
if (layer) map.removeLayer(layer);
layer = L.geoJSON(data, {
filter: function (f) {
return !(filterOn && f.properties.level === 1);
},
style: styleFor,
pointToLayer: function (feature, latlng) {
var lv = feature.properties.level || 1;
return L.marker(latlng, {
icon: L.divIcon({
className: '',
html: '<div class="dot" style="background:' + COLORS[lv] + '"></div>',
iconSize: [13, 13],
iconAnchor: [6.5, 6.5]
})
});
},
onEachFeature: function (feature, lyr) {
var p = feature.properties;
lyr.bindTooltip(p.name, { direction: 'top', offset: [0, -10] });
lyr.bindPopup(
'<b>' + p.name + '</b><br>等级 ' + p.level +
'<br>几何 ' + feature.geometry.type
);
}
}).addTo(map);
countEl.textContent = layer.getLayers().length;
}
build();
map.fitBounds(layer.getBounds(), { padding: [40, 40] });
document.getElementById('filter').addEventListener('click', function () {
filterOn = !filterOn;
this.textContent = '过滤低等级:' + (filterOn ? '开' : '关');
this.classList.toggle('on', filterOn);
build();
document.getElementById('hint').textContent =
filterOn ? 'level=1 的西单已被 filter 排除' : '已显示全部要素';
});
document.getElementById('emphasis').addEventListener('click', function () {
emphasisOn = !emphasisOn;
this.textContent = '强调重点:' + (emphasisOn ? '开' : '关');
this.classList.toggle('on', emphasisOn);
/* setStyle 只影响线与面,点坐标在 pointToLayer 里,需重建 */
layer.setStyle(styleFor);
if (emphasisOn) {
layer.eachLayer(function (lyr) {
if (lyr instanceof L.Marker) lyr.setZIndexOffset(1000);
});
}
});
document.getElementById('fit').addEventListener('click', function () {
map.fitBounds(layer.getBounds(), { padding: [40, 40] });
});
})();
</script>
</body>
</html>
Language: the code and this page are in English; the full tutorial and API notes for this example are in Chinese. The demo itself is the authoritative version — it runs exactly as shown.