Mapbox Style
Runnable OpenLayers example: Mapbox Style. 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
<!--
示例:Mapbox 样式表达式
要点:interpolate 按属性插值颜色,step 按缩放级别切换线宽
依赖:OpenLayers 10.3.1 · 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>Mapbox 样式表达式</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@10.3.1/ol.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;color:#16181d}
.bar{flex:0 0 auto;padding:8px 12px;background:#f6f7f9;border-bottom:1px solid #e3e6ea;
display:flex;gap:16px;align-items:center;font-size:12px;color:#58606e;flex-wrap:wrap}
.bar b{color:#16181d;font-weight:600}
#map{flex:1 1 auto;min-height:0;background:#eef0f3}
</style>
</head>
<body>
<div class="bar">
<span>当前缩放:<b id="z">—</b></span>
<span>把地图缩放到 9 级以下、13 级以上,对比道路线宽变化</span>
</div>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/ol@10.3.1/dist/ol.js"></script>
<script>
(function () {
'use strict';
if (!window.ol) {
document.getElementById('map').textContent = 'OpenLayers 加载失败,请检查 cdn.jsdelivr.net 是否可访问';
return;
}
var fromLonLat = ol.proj.fromLonLat;
var data = {
type: 'FeatureCollection',
features: [
{ type: 'Feature', properties: { name: '甲区', pop: 2000 },
geometry: { type: 'Polygon', coordinates: [[[116.29, 39.95], [116.37, 39.95], [116.37, 39.89], [116.29, 39.89], [116.29, 39.95]]] } },
{ type: 'Feature', properties: { name: '乙区', pop: 12000 },
geometry: { type: 'Polygon', coordinates: [[[116.38, 39.96], [116.46, 39.96], [116.46, 39.895], [116.38, 39.895], [116.38, 39.96]]] } },
{ type: 'Feature', properties: { name: '丙区', pop: 26000 },
geometry: { type: 'Polygon', coordinates: [[[116.47, 39.945], [116.55, 39.945], [116.55, 39.875], [116.47, 39.875], [116.47, 39.945]]] } },
{ type: 'Feature', properties: { name: '干线', rank: 1 },
geometry: { type: 'LineString', coordinates: [[116.28, 39.92], [116.40, 39.93], [116.56, 39.91]] } },
{ type: 'Feature', properties: { name: '支线', rank: 2 },
geometry: { type: 'LineString', coordinates: [[116.31, 39.87], [116.42, 39.895], [116.52, 39.87]] } }
]
};
/* 样式表达式,语法与 Mapbox Style Spec 一致 */
var fillExpr = ['interpolate', ['linear'], ['get', 'pop'], 0, '#e8f0fb', 5000, '#7fb0e8', 15000, '#1f5fd0', 26000, '#0b3d91'];
var lineWidthExpr = ['step', ['zoom'], 1.5, 10, 3, 13, 5.5];
function parseColor(hex) {
return [parseInt(hex.substr(1, 2), 16), parseInt(hex.substr(3, 2), 16), parseInt(hex.substr(5, 2), 16)];
}
function evalExpr(expr, props) {
var op = expr[0];
if (op === 'get') return props[expr[1]];
if (op === 'zoom') return props.__zoom;
if (op === 'literal') return expr[1];
if (op === 'interpolate') {
var kind = expr[1];
var input = evalExpr(expr[2], props);
var stops = expr.slice(3);
var lo = null, hi = null;
for (var i = 0; i < stops.length; i += 2) {
if (stops[i] <= input) lo = [stops[i], stops[i + 1]];
if (stops[i] >= input && !hi) hi = [stops[i], stops[i + 1]];
}
if (!lo) return hi[1];
if (!hi) return lo[1];
var f = (input - lo[0]) / Math.max(1e-6, hi[0] - lo[0]);
if (typeof lo[1] === 'string' && kind[0] === 'linear') {
var a = parseColor(lo[1]), b = parseColor(hi[1]);
return 'rgb(' + Math.round(a[0] + (b[0] - a[0]) * f) + ',' +
Math.round(a[1] + (b[1] - a[1]) * f) + ',' +
Math.round(a[2] + (b[2] - a[2]) * f) + ')';
}
return lo[1] + (hi[1] - lo[1]) * f;
}
if (op === 'step') {
var v = evalExpr(expr[1], props);
var out = expr[2];
for (var s = 3; s < expr.length; s += 2) {
if (v >= expr[s]) out = expr[s + 1];
}
return out;
}
return null;
}
var features = new ol.format.GeoJSON().readFeatures(data, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
});
var view = new ol.View({ center: fromLonLat([116.41, 39.915]), zoom: 11 });
var layer = new ol.layer.Vector({
source: new ol.source.Vector({ features: features }),
style: function (feature, resolution) {
var props = { __zoom: view.getZoomForResolution(resolution) };
feature.getProperties && Object.keys(feature.getProperties()).forEach(function (k) {
props[k] = feature.get(k);
});
if (feature.getGeometry().getType() === 'Polygon') {
return new ol.style.Style({
fill: new ol.style.Fill({ color: evalExpr(fillExpr, props) }),
stroke: new ol.style.Stroke({ color: 'rgba(11,61,145,0.55)', width: 1 }),
text: new ol.style.Text({
text: feature.get('name'),
font: '500 12px sans-serif',
fill: new ol.style.Fill({ color: '#16181d' }),
stroke: new ol.style.Stroke({ color: '#ffffff', width: 3 })
})
});
}
return new ol.style.Style({
stroke: new ol.style.Stroke({
color: feature.get('rank') === 1 ? '#b42318' : '#a9700f',
width: evalExpr(lineWidthExpr, props)
})
});
}
});
var map = new ol.Map({
target: 'map',
layers: [new ol.layer.Tile({ source: new ol.source.XYZ({url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', attributions: 'Tiles © Esri'}) }), layer],
view: view,
controls: ol.control.defaults.defaults({ rotate: false })
});
function reportZoom() {
document.getElementById('z').textContent = view.getZoom().toFixed(1);
}
map.on('moveend', reportZoom);
reportZoom();
})();
</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.