diff --git a/src/TempoLite.vue b/src/TempoLite.vue
index 688c05c..26cc943 100644
--- a/src/TempoLite.vue
+++ b/src/TempoLite.vue
@@ -146,6 +146,20 @@
TEMPO Field of Regard
+
+
+
+
+
-
+
+
+
+
+
+ The cloud mask shows where the satellite could not measure NO2 because of cloud cover.
+
+
+
- TEMPO opacity
+ Overlay opacity
@@ -514,12 +543,13 @@
-
-
-
-
+
+
+
+
+
@@ -555,7 +585,7 @@ import { getTimestamps } from "./timestamps";
const erdTimestamps: number[] = [];
const newTimestamps: number[] = [];
-
+const cloudTimestamps: number[] = [];
const fosterTimestamps = [
1698838920000,
@@ -761,6 +791,13 @@ export default defineComponent({
loadedImagesProgress: 0,
useHighRes: false,
+
+ cloudOverlay: new L.ImageOverlay("", novDecBounds, {
+ opacity,
+ interactive: false,
+ }),
+ cloudTimestamps,
+ showClouds: true,
};
},
@@ -804,6 +841,7 @@ export default defineComponent({
this.singleDateSelected = this.uniqueDays[this.uniqueDays.length-1].value;
this.imageOverlay.setUrl(this.imageUrl).addTo(this.map as Map);
+ this.cloudOverlay.setUrl(this.cloudUrl).addTo(this.map as Map);
this.updateFieldOfRegard();
if (this.showFieldOfRegard) {
@@ -917,6 +955,21 @@ export default defineComponent({
return url + this.imageName;
},
+ cloudUrl(): string {
+ if (!this.showClouds) {
+ return '';
+ }
+
+ if (this.cloudTimestamps.includes(this.timestamp)) {
+ return this.getCloudFilename(this.date);
+ }
+ return '';
+ },
+
+ cloudDataAvailable(): boolean {
+ return this.cloudTimestamps.includes(this.timestamp);
+ },
+
whichDataSet(): string {
if (this.fosterTimestamps.includes(this.timestamp)) {
return 'TEMPO-lite';
@@ -1046,6 +1099,7 @@ export default defineComponent({
},
updateBounds() {
this.imageOverlay.setBounds(this.imageBounds);
+ this.cloudOverlay.setBounds(this.imageBounds);
},
// preloadImages(images: string[]) {
@@ -1058,9 +1112,19 @@ export default defineComponent({
this.erdTimestamps = ts.early_release;
this.newTimestamps = ts.released;
this.timestamps = this.timestamps.concat(this.erdTimestamps, this.newTimestamps).sort();
+ this.cloudTimestamps = ts.clouds;
});
},
+ getCloudFilename(date: Date): string {
+ const filename = this.getTempoFilename(date);
+ if (this.useHighRes) {
+ return 'https://raw.githubusercontent.com/johnarban/tempo-data-holdings/main/clouds/images/' + filename;
+ } else {
+ return 'https://raw.githubusercontent.com/johnarban/tempo-data-holdings/main/clouds/images/resized_images/' + filename;
+ }
+ },
+
getTempoFilename(date: Date): string {
return `tempo_${date.getUTCFullYear()}-${zpad(date.getUTCMonth()+1)}-${zpad(date.getUTCDate())}T${zpad(date.getUTCHours())}h${zpad(date.getUTCMinutes())}m.png`;
},
@@ -1114,6 +1178,8 @@ export default defineComponent({
console.log('preloading images for ', this.thumbLabel);
const times = this.timestamps.slice(this.minIndex, this.maxIndex + 1);
const images = times.map(ts => this.getTempoDataUrl(ts) + this.getTempoFilename(new Date(ts)));
+ const cloudImages = times.filter(ts => this.cloudTimestamps.includes(ts)).map(ts => this.getCloudFilename(new Date(ts)));
+ images.push(...cloudImages);
const promises = _preloadImages(images);
let loaded = 0;
this.loadedImagesProgress = 0;
@@ -1170,6 +1236,10 @@ export default defineComponent({
this.updateFieldOfRegard();
},
+ cloudUrl(url: string) {
+ this.cloudOverlay.setUrl(url);
+ },
+
useHighRes() {
this.imagePreload();
},
@@ -1221,6 +1291,7 @@ export default defineComponent({
opacity(value: number) {
this.imageOverlay.setOpacity(value);
+ this.cloudOverlay.setOpacity(value);
}
}
});
@@ -1461,12 +1532,12 @@ ul {
grid-row: 4 / 6;
}
- #body-logos {
- grid-column: 3 / 4;
- grid-row: 5 / 6;
- align-self: end;
- justify-self: end;
- }
+ // #body-logos {
+ // grid-column: 3 / 4;
+ // grid-row: 5 / 6;
+ // align-self: end;
+ // justify-self: end;
+ // }
}
// style the content
@@ -1599,6 +1670,13 @@ a {
width: 250px;
border: 2px solid black;
}
+
+ #map-show-hide-clouds {
+ z-index: 1000;
+ position: absolute;
+ top: 1rem;
+ right: 80px;
+ }
#map-legend {
position: absolute;
@@ -1718,7 +1796,7 @@ a {
width: 100%;
display: flex;
flex-direction: column;
- gap: 10px;
+ gap: 5px;
}
#opacity-slider-container {
@@ -1742,8 +1820,10 @@ a {
}
#body-logos {
+ margin-bottom: -1rem;
display: flex;
flex-direction: row;
+ justify-content: flex-end;
img {
height: 35px !important;
vertical-align: middle;
@@ -1914,10 +1994,10 @@ i.mdi-menu-down {
grid-row: 6 / 7;
}
- #body-logos {
- grid-column: 1 / 2;
- grid-row: 7 / 8;
- }
+ // #body-logos {
+ // grid-column: 1 / 2;
+ // grid-row: 7 / 8;
+ // }
}
.content-with-sidebars {
diff --git a/src/timestamps.ts b/src/timestamps.ts
index 75be019..820f900 100644
--- a/src/timestamps.ts
+++ b/src/timestamps.ts
@@ -10,6 +10,11 @@ export interface Manifest {
resized_image_directory: string;
timestamps: number[];
};
+ clouds: {
+ image_directory: string;
+ resized_image_directory: string;
+ timestamps: number[];
+ };
}
export async function fetchManifest(): Promise {
@@ -20,11 +25,13 @@ export async function fetchManifest(): Promise {
interface Timestamps {
early_release: number[];
released: number[];
+ clouds: number[];
}
export async function getTimestamps(): Promise {
const manifest = await fetchManifest();
const earlyRelease = manifest.early_release;
const released = manifest.released;
- return { early_release: earlyRelease.timestamps, released: released.timestamps };
+ const clouds = manifest.clouds;
+ return { early_release: earlyRelease.timestamps, released: released.timestamps, clouds: clouds.timestamps };
}