GIS地图-地图离线
地图离线
地图离线可以采用SQLite(indexDB,瓦片以图片方式存入手机等其他方式也可实现)。实现地图离线的重要方式为mapbox的 transformRequest,它可以在地图请求外部URL之前运行回调。回调可用于拦截修改url。无网时我们可以通过transformRequest向框架发送模拟请求。
下载瓦片我这里使用的是fetch请求,原因是demo里是为了演示地图缓存,只要是查看过的区域就会自动缓存,后续可能会需要像奥维地图一样,有框选某片区域缓存离线瓦片的需求,那么也可以利用框选区域的zxy来fetch请求实现缓存。
有网时缓存瓦片到SQLite数据库
js
/**
* 设置瓦片请求监听
*
* transformRequest也可实现请求监听,这里为了不影响正常的瓦片加载流程,同时可以异步地进行缓存操作,使用了Mapbox的事件监听事件
* 监听两个事件:1、sourcedata:当地图源数据发生变化时触发 2、data:当地图数据加载时触发。真实业务里面我们要缓存不止地图瓦片还有地图上的图层数据,等等其他数据,
*
* @param {string[]} sources 需要缓存的瓦片源
*/
setupTileListener(sources) {
const handleTileEvent = (e) => {
if (e.sourceId && sources.includes(e.sourceId) && e.tile) {
const source = this.map.getSource(e.sourceId)
if (source && source.tiles) {
// 获取到标准化的瓦片坐标(z/x/y)
const coords = e.tile.tileID.canonical // TILEMATRIX={z} 对应缩放级别
// TILECOL={x} 对应x坐标
// TILEROW={y} 对应y坐标
const url = source.tiles[0]
.replace('{z}', coords.z)
.replace('{x}', coords.x)
.replace('{y}', coords.y)
// console.log('🚀 ~ [瓦片请求:]', url, '🚀 ~ [source:]', e.sourceId)
downloadAndCacheTile(url, coords.z, coords.x, coords.y, e.sourceId)
}
}
}
this.map.on('sourcedata', handleTileEvent)
this.map.on('data', handleTileEvent)
}/**
* 下载并缓存瓦片
* @param {string} url 瓦片URL
* @param {number} z 缩放级别
* @param {number} x X坐标
* @param {number} y Y坐标
* @param {string} source 瓦片源
*/
export const downloadAndCacheTile = async (url, z, x, y, source) => {
try {
// 先检查瓦片是否已存在
const exists = await checkTileExists(z, x, y, source)
if (exists) {
// console.log('🚀 ~ [瓦片已存在,跳过下载:]', `z=${z}, x=${x}, y=${y}, source=${source}`)
return null
}
// console.log('🚀 ~ [开始下载瓦片:]', url)
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const blob = await response.blob()
// console.log('🚀 ~ [获取到的瓦片大小:]', blob.size, 'bytes')
const base64Data = await new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onloadend = () => resolve(reader.result)
reader.onerror = reject
reader.readAsDataURL(blob)
})
// console.log('🚀 ~ [Base64数据长度:]', base64Data.length)
await saveTile(url, z, x, y, base64Data, source)
return base64Data
} catch (error) {
console.log('🚀 ~ [下载瓦片失败:]', error)
return null
}
}
有网时的请求链接

无网加载SQLite中瓦片
js
async createOfflineMap(container, source, options = {}) {
// 首先从SQLite缓存获取所有瓦片
const tiles = await getAllTiles(source)
const tileData = formatTileData(tiles)
// 使用特殊的瓦片源配置
const defaultOptions = {
style: {
sources: {
'offline-tiles': {
type: 'raster',
// 关键点1:使用特殊格式的虚拟URL(前缀可以随意定义,我们只需要和transformRequest中一样就行)
tiles: [
'data:image/png;base64,{z}/{x}/{y}'
],
tileSize: 256
}
}
},
// 通过 transformRequest 拦截和处理这个虚拟URL
transformRequest: (url, resourceType) => {
// 关键点2:识别虚拟URL请求
if (resourceType === 'Tile' && url.includes('data:image/png;base64,')) {
// 关键点3:从虚拟URL中提取瓦片坐标
const parts = url.replace('data:image/png;base64,', '').split('/')
const key = parts.join('/')
// 关键点4:返回实际的缓存瓦片数据,框架会自己处理加载瓦片
if (tileData[key]) {
return { url: tileData[key] }
}
return { cancel: true }
}
return { url }
}
}
}
无网时模拟的请求链接

数据库中的缓存数据

- 通过数据库工具查看表结构
