geotile_sdk
Version:
A tile sdk for Cloud Optimised Geotiff and shapefile
292 lines (258 loc) • 8.94 kB
JavaScript
/*
* @Description: 将当前瓦片请求以及横向相关区域加入orderList(快队列), 立即处理orderList中的瓦片请求, 调用map.js获取瓦片
* @Author: luojun1
* @Date: 2021-10-21 17:25:12
* @LastEditTime: 2021-12-10 11:46:07
*/
var util = require('util')
var map = require('./map')
var Cache = require('./regionCache')
// 当前正在处理的请求数量
var requestNum = 0
// 一次获取 pad_width×pad_width个瓦片
var padWidth = 2
/** **order类, 某一瓦片相关的order请求和瓦片数据*****/
class Order {
constructor (key, regionNode) {
this.key = key
this.PaddingRegion = regionNode // 所属的regionNode
this.request_recall = [] // 请求此瓦片的回调函数
this.tile_ = undefined // 请求得到的瓦片(暂存)
this.BigTile_ = undefined // 请求得到的大瓦片,当有请求时,才提取出小瓦片
this.offsetX = 0
this.offsetY = 0
}
sendTile (recall) {
if (this.tile_) {
recall(null, this.tile_)
} else if (this.BigTile_) {
// var start1 = new Date().getTime()
map.getBlock(this.BigTile_, this.offsetX, this.offsetY, 256, 256, (err, tile) => {
// var processTime1 = new Date().getTime()
// console.log('###resize:', processTime1 - start1)
if (err) {
console.error('###getBlock:', err)
}
// 将瓦片喂给orderList
tile.encode('png', function (err, PngTile) {
// var processTime2 = new Date().getTime()
// console.log('###encode:', processTime2 - processTime1)
this.tile_ = PngTile
this.BigTile_ = undefined
recall(err, this.tile_)
})
})
}
requestNum--
// preload()
}
// 来了新的请求,先检查瓦片是否就绪,如未就绪,加入等待队列,如已就绪,直接返回
request (recall) {
requestNum++
if (!this.tile_ && !this.BigTile_) {
this.request_recall.push(recall)
} else {
this.sendTile(recall)
}
}
// 瓦片获取成功后,放到order里暂存; 如有等待队列,再喂给等待队列
// 喂小瓦片256*256
feed256Tile (tile) {
this.tile_ = tile
while (this.request_recall.length) {
var recall = this.request_recall.pop()
this.sendTile(recall)
}
}
// 喂大瓦片512*512
feed512Tile (tile, offsetX, offsetY) {
this.BigTile_ = tile
this.offsetX = offsetX
this.offsetY = offsetY
while (this.request_recall.length) {
var recall = this.request_recall.pop()
this.sendTile(recall)
}
}
}
/** orderList, 快队列, 存放瓦片order, 即刻获取瓦片,hashmap便于快速查找, 全局对象***/
// key: xml_uuid_z_x_y"
// value: Order
var orderList = new Map()
function pushOrders (paddingRegion, Z, xmlUUID, regionNode) {
for (var x = paddingRegion[0]; x < paddingRegion[1]; x++) {
for (var y = paddingRegion[2]; y < paddingRegion[3]; y++) {
var key = util.format('%s_%d_%d_%d', xmlUUID, Z, x, y)
if (!orderList.has(key)) orderList.set(key, new Order(key, regionNode))
// console.log('pushOrders:', key)
}
}
}
function closeOrder (x, y, z, xmlUUID) {
var key = util.format('%s_%d_%d_%d', xmlUUID, z, x, y)
if (orderList.has(key)) {
if (orderList.get(key).tile_) {
delete this.tile_
}
orderList.delete(key)
}
}
function cancelOrder (x, y, z, xmlUUID, err) {
var key = util.format('%s_%d_%d_%d', xmlUUID, z, x, y)
if (orderList.has(key)) {
orderList.get(key).feed256Tile(err, [])
orderList.delete(key)
}
}
function cancelOrders (paddingRegion, z, xmlUUID, err) {
for (var x = paddingRegion[0]; x < paddingRegion[1]; x++) {
for (var y = paddingRegion[2]; y < paddingRegion[3]; y++) {
cancelOrder(x, y, z, xmlUUID, err)
}
}
}
function checkOrder (x, y, z, xmlUUID, sendRecall) {
var key = util.format('%s_%d_%d_%d', xmlUUID, z, x, y)
if (orderList.has(key)) {
Cache.updateRegion(orderList.get(key).PaddingRegion)
if (sendRecall) orderList.get(key).request(sendRecall)
console.log('###request: got', key)
return true
} else {
console.log('###request: not', key)
return false
}
}
function feedSmallTile (x, y, z, xmlUUID, tile) {
var key = util.format('%s_%d_%d_%d', xmlUUID, z, x, y)
if (orderList.has(key)) {
orderList.get(key).feed256Tile(tile)
}
}
function feed512Tile (paddingRegion, z, xmlUUID, BigTile) {
for (var x = paddingRegion[0]; x < paddingRegion[1]; x++) {
for (var y = paddingRegion[2]; y < paddingRegion[3]; y++) {
var key = util.format('%s_%d_%d_%d', xmlUUID, z, x, y)
if (orderList.has(key)) {
orderList.get(key).feed512Tile(BigTile, 256 * (x - paddingRegion[0]), 256 * (y - paddingRegion[2]))
}
}
}
}
function pushOrder (paddingRegion, X, Y, Z, xmlUUID, regionNode, sendRecall) {
pushOrders(paddingRegion, Z, xmlUUID, regionNode)
// 添加触发瓦片的请求回调
var key = util.format('%s_%d_%d_%d', xmlUUID, Z, X, Y)
if (orderList.has(key)) orderList.get(key).request(sendRecall)
}
// orderList
// 根据瓦片请求,添加预加载任务
function addPreloadingTask (x, y, z, xmlUUID) {
if (z > 19) {
return
}
// 计算低级别瓦片的范围 region z+1
let lowRegion = [x * 2, x * 2 + 2, y * 2, y * 2 + 2]
let lowLevel = Number(z) + 1
// 检查生成的低级别瓦片是否已经存在于orderList
if (!checkOrder(lowRegion[0], lowRegion[2], lowLevel, xmlUUID)) {
Cache.addVerticalRegion(lowRegion, lowLevel, xmlUUID)
}
}
function hasRegion (Region, Z, xmlUUID, mRegion) {
for (var x = Region[0]; x < Region[1]; x++) {
for (var y = Region[2]; y < Region[3]; y++) {
var key = util.format('%s_%d_%d_%d', xmlUUID, Z, x, y)
if (orderList.has(key)) {
mRegion = orderList.get(key).PaddingRegion
return true
}
}
}
mRegion = null
return false
}
// 周期循环,最多10次: 0.1s执行一次低级别瓦片预加载功能, 执行完判断是否闲时,如是,继续执行预加载
function timeLoop () {
setTimeout(function () {
// 预加载低]级别瓦片
// console.log('requestNum:', requestNum)
if (requestNum <= 0) {
let err = Cache.preloadVerticalRegion((Region, z, xmlUUID) => {
Cache.checkRegionCache(cancelOrders)
// 完善判断条件,避免重复插入region到cache
let _
if (!hasRegion(Region, z, xmlUUID, _)) {
let regionNode = Cache.addRegion(Region, z, xmlUUID)
pushOrders(Region, z, xmlUUID, regionNode)
// 提取padding_region瓦片
var start = new Date().getTime()
map.fetchTiles(Region, z, xmlUUID, (err, BigTile) => {
if (err) {
// 如果获取失败,则取消相关的order,并返回err
console.error('###fetchTiles failed')
cancelOrders(Region, z, xmlUUID, err)
} else {
var processTime = new Date().getTime()
console.log('###preload big tile:', processTime - start)
feed512Tile(Region, z, xmlUUID, BigTile)
}
})
} else {
console.log('<<<hasRegion')
}
})
if (err) {
// console.log('>>>>pop end')
return
}
timeLoop()
}
}, 10)
}
// 外部接口
function preload () {
if (requestNum <= 0) {
timeLoop()
}
}
// 外部接口
function OrderTile (x, y, z, xmlUUID, sendRecall) {
// 纵向扩大提取范围(预加载低级别瓦片)
addPreloadingTask(x, y, z, xmlUUID)
// 检查orderList中是否依旧包含此order,如包含,则无需再请求
if (checkOrder(x, y, z, xmlUUID, sendRecall)) {
return
}
// 横向扩大提取范围
var paddingRegion = [
padWidth * Math.floor(x / padWidth),
padWidth * Math.floor(x / padWidth) + padWidth,
padWidth * Math.floor(y / padWidth),
padWidth * Math.floor(y / padWidth) + padWidth]
// 将要提取的范围存入orderList(订单列表),并调用回调函数发送
Cache.checkRegionCache(cancelOrders)
let regionNode
if (!hasRegion(paddingRegion, z, xmlUUID, regionNode)) {
regionNode = Cache.addRegion(paddingRegion, z, xmlUUID)
}
// var regionNode = Cache.addRegion(paddingRegion, z, xmlUUID)
pushOrder(paddingRegion, x, y, z, xmlUUID, regionNode, sendRecall)
// 提取padding_region瓦片
var start = new Date().getTime()
map.fetchTiles(paddingRegion, z, xmlUUID, (err, BigTile) => {
var processTime = new Date().getTime()
console.log('###fetch big tile:', processTime - start)
if (err) {
// 如果获取失败,则取消相关的order,并返回err
console.error('###fetchTiles failed')
cancelOrders(paddingRegion, z, xmlUUID, err)
} else {
feed512Tile(paddingRegion, z, xmlUUID, BigTile)
}
})
}
module.exports = {
OrderTile,
preload
}