栅格切片在三个框架中的不同表现
2026-02-08 本文已影响0人
牛老师讲GIS
概述
事情的缘起是上线的系统被用户反馈说底图有点模糊,我知道原因是由于使用的框架是mapboxgl,加载的底图是高德的栅格切片,在框架调用的时候会对图片进行拉伸,但具体是什么表现还不清楚,为了搞明白这个问题,我用node写了一个底图边框展示的代码,叠加到地图上加以对比分析。
结论
- leaflet其核心渲染方式是dom,地图可展示的级别是整数,所以不存在图片的压缩和模糊的;
- mapboxgl渲染方式是webgl,地图展示级别是小数,所以图片存在缩小和放大的情况;
- openlayers渲染方式是canvas 2d,根据不同的版本,其表现不一样,4.6.5以上的版本,地图展示级别是小数,图片存在缩小和放大的情况;
4.被放大后存在底图模糊的情况。
放大和缩小的地图表现
代码
1.切片边框代码
const sharp = require('sharp')
const express = require('express')
console.time('app')
const app = express()
// 自定义跨域中间件
const allowCors = function (req, res, next) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Credentials', 'true');
next();
};
app.use(allowCors);// 使用跨域中间件
app.use(express.static('public'))
app.get('/tile-bbox/:z/:x/:y', async (req, res) => {
try {
const { z, x, y } = req.params
const TILE_SIZE = 256;
const text = `${z}-${x}-${y}`;
// 创建 SVG 内容用于绘制边框和文字
const svg = `
<svg width="${TILE_SIZE}" height="${TILE_SIZE}" xmlns="http://www.w3.org/2000/svg">
<!-- 透明背景 -->
<rect width="${TILE_SIZE}" height="${TILE_SIZE}" fill="none"/>
<!-- 红色边框 -->
<rect x="0" y="0" width="${TILE_SIZE}" height="${TILE_SIZE}"
fill="none" stroke="red" stroke-width="2"/>
<!-- 文字 -->
<text x="${TILE_SIZE/2}" y="${TILE_SIZE/2}"
font-family="Arial, sans-serif" font-size="24"
font-weight="bold" text-anchor="middle"
dominant-baseline="middle" fill="red">
${text}
</text>
</svg>
`;
// 使用 Sharp 处理 SVG 转为 PNG
const buffer = await sharp(Buffer.from(svg))
.png()
.toBuffer();
res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toUTCString());
res.setHeader('Content-Type', 'image/png');
res.send(buffer);
} catch (error) {
console.error('生成瓦片图像失败:', error);
res.status(500).send('生成瓦片图像失败');
}
})
app.listen(18089, () => {
console.timeEnd('app')
console.log('express server running at http://127.0.0.1:18089')
})
2. leaflet调用代码
<body>
<div class="map" id="map">
<div class="tile-size"></div>
</div>
<script>
let layers = [
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1', {
subdomains: '1234'
})
]
let map = L.map('map', {
layers: layers
}).setView([22.52, 113.94], 4);
L.tileLayer('http://127.0.0.1:18089/tile-bbox/{z}/{x}/{y}').addTo(map)
window.map = map
</script>
</body>
3. openlayers代码
<body>
<div class="map" id="map">
<div class="tile-size"></div>
</div>
<script>
const map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://webrd0{1-4}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1'
})
}),
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'http://127.0.0.1:18089/tile-bbox/{z}/{x}/{y}'
})
})
],
view: new ol.View({
center: ol.proj.fromLonLat([113.94, 22.52]), // Longitude, Latitude
zoom: 4,
zoomFactor: 1
})
});
</script>
</body>
4.mapboxgl代码
<body>
<div class="map" id="map">
<div class="tile-size"></div>
</div>
<script>
const style = {
version: 8,
name: "my-map-style",
sources: {
"tile-vec-source": {
type: "raster",
tiles: [
"https://webrd01.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1",
"https://webrd02.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1",
"https://webrd03.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1",
"https://webrd04.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1",
],
tileSize: 256
},
'tile-boundry': {
type: "raster",
tiles: [
'http://127.0.0.1:18089/tile-bbox/{z}/{x}/{y}'
],
tileSize: 256
}
},
layers: [
{
id: "tile-vec-layer",
type: "raster",
source: "tile-vec-source",
},
{
id: "tile-boundry",
type: "raster",
source: "tile-boundry",
},
],
}
const map = new mapboxgl.Map({
container: 'map',
center: [113.94, 22.52],
zoom: 4,
style: style,
// 重要:限制整体缩放范围,通常栅格瓦片有固定的级别
minZoom: 0,
maxZoom: 18,
pitchWithRotate: false
});
map.on('load', () => {
window.map = map;
})
</script>
</body>
5.tile-size样式
<style>
html,
body,
.map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.tile-size {
width: 256px;
height: 256px;
background-color: rgba(0, 0, 255, 0.5);
position: absolute;
border: 1px solid blue;
box-sizing: border-box;
top: calc(50% - 128px);
left: calc(50% - 128px);
z-index: 9;
}
</style>