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); } }
标签:map,const,text,网格,bounds,mapboxgl,lngStep,Math,加载 From: https://www.cnblogs.com/xiaohanyanliu/p/18347311