jsfreemaplib
Version:
Miscellaneous JavaScript classes and functions for working with geodata, including working with Spherical Mercator projection and managing XYZ tiles, plus some general UI functions.
118 lines (103 loc) • 3.88 kB
JavaScript
const GoogleProjection = require('./GoogleProjection');
const Tile = require('./Tile');
// changes for 0.3.0:
//
// - Removed tilesLoaded array and replaceed by indexedTiles object,
// indexing each tile of data with the z/x/y index of the tile.
//
// - added getData() method to download (if necessary) and return the data
// corresponding to the tile at a given position.
//
// - added overridable rawTileToStoredTile() method to allow something other
// than the raw tiled data (e.g. a DEM object) to be stored
class Tiler {
constructor(url) {
this.tile = new Tile(0, 0, 13);
this.url = url;
this.sphMerc = new GoogleProjection();
this.indexedTiles = { };
}
setZoom(z) {
this.tile.z = z;
}
lonLatToSphMerc(lon, lat) {
return this.sphMerc.project(lon, lat);
}
getTile(sphMercPos, z) {
return this.sphMerc.getTile(sphMercPos, z);
}
async update(pos) {
const loadedData = [];
let t;
if( t = this.needNewData(pos)) {
const tilesX = [t.x, t.x-1, t.x+1], tilesY = [t.y, t.y-1, t.y+1];
for(let ix=0; ix<tilesX.length; ix++) {
for(let iy=0; iy<tilesY.length; iy++) {
const thisTile = new Tile(tilesX[ix], tilesY[iy], t.z);
const data = await this.loadTile(thisTile);
if(data !== null) {
loadedData.push({ data: data, tile: thisTile });
}
}
}
}
return loadedData;
}
getCurrentTiles() {
const tiles = [];
const tilesX = [this.tile.x, this.tile.x-1, this.tile.x+1], tilesY = [this.tile.y, this.tile.y-1, this.tile.y+1];
for(let ix=0; ix<tilesX.length; ix++) {
for(let iy=0; iy<tilesY.length; iy++) {
const data = this.indexedTiles[`${this.tile.z}/${tilesX[ix]}/${tilesY[iy]}`];
if(data !== null) {
tiles.push({ data: data, tile: new Tile(ix, iy, this.tile.z) });
}
}
}
return tiles;
}
needNewData(pos) {
if(this.tile) {
const newTile = this.sphMerc.getTile(pos, this.tile.z);
const needUpdate = newTile.x != this.tile.x || newTile.y != this.tile.y;
this.tile = newTile;
return needUpdate ? newTile : false;
}
return false;
}
async loadTile(tile) {
const tileIndex = `${tile.z}/${tile.x}/${tile.y}`;
if(this.indexedTiles[tileIndex] === undefined) {
const tData = await this.readTile(this.url
.replace("{x}", tile.x)
.replace("{y}", tile.y)
.replace("{z}", tile.z)
);
this.indexedTiles[tileIndex] = this._rawTileToStoredTile(tile, tData); // now add the data to indexedTiles
return this.indexedTiles[tileIndex];
}
return null;
}
async readTile(url) {
return null;
}
// This method was unintentionally duplicated but will not remove until
// 0.5.x to avoid breaking anything
projectLonLat(lon, lat) {
return this.lonLatToSphMerc(lon, lat);
}
// new for 0.3.0
// for a given sphmerc pos, downloads data if necessary and returns
// the data at the tile corresponding to that position
async getData (sphMercPos, z=13) {
await this.update(sphMercPos);
const thisTile = this.sphMerc.getTile(sphMercPos, z);
return this.indexedTiles[`${z}/${thisTile.x}/${thisTile.y}`];
}
// can be overridden if we want to store something other than the raw data
// (for example DEM objects if we are dealing with DEM tiles)
_rawTileToStoredTile(tile, data) {
return data;
}
}
module.exports = Tiler;