import mapboxgl from "mapbox-gl";
export default class GridLayer {
constructor(map) {
this.map = map;
this.gridSourceId = "grid-source";
this.gridLayerId = "grid-layer";
this.labelSourceId = "label-source";
this.labelLayerId = "label-layer";
this.initGrid();
this.map.on("moveend", () => this.updateGrid());
this.map.on("zoomend", () => this.updateGrid());
}
initGrid() {
this.map.addSource(this.gridSourceId, {
type: "geojson",
data: this.createGridData()
});
this.map.addLayer({
id: this.gridLayerId,
type: "line",
source: this.gridSourceId,
layout: {},
paint: {
"line-color": "#888",
"line-width": 1
}
});
// label文本显示需要在样式中设置glyphs
this.map.addSource(this.labelSourceId, {
type: "geojson",
data: this.createLabelData()
});
this.map.addLayer({
id: this.labelLayerId,
type: "symbol",
source: this.labelSourceId,
layout: {
"text-field": ["get", "text"],
"text-size": 24,
"text-offset": [0, 0],
"text-font": ["Arial Regular"]
},
paint: {
"text-color": "#fff", // 文本的颜色(可选,默认值为 #000000)
"text-halo-color": "rgb(190,190,190)", // 文本的光晕颜色(可选,默认值为 rgba(0,0,0,0))
"text-halo-width": 3 // 文本的光晕宽度(可选,值 >= 0,默认值为 0,单位:像素)
}
});
}
createGridData() {
const bounds = this.map.getBounds();
const zoom = Math.floor(this.map.getZoom());
const tileCount = Math.pow(2, zoom);
const lngStep = 360 / tileCount;
const minLat = -85.05112878;
const maxLat = 85.05112878;
const minLng = Math.floor(bounds.getWest() / lngStep) * lngStep;
const maxLng = Math.ceil(bounds.getEast() / lngStep) * lngStep;
const lines = [];
for (let lng = minLng; lng <= maxLng; lng += lngStep) {
lines.push({
type: "Feature",
geometry: {
type: "LineString",
coordinates: [
[lng, minLat],
[lng, maxLat]
]
}
});
}
for (let y = 0; y <= tileCount; y++) {
const lat = this.yTile2lat(y, zoom);
lines.push({
type: "Feature",
geometry: {
type: "LineString",
coordinates: [
[minLng, lat],
[maxLng, lat]
]
}
});
}
return {
type: "FeatureCollection",
features: lines
};
}
createLabelData() {
const bounds = this.map.getBounds();
const zoom = Math.floor(this.map.getZoom());
const tileCount = Math.pow(2, zoom);
const lngStep = 360 / tileCount;
// const minLat = -85.05112878;
// const maxLat = 85.05112878;
// const minLng = Math.floor(bounds.getWest() / lngStep) * lngStep;
// const maxLng = Math.ceil(bounds.getEast() / lngStep) * lngStep;
// 通过经纬度计算瓦片信息,保证只绘制可视域范围内的瓦片
let minxyz = this.lngLat2Tile([bounds.getWest(), bounds.getSouth()], zoom);
let maxxyz = this.lngLat2Tile([bounds.getEast(), bounds.getNorth()], zoom);
const labels = [];
for (let x = minxyz.x; x <= maxxyz.x; x++) {
for (let y = maxxyz.y; y <= minxyz.y; y++) {
const lng = this.xTile2lng(x, zoom);
const lat = this.yTile2lat(y, zoom);
const nextLng = this.xTile2lng(x + 1, zoom);
const nextLat = this.yTile2lat(y + 1, zoom);
if (
(lng >= bounds.getWest() || nextLng <= bounds.getEast()) &&
(lat >= bounds.getSouth() || nextLat <= bounds.getNorth())
) {
labels.push({
type: "Feature",
geometry: {
type: "Point",
coordinates: [(lng + nextLng) / 2, (lat + nextLat) / 2]
},
properties: {
text: `Z: ${zoom}\nX: ${x}\nY: ${y}`
}
});
}
}
}
return {
type: "FeatureCollection",
features: labels
};
}
//经纬度转瓦片序列号
lngLat2Tile(coordinates, zoom) {
const { x, y } = mapboxgl.MercatorCoordinate.fromLngLat({
lng: coordinates[0],
lat: coordinates[1]
});
const scale = Math.pow(2, zoom);
const tileX = Math.floor(x * scale);
const tileY = Math.floor(y * scale);
return { x: tileX, y: tileY, z: zoom };
}
xTile2lng(x, zoom) {
return (x / Math.pow(2, zoom)) * 360 - 180;
}
yTile2lat(y, zoom) {
const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, zoom);
return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
}
updateGrid() {
this.map.getSource(this.gridSourceId).setData(this.createGridData());
this.map.getSource(this.labelSourceId).setData(this.createLabelData());
}
removeGridLayer() {
this.map.removeLayer(this.gridLayerId);
this.map.removeSource(this.gridSourceId);
this.map.removeLayer(this.labelLayerId);
this.map.removeSource(this.labelSourceId);
}
}
08-18
08-18
08-18
08-18