Skip to content

Commit

Permalink
Use class for Tile
Browse files Browse the repository at this point in the history
  • Loading branch information
steinbro committed Nov 29, 2024
1 parent 8562446 commit 8eeaa28
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 74 deletions.
14 changes: 7 additions & 7 deletions src/composables/tile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import fs from 'fs';
import nock from 'nock';
import { expect } from "chai";
import {
Tile,
createBoundingBox,
createTile,
latLonToTileCoords,
enumerateTilesAround,
} from "./tile";
Expand Down Expand Up @@ -51,11 +51,10 @@ describe('Tile', () => {
// 10m radius around Washington Monument
const result = enumerateTilesAround(38.889444, -77.035278, 10)
expect(result.length).to.equal(1);
expect(result[0]).to.include({
expect(result[0].coordinates).to.deep.equal({
x: 18744,
y: 25072,
z: 16,
key: '16/18744/25072',
});
});

Expand All @@ -72,12 +71,12 @@ describe('Tile', () => {
});

it('should be true when tile has not been fetched', async () => {
let tile = createTile(18109, 23965, 16);
let tile = new Tile({ x: 18109, y: 23965, z: 16 });
expect(await tile.shouldRefresh()).to.be.true;
});

it('should be false when tile has been fetched recently', async () => {
let tile = createTile(18109, 23965, 16);
let tile = new Tile({ x: 18109, y: 23965, z: 16 });
cache.updateLastFetch(tile.url);
expect(await tile.shouldRefresh()).to.be.false;
});
Expand Down Expand Up @@ -108,12 +107,13 @@ describe('Tile', () => {
.get('/16/18109/23965.json')
.reply(200, tileData);

await createTile(18109, 23965, 16).load();
let tile = new Tile({ x: 18109, y: 23965, z: 16 });
await tile.load();
expect(scope.isDone()).to.be.true;
});

it('should not re-request fresh tile data', async () => {
let tile = createTile(18109, 23965, 16);
let tile = new Tile({ x: 18109, y: 23965, z: 16 });
// Pretend we just loaded tile data
cache.updateLastFetch(tile.url);

Expand Down
126 changes: 59 additions & 67 deletions src/composables/tile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,65 @@ interface TileCoordinates {
z: number;
}

interface Tile extends TileCoordinates {
export class Tile {
coordinates: TileCoordinates;
key: string;
url: string,
shouldRefresh: () => Promise<boolean>;
load: () => Promise<void>;
getFeatures: () => Promise<SoundscapeFeature[]>;
url: string;

constructor(coords: TileCoordinates) {
this.coordinates = coords;
this.key = `${coords.z}/${coords.x}/${coords.y}`;
this.url = `${config.tileServer}/${this.key}.json`;
}

async shouldRefresh(): Promise<boolean> {
// Check if the cached entry is still valid based on maxAgeMilliseconds
const currentTime = new Date().getTime();
const lastFetchTime = await cache.lastFetchTime(this.url);

return (
lastFetchTime === null ||
currentTime - lastFetchTime > maxAgeMilliseconds
);
}

async load(): Promise<void> {
if (tilesInProgressOrDone.has(this.key)) {
// no need to request again
return;
}
tilesInProgressOrDone.add(this.key);

if (!await this.shouldRefresh()) {
// Data is still fresh; no need to refresh
return;
}

// Delete any stale features
cache.deleteFeatures(this.key);

try {
// Fetch the URL since it's not in the cache or has expired
const response = await fetch(this.url);
console.log("Fetched: ", this.url);
const data = await response.json();
if (data?.features) {
for (const feature of data.features) {
await cache.addFeature(feature, this.key);
}
console.log(`Loaded ${data.features.length} new features.`);
cache.updateLastFetch(this.url);
}
} catch (error) {
console.error(error);
// should be retried when next needed
tilesInProgressOrDone.delete(this.key);
}
}

async getFeatures(): Promise<SoundscapeFeature[]> {
return cache.getFeatures(this.key);
}
}

// Function to create a half-kilometer bounding box around a point
Expand Down Expand Up @@ -92,67 +145,6 @@ export function enumerateTilesInBoundingBox(
return tiles;
}

export function createTile(x: number, y: number, z: number): Tile {
const tile: Tile = {
x,
y,
z,
key: `${z}/${x}/${y}`,
url: `${config.tileServer}/${z}/${x}/${y}.json`,

shouldRefresh: async function(): Promise<boolean> {
// Check if the cached entry is still valid based on maxAgeMilliseconds
const currentTime = new Date().getTime();
const lastFetchTime = await cache.lastFetchTime(this.url);

return (
lastFetchTime === null ||
currentTime - lastFetchTime > maxAgeMilliseconds
);
},

load: async function(): Promise<void> {
if (tilesInProgressOrDone.has(tile.key)) {
// no need to request again
return;
}
tilesInProgressOrDone.add(tile.key);

if (!await this.shouldRefresh()) {
// Data is still fresh; no need to refresh
return;
}

// Delete any stale features
cache.deleteFeatures(this.key);

try {
// Fetch the URL since it's not in the cache or has expired
const response = await fetch(this.url);
console.log("Fetched: ", this.url);
const data = await response.json();
if (data?.features) {
for (const feature of data.features) {
await cache.addFeature(feature, tile.key);
}
console.log(`Loaded ${data.features.length} new features.`);
cache.updateLastFetch(this.url);
}
} catch (error) {
console.error(error);
// should be retried when next needed
tilesInProgressOrDone.delete(tile.key);
}
},

getFeatures: async function(): Promise<SoundscapeFeature[]> {
return cache.getFeatures(tile.key);
},
};

return tile;
}

export function enumerateTilesAround(
latitude: number,
longitude: number,
Expand All @@ -161,5 +153,5 @@ export function enumerateTilesAround(
// Find all tiles within radiusMeters radius of location
const boundingBox = createBoundingBox(latitude, longitude, radiusMeters);
return enumerateTilesInBoundingBox(boundingBox, zoomLevel, zoomLevel)
.map(tile => createTile(tile.x, tile.y, tile.z));
.map(coords => new Tile(coords));
}

0 comments on commit 8eeaa28

Please sign in to comment.