Control symbol scaling with setScaleFactor
This example demonstrates how to use setScaleFactor to dynamically scale symbol layers on the map. The example displays rally race waypoints with icons and text labels.
The control panel allows you to:
- Scale Factor: Adjusts the global scale factor for all symbol layers using
map.setScaleFactor(). - Icon Size Scale Range: Sets the minimum and maximum scaling limits for icons using the
icon-size-scale-rangelayout property. - Text Size Scale Range: Sets the minimum and maximum scaling limits for text using the
text-size-scale-rangelayout property.
In the example, you can experiment with different scale factor values and scale ranges to see how they affect the appearance of the symbols on the map. "Icon Size Scale Range" and "Text Size Scale Range" allow you to control the minimum and maximum scaling limits for icons and text of the custom layer, respectively, providing flexibility in how symbols are displayed at different scale levels. For example, if you set icon-size-scale-range to [1, 1], the icons will not scale regardless of the scale factor value.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Control symbol scaling with setScaleFactor</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.20.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.20.0/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<style>
.map-overlay {
font:
12px/20px 'Helvetica Neue',
Arial,
Helvetica,
sans-serif;
position: absolute;
width: 220px;
top: 0;
left: 0;
padding: 10px;
}
.map-overlay .map-overlay-inner {
background-color: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
border-radius: 3px;
padding: 10px;
margin-bottom: 10px;
}
.map-overlay-inner fieldset {
border: none;
padding: 0;
margin: 0 0 10px 0;
}
.map-overlay-inner label {
display: block;
font-weight: bold;
margin-bottom: 5px;
}
.map-overlay-inner input[type='range'] {
width: 100%;
cursor: ew-resize;
}
.map-overlay-inner .range-value {
font-size: 11px;
color: #666;
margin-top: 2px;
}
.map-overlay-inner .dual-range {
display: flex;
gap: 10px;
}
.map-overlay-inner .dual-range > div {
flex: 1;
}
.map-overlay-inner .dual-range label {
font-weight: normal;
font-size: 11px;
}
</style>
<div id="map"></div>
<div class="map-overlay top">
<div class="map-overlay-inner">
<fieldset>
<label>Scale Factor: <span id="scale-factor-value">1.0</span></label>
<input id="scale-factor" type="range" min="0.5" max="3" step="0.1" value="1">
</fieldset>
<fieldset>
<label>Icon Size Scale Range</label>
<div class="dual-range">
<div>
<label>Min: <span id="icon-min-value">0.8</span></label>
<input id="icon-size-min" type="range" min="0.1" max="5" step="0.1" value="0.8">
</div>
<div>
<label>Max: <span id="icon-max-value">2.0</span></label>
<input id="icon-size-max" type="range" min="0.1" max="5" step="0.1" value="2">
</div>
</div>
</fieldset>
<fieldset>
<label>Text Size Scale Range</label>
<div class="dual-range">
<div>
<label>Min: <span id="text-min-value">0.8</span></label>
<input id="text-size-min" type="range" min="0.1" max="5" step="0.1" value="0.8">
</div>
<div>
<label>Max: <span id="text-max-value">2.0</span></label>
<input id="text-size-max" type="range" min="0.1" max="5" step="0.1" value="2">
</div>
</div>
</fieldset>
</div>
</div>
<script>
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
// Rally Finland waypoints - a fictional stage route around Jyväskylä
const rallyWaypoints = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: { name: 'Start', order: 1 },
geometry: { type: 'Point', coordinates: [25.7473, 62.2426] }
},
{
type: 'Feature',
properties: { name: '01', order: 2 },
geometry: { type: 'Point', coordinates: [25.7821, 62.2589] }
},
{
type: 'Feature',
properties: { name: '02', order: 3 },
geometry: { type: 'Point', coordinates: [25.8234, 62.2712] }
},
{
type: 'Feature',
properties: { name: '03', order: 4 },
geometry: { type: 'Point', coordinates: [25.8612, 62.2534] }
},
{
type: 'Feature',
properties: { name: '04', order: 5 },
geometry: { type: 'Point', coordinates: [25.8923, 62.2321] }
},
{
type: 'Feature',
properties: { name: 'Finish', order: 6 },
geometry: { type: 'Point', coordinates: [25.9245, 62.2198] }
}
]
};
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/gl-js-team/cmleyrmcy002f01qthcccbolv',
center: [25.83, 62.245],
zoom: 11.5
});
map.on('style.load', () => {
map.addSource('rally-waypoints', {
type: 'geojson',
data: rallyWaypoints
});
map.addLayer({
id: 'rally-waypoints',
type: 'symbol',
source: 'rally-waypoints',
layout: {
'icon-image': 'bicycle',
'icon-size': 1.5,
'icon-size-scale-range': [0.8, 2],
'text-field': ['get', 'name'],
'text-font': ['DIN Pro Medium', 'Arial Unicode MS Regular'],
'text-size': 14,
'text-size-scale-range': [0.8, 2],
'text-offset': [0, 1.2],
'text-anchor': 'top',
'icon-allow-overlap': true,
'text-allow-overlap': true
},
paint: {
'icon-color': '#e74c3c',
'text-color': '#2c3e50',
'text-halo-color': '#ffffff',
'text-halo-width': 1
}
});
});
// Scale Factor slider
const scaleFactorSlider = document.getElementById('scale-factor');
const scaleFactorValue = document.getElementById('scale-factor-value');
scaleFactorSlider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
scaleFactorValue.textContent = value.toFixed(1);
map.setScaleFactor(value);
});
// Icon Size Scale Range sliders
const iconMinSlider = document.getElementById('icon-size-min');
const iconMaxSlider = document.getElementById('icon-size-max');
const iconMinValue = document.getElementById('icon-min-value');
const iconMaxValue = document.getElementById('icon-max-value');
function updateIconSizeScaleRange() {
const min = parseFloat(iconMinSlider.value);
const max = parseFloat(iconMaxSlider.value);
iconMinValue.textContent = min.toFixed(1);
iconMaxValue.textContent = max.toFixed(1);
map.setLayoutProperty('rally-waypoints', 'icon-size-scale-range', [
min,
max
]);
}
iconMinSlider.addEventListener('input', updateIconSizeScaleRange);
iconMaxSlider.addEventListener('input', updateIconSizeScaleRange);
// Text Size Scale Range sliders
const textMinSlider = document.getElementById('text-size-min');
const textMaxSlider = document.getElementById('text-size-max');
const textMinValue = document.getElementById('text-min-value');
const textMaxValue = document.getElementById('text-max-value');
function updateTextSizeScaleRange() {
const min = parseFloat(textMinSlider.value);
const max = parseFloat(textMaxSlider.value);
textMinValue.textContent = min.toFixed(1);
textMaxValue.textContent = max.toFixed(1);
map.setLayoutProperty('rally-waypoints', 'text-size-scale-range', [
min,
max
]);
}
textMinSlider.addEventListener('input', updateTextSizeScaleRange);
textMaxSlider.addEventListener('input', updateTextSizeScaleRange);
</script>
</body>
</html>
このコードスニペットは、
YOUR_MAPBOX_ACCESS_TOKENをあなたのMapboxアカウントのアクセストークンに置き換えるまで、期待通りに動作しません。import React, { useState, useEffect, useRef } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
// Rally Finland waypoints - a fictional stage route around Jyväskylä
const rallyWaypoints = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: { name: 'Start', order: 1 },
geometry: { type: 'Point', coordinates: [25.7473, 62.2426] }
},
{
type: 'Feature',
properties: { name: '01', order: 2 },
geometry: { type: 'Point', coordinates: [25.7821, 62.2589] }
},
{
type: 'Feature',
properties: { name: '02', order: 3 },
geometry: { type: 'Point', coordinates: [25.8234, 62.2712] }
},
{
type: 'Feature',
properties: { name: '03', order: 4 },
geometry: { type: 'Point', coordinates: [25.8612, 62.2534] }
},
{
type: 'Feature',
properties: { name: '04', order: 5 },
geometry: { type: 'Point', coordinates: [25.8923, 62.2321] }
},
{
type: 'Feature',
properties: { name: 'Finish', order: 6 },
geometry: { type: 'Point', coordinates: [25.9245, 62.2198] }
}
]
};
const MapboxExample = () => {
const mapContainerRef = useRef();
const mapRef = useRef();
const [scaleFactor, setScaleFactor] = useState(1);
const [iconSizeMin, setIconSizeMin] = useState(0.8);
const [iconSizeMax, setIconSizeMax] = useState(2);
const [textSizeMin, setTextSizeMin] = useState(0.8);
const [textSizeMax, setTextSizeMax] = useState(2);
const [styleLoaded, setStyleLoaded] = useState(false);
useEffect(() => {
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
mapRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
style: 'mapbox://styles/gl-js-team/cmleyrmcy002f01qthcccbolv',
center: [25.83, 62.245],
zoom: 11.5
});
mapRef.current.on('style.load', () => {
setStyleLoaded(true);
mapRef.current.addSource('rally-waypoints', {
type: 'geojson',
data: rallyWaypoints
});
mapRef.current.addLayer({
id: 'rally-waypoints',
type: 'symbol',
source: 'rally-waypoints',
layout: {
'icon-image': 'bicycle',
'icon-size': 1.5,
'icon-size-scale-range': [0.8, 2],
'text-field': ['get', 'name'],
'text-font': ['DIN Pro Medium', 'Arial Unicode MS Regular'],
'text-size': 14,
'text-size-scale-range': [0.8, 2],
'text-offset': [0, 1.2],
'text-anchor': 'top',
'icon-allow-overlap': true,
'text-allow-overlap': true
},
paint: {
'icon-color': '#e74c3c',
'text-color': '#2c3e50',
'text-halo-color': '#ffffff',
'text-halo-width': 1
}
});
});
return () => mapRef.current.remove();
}, []);
useEffect(() => {
if (!styleLoaded) return;
mapRef.current.setScaleFactor(scaleFactor);
}, [scaleFactor, styleLoaded]);
useEffect(() => {
if (!styleLoaded) return;
mapRef.current.setLayoutProperty(
'rally-waypoints',
'icon-size-scale-range',
[iconSizeMin, iconSizeMax]
);
}, [iconSizeMin, iconSizeMax, styleLoaded]);
useEffect(() => {
if (!styleLoaded) return;
mapRef.current.setLayoutProperty(
'rally-waypoints',
'text-size-scale-range',
[textSizeMin, textSizeMax]
);
}, [textSizeMin, textSizeMax, styleLoaded]);
const mapOverlayStyle = {
font: "12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif",
position: 'absolute',
width: '220px',
top: '0',
left: '0',
padding: '10px'
};
const mapOverlayInnerStyle = {
backgroundColor: '#fff',
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.1)',
borderRadius: '3px',
padding: '10px',
marginBottom: '10px'
};
const fieldsetStyle = {
border: 'none',
padding: '0',
margin: '0 0 10px 0'
};
const labelStyle = {
display: 'block',
fontWeight: 'bold',
marginBottom: '5px'
};
const inputStyle = {
width: '100%',
cursor: 'ew-resize'
};
const dualRangeStyle = {
display: 'flex',
gap: '10px'
};
const dualRangeItemStyle = {
flex: 1
};
const smallLabelStyle = {
fontWeight: 'normal',
fontSize: '11px',
display: 'block',
marginBottom: '3px'
};
return (
<>
<div ref={mapContainerRef} style={{ height: '100%' }} />
<div className="map-overlay" style={mapOverlayStyle}>
<div className="map-overlay-inner" style={mapOverlayInnerStyle}>
<fieldset style={fieldsetStyle}>
<label style={labelStyle}>
Scale Factor: {scaleFactor.toFixed(1)}
</label>
<input
type="range"
min="0.5"
max="3"
step="0.1"
value={scaleFactor}
onChange={(e) => setScaleFactor(parseFloat(e.target.value))}
style={inputStyle}
/>
</fieldset>
<fieldset style={fieldsetStyle}>
<span style={labelStyle}>Icon Size Scale Range</span>
<div style={dualRangeStyle}>
<div style={dualRangeItemStyle}>
<label style={smallLabelStyle}>
Min: {iconSizeMin.toFixed(1)}
</label>
<input
type="range"
min="0.1"
max="5"
step="0.1"
value={iconSizeMin}
onChange={(e) => setIconSizeMin(parseFloat(e.target.value))}
style={inputStyle}
/>
</div>
<div style={dualRangeItemStyle}>
<label style={smallLabelStyle}>
Max: {iconSizeMax.toFixed(1)}
</label>
<input
type="range"
min="0.1"
max="5"
step="0.1"
value={iconSizeMax}
onChange={(e) => setIconSizeMax(parseFloat(e.target.value))}
style={inputStyle}
/>
</div>
</div>
</fieldset>
<fieldset style={fieldsetStyle}>
<span style={labelStyle}>Text Size Scale Range</span>
<div style={dualRangeStyle}>
<div style={dualRangeItemStyle}>
<label style={smallLabelStyle}>
Min: {textSizeMin.toFixed(1)}
</label>
<input
type="range"
min="0.1"
max="5"
step="0.1"
value={textSizeMin}
onChange={(e) => setTextSizeMin(parseFloat(e.target.value))}
style={inputStyle}
/>
</div>
<div style={dualRangeItemStyle}>
<label style={smallLabelStyle}>
Max: {textSizeMax.toFixed(1)}
</label>
<input
type="range"
min="0.1"
max="5"
step="0.1"
value={textSizeMax}
onChange={(e) => setTextSizeMax(parseFloat(e.target.value))}
style={inputStyle}
/>
</div>
</div>
</fieldset>
</div>
</div>
</>
);
};
export default MapboxExample;
このコードスニペットは、
YOUR_MAPBOX_ACCESS_TOKENをあなたのMapboxアカウントのアクセストークンに置き換えるまで、期待通りに動作しません。このexampleは役に立ちましたか?