Skip to content
Snippets Groups Projects
Commit 0bf9a62a authored by jonschi's avatar jonschi
Browse files

load data from main app and present loading screen first

parent 799a7d1d
Branches
No related tags found
No related merge requests found
......@@ -3,75 +3,139 @@ import { loadJsonFile } from "./helpers/fileLoader";
import Globe from "./components/Globe.vue";
import Sidebar from "./components/Sidebar.vue";
import VCenter from "./components/VCenter.vue";
import Spinner from "./components/Spinner.vue";
import { ref } from "vue";
import { computed } from "@vue/reactivity";
import moment from 'moment'
import moment from "moment";
moment.updateLocale("en", { week: {
// define moment locale and some constants
moment.updateLocale("en", {
week: {
dow: 1, // First day of week is Monday
doy: 4 // First week of year must contain 4 January (7 + 1 - 4)
}});
doy: 4, // First week of year must contain 4 January (7 + 1 - 4)
},
});
const geoDataFile = "../src/assets/NUTS_RG_60M_2021_4326.geojson";
const covidDataFile = "../src/assets/sample-covid-data-2022-13.json";
const initialDate = "2022-03-14T00:00:00.000Z"; // TODO: determine last date somehow?
const artificialLoadingTime = 3000;
const loading = ref(true);
const geoData = ref({});
const covidData = ref({});
const selectedNutsCode = ref(null) // TODO: remove hardcoded value
const selectedDate = ref(new Date('2022-03-14T00:00:00.000Z')) // TODO: determine last date somehow?
const selectedNutsCode = ref(null);
const selectedDate = ref(null);
const init = async function() {
covidData.value = await loadJsonFile(
"../src/assets/sample-covid-data-2022-13.json"
);
console.log("fetched covid data")
selectedDate.value = new Date('2022-03-14T00:00:00.000Z') // TODO: improve this so i dont have to set this here again in order to render after awaiting this
Promise.all([loadJsonFile(geoDataFile), loadJsonFile(covidDataFile)]).then(
([loadedGeoData, loadedCovidData]) => {
geoData.value = loadedGeoData;
covidData.value = loadedCovidData;
selectedDate.value = new Date(initialDate);
// give three.js time to draw the globe
setTimeout(() => {
loading.value = false;
}, artificialLoadingTime);
}
);
const search = function () {
// TODO: for now we just empty the array completely to see how the interactivity works
covidData.value = covidData.value.splice(0, covidData.value.length)
}
// TODO: implement search
};
const regionSelected = function (nuts) {
selectedNutsCode.value = nuts
}
selectedNutsCode.value = nuts;
};
const dataLoaded = computed(() => {
return selectedDate.value != null;
});
const setSelectedDate = function (date) {
let momentDate = date._isAMomentObject ? date : moment(date)
momentDate.utc(true)
momentDate.startOf('week')
if (momentDate.isDST()) momentDate.add(1, 'h')
selectedDate.value = momentDate.toDate()
}
let momentDate = date._isAMomentObject ? date : moment(date);
momentDate.utc(true);
momentDate.startOf("week");
if (momentDate.isDST()) momentDate.add(1, "h");
selectedDate.value = momentDate.toDate();
};
const oneWeekBack = function () {
let momentDate = moment(selectedDate.value)
momentDate.subtract(1, 'w')
setSelectedDate(momentDate.toDate())
}
let momentDate = moment(selectedDate.value);
momentDate.subtract(1, "w");
setSelectedDate(momentDate.toDate());
};
const oneWeekForward = function () {
let momentDate = moment(selectedDate.value)
momentDate.add(1, 'w')
setSelectedDate(momentDate.toDate())
}
let momentDate = moment(selectedDate.value);
momentDate.add(1, "w");
setSelectedDate(momentDate.toDate());
};
const jumpCurrentWeek = function () {
let momentDate = moment()
momentDate.utc(true)
momentDate.startOf('week')
setSelectedDate(momentDate.toDate())
}
init();
let momentDate = moment();
momentDate.utc(true);
momentDate.startOf("week");
setSelectedDate(momentDate.toDate());
};
</script>
<template>
<div>
<Sidebar :covid-data="covidData" :selectedNutsCode="selectedNutsCode" :selected-date="selectedDate" @date-selected="setSelectedDate" @one-week-back="oneWeekBack" @one-week-forward="oneWeekForward" @jumpCurrentWeek="jumpCurrentWeek" @search="search"/>
<Globe :covid-data="covidData" :selected-date="selectedDate" @region-selected="regionSelected" />
<div v-if="dataLoaded">
<Sidebar
:covid-data="covidData"
:selectedNutsCode="selectedNutsCode"
:selected-date="selectedDate"
@date-selected="setSelectedDate"
@one-week-back="oneWeekBack"
@one-week-forward="oneWeekForward"
@jumpCurrentWeek="jumpCurrentWeek"
@search="search"
/>
<Globe
:geo-data="geoData"
:covid-data="covidData"
:selected-date="selectedDate"
@region-selected="regionSelected"
/>
</div>
<transition name="fade">
<div v-if="loading" class="loading-container">
<VCenter>
<Spinner />
<br />
<br />
<h3 class="title is-3">cargando....</h3>
</VCenter>
</div>
</transition>
</div>
</template>
<style>
body {
margin: 0;
background-color: black;
}
.title.is-3 {
color: white;
}
.loading-container {
background-color: black;
min-height: 100vh;
width: 100%;
height: 100%;
position: absolute;
z-index: 2;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 1s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>
<script setup>
import { nextTick, onMounted, watch } from "@vue/runtime-core"
import * as THREE from "three"
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"
import { VueElement, ref } from "vue"
import { loadJsonFile } from "../helpers/fileLoader.js"
import { nextTick, onMounted, watch } from "@vue/runtime-core";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { VueElement, ref } from "vue";
const props = defineProps({
geoData: Object,
covidData: Object,
selectedDate: Date,
})
const emit = defineEmits(['regionSelected'])
});
watch(() => props.selectedDate, () => {
addAllRegions()
})
const emit = defineEmits(["regionSelected"]);
// Create loaders and globally usable variables for data
const texLoader = new THREE.TextureLoader();
let geoData, covidData;
let tooltipElement;
let tooltipString = ref("");
......@@ -82,10 +77,10 @@ let ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
// Create globe mesh
let mapMaterial = texLoader.load("../src/assets/8081_earthmap10k.jpeg");
let earthRadius = 300;
let segments = 128;
let rings = 128;
let mapMaterial = texLoader.load("../src/assets/8081_earthmap10k.jpeg");
let geometry = new THREE.SphereGeometry(earthRadius, segments, rings);
mapMaterial.wrapS = THREE.RepeatWrapping;
mapMaterial.wrapT = THREE.RepeatWrapping;
......@@ -103,19 +98,24 @@ scene.add(mesh);
function createPathStrings(filename) {
const fileType = ".png";
const sides = ["ft", "bk", "up", "dn", "rt", "lf"];
const pathStings = sides.map(side => {
const pathStings = sides.map((side) => {
return filename + "_" + side + fileType;
});
return pathStings;
}
function createMaterialArray(filename) {
const skyboxImagepaths = createPathStrings(filename)
const materialArray = skyboxImagepaths.map(image => {
let texture = texLoader.load(image)
return new THREE.MeshBasicMaterial({ map: texture, side: THREE.BackSide, transparent: true, opacity: 0.5 })
})
return materialArray
const skyboxImagepaths = createPathStrings(filename);
const materialArray = skyboxImagepaths.map((image) => {
let texture = texLoader.load(image);
return new THREE.MeshBasicMaterial({
map: texture,
side: THREE.BackSide,
transparent: true,
opacity: 0.5,
});
});
return materialArray;
}
// Draw a single region
......@@ -174,17 +174,17 @@ function addAllRegions() {
rootObject = new THREE.Object3D();
scene.add(rootObject);
geoData.features.forEach(function(region) {
props.geoData.features.forEach(function (region) {
const regionNutsCode = region.properties.NUTS_ID;
if (props.covidData == null || Object.keys(props.covidData) == 0) {
console.log("early return", props.covidData == null)
console.log("early return", props.covidData == null);
return;
}
const covidDataForDate = props.covidData[props.selectedDate.toJSON()]
const covidDataForDate = props.covidData[props.selectedDate.toJSON()];
if (!covidDataForDate) {
debugger
debugger;
}
const covidDataWeek = covidDataForDate[regionNutsCode]
const covidDataWeek = covidDataForDate[regionNutsCode];
if (!covidDataWeek) {
// could not covid data but a region is available
......@@ -230,12 +230,12 @@ onMounted(async () => {
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.05;
controls.screenSpacePanning = true;
controls.zoomSpeed = 0.2
controls.minDistance = 350
controls.maxDistance = 2000
controls.zoomSpeed = 0.2;
controls.minDistance = 350;
controls.maxDistance = 2000;
let container = document.querySelector('#container')
container.style.right = `${offset}px`
let container = document.querySelector("#container");
container.style.right = `${offset}px`;
// Setup skybox
const materialArray = createMaterialArray("../src/assets/skybox/corona");
......@@ -270,9 +270,13 @@ onMounted(async () => {
renderer.setSize(sizes.width, sizes.height);
});
canvas.addEventListener('click', () => {
emit('regionSelected', intersectedObject?.name)
}, false)
canvas.addEventListener(
"click",
() => {
emit("regionSelected", intersectedObject?.name);
},
false
);
const tick = () => {
renderer.autoClear = true;
......@@ -307,7 +311,8 @@ onMounted(async () => {
// update text, if it has a "name" field. This will contain the nuts code of the intersected region
const name = intersects[0].object.name;
if (name) {
var covidDataElement = props.covidData[props.selectedDate.toJSON()][name]
var covidDataElement =
props.covidData[props.selectedDate.toJSON()][name];
tooltipString.value = `${covidDataElement.region}: ${covidDataElement.incidence}`;
}
} // else: it is the same object that is already intersected, so dont do anything
......@@ -325,8 +330,7 @@ onMounted(async () => {
window.requestAnimationFrame(tick); // request a new animation frame
};
geoData = await loadJsonFile("../src/assets/NUTS_RG_60M_2021_4326.geojson");
watch(() => props.selectedDate, addAllRegions, { immediate: true });
tick();
});
</script>
......
<script setup>
// adapted from https://loading.io/css/
</script>
<template>
<div class="lds-spinner">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</template>
<style scoped>
.lds-spinner {
color: official;
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-spinner div {
transform-origin: 40px 40px;
animation: lds-spinner 1.2s linear infinite;
}
.lds-spinner div:after {
content: " ";
display: block;
position: absolute;
top: 3px;
left: 37px;
width: 6px;
height: 18px;
border-radius: 20%;
background: #fff;
}
.lds-spinner div:nth-child(1) {
transform: rotate(0deg);
animation-delay: -1.1s;
}
.lds-spinner div:nth-child(2) {
transform: rotate(30deg);
animation-delay: -1s;
}
.lds-spinner div:nth-child(3) {
transform: rotate(60deg);
animation-delay: -0.9s;
}
.lds-spinner div:nth-child(4) {
transform: rotate(90deg);
animation-delay: -0.8s;
}
.lds-spinner div:nth-child(5) {
transform: rotate(120deg);
animation-delay: -0.7s;
}
.lds-spinner div:nth-child(6) {
transform: rotate(150deg);
animation-delay: -0.6s;
}
.lds-spinner div:nth-child(7) {
transform: rotate(180deg);
animation-delay: -0.5s;
}
.lds-spinner div:nth-child(8) {
transform: rotate(210deg);
animation-delay: -0.4s;
}
.lds-spinner div:nth-child(9) {
transform: rotate(240deg);
animation-delay: -0.3s;
}
.lds-spinner div:nth-child(10) {
transform: rotate(270deg);
animation-delay: -0.2s;
}
.lds-spinner div:nth-child(11) {
transform: rotate(300deg);
animation-delay: -0.1s;
}
.lds-spinner div:nth-child(12) {
transform: rotate(330deg);
animation-delay: 0s;
}
@keyframes lds-spinner {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
</style>
......@@ -2,33 +2,30 @@
</script>
<template>
<div class="outer">
<div class="middle">
<div class="inner">
<div class="">
<div class="columns is-centered">
<div class="vertical-center">
<div class="column">
<slot />
</div>
</div>
</div>
</div>
</template>
<style scoped>
.outer {
display: table;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
.container {
text-align: center;
height: 90%;
position: relative;
}
.middle {
display: table-cell;
vertical-align: middle;
}
.inner {
margin-left: auto;
margin-right: auto;
width: 400px;
.vertical-center {
text-align: center;
margin: 0;
position: absolute;
top: 50%;
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
</style>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment