Skip to content

Commit 78c31be

Browse files
committed
Merge branch 'main' into feature/msavi-endpoint
2 parents 526b1e9 + 8b13ba6 commit 78c31be

15 files changed

+938
-17
lines changed

.github/workflows/deploy.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ jobs:
6161
working-directory: ./backend
6262
env:
6363
API_BASE_URL: "http://127.0.0.1:8000"
64+
GOOGLE_EARTH_ENGINE_SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.GOOGLE_EARTH_ENGINE_SERVICE_ACCOUNT_CREDENTIALS }}
65+
GOOGLE_EARTH_ENGINE_SERVICE_ACCOUNT_EMAIL: ${{ secrets.GOOGLE_EARTH_ENGINE_SERVICE_ACCOUNT_EMAIL }}
66+
ENVIRONMENT: "PRODUCTION"
6467
run: |
6568
python -m pip install --upgrade pip
6669
pip install -r requirements-dev.txt
@@ -81,7 +84,11 @@ jobs:
8184
--allow-unauthenticated \
8285
--platform=managed \
8386
--min-instances=1 \
84-
--max-instances=5
87+
--max-instances=5 \
88+
--set-env-vars "GOOGLE_EARTH_ENGINE_SERVICE_ACCOUNT_CREDENTIALS=${{ secrets.GOOGLE_EARTH_ENGINE_SERVICE_ACCOUNT_CREDENTIALS }}" \
89+
--set-env-vars "GOOGLE_EARTH_ENGINE_SERVICE_ACCOUNT_EMAIL=${{ secrets.GOOGLE_EARTH_ENGINE_SERVICE_ACCOUNT_EMAIL }}" \
90+
--set-env-vars "ENVIRONMENT=PRODUCTION"
91+
8592
8693
# Step 9: Build and Deploy the frontend
8794
- name: Build Docker image (frontend)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<template>
2+
<v-container>
3+
<h2 class="pb-10">Temperature vs. NDVI</h2>
4+
<v-row justify="center">
5+
<div
6+
id="plotlyGraphTemperatureNdvi"
7+
style="width: 100%; height: 400px"
8+
class="d-flex justify-center"
9+
></div>
10+
</v-row>
11+
</v-container>
12+
</template>
13+
14+
<script>
15+
import axios from 'axios'
16+
import Plotly from 'plotly.js-dist-min'
17+
import { onMounted, ref, render } from 'vue'
18+
19+
export default {
20+
name: 'TemperatureNdviBarAndLine',
21+
setup() {
22+
const temperatureData = ref(null)
23+
const ndviData = ref(null)
24+
const startDate = ref(1514761200) // 2018-01-01
25+
const endDate = ref(1733007599) // 2024-11-30
26+
const temporalResolution = ref("Monthly") // options: "Daily", "Monthly"
27+
const aggregation = ref("Mean") // options: "Mean", "Median", "Max", "Min"
28+
29+
const fetchTemperatureData = async () => {
30+
const apiUrl = 'https://thf-climate-run-1020174331409.europe-west3.run.app/weather/index'
31+
const params = {
32+
weatherVariable: "temperature_2m",
33+
startDate: startDate.value,
34+
endDate: endDate.value,
35+
location: "TEMPELHOFER_FELD",
36+
temporalResolution: temporalResolution.value.toUpperCase(),
37+
aggregation: aggregation.value.toUpperCase()
38+
}
39+
40+
try {
41+
const response = await axios.get(apiUrl, { params })
42+
temperatureData.value = response.data
43+
renderPlot()
44+
} catch (error) {
45+
console.error("Error fetching temperature data:", error)
46+
}
47+
}
48+
49+
const fetchNdviData = async () => {
50+
const apiUrl = 'https://thf-climate-run-1020174331409.europe-west3.run.app/index/ndvi'
51+
const params = {
52+
startDate: startDate.value,
53+
endDate: endDate.value,
54+
location: "TEMPELHOFER_FELD",
55+
temporalResolution: temporalResolution.value.toUpperCase(),
56+
aggregation: aggregation.value.toUpperCase()
57+
}
58+
59+
try {
60+
const response = await axios.get(apiUrl, { params })
61+
ndviData.value = response.data
62+
renderPlot()
63+
} catch (error) {
64+
console.error("Error fetching NDVI data:", error)
65+
}
66+
}
67+
68+
const renderPlot = () => {
69+
if (temperatureData.value && ndviData.value) {
70+
const tempTimestamps = temperatureData.value.data.map(entry =>
71+
new Date(entry.timestamp * 1000).toISOString().split('T')[0]
72+
)
73+
const tempValues = temperatureData.value.data.map(entry => entry.value)
74+
const ndviTimestamps = ndviData.value.data.map(entry => new Date(entry.timestamp * 1000))
75+
const ndviValues = ndviData.value.data.map(entry => entry.value)
76+
77+
const tempTrace = {
78+
x: tempTimestamps,
79+
y: tempValues,
80+
type: 'bar', // can change to lines+markers
81+
name: 'Temperature (°C)',
82+
marker: { color: 'red' }
83+
};
84+
85+
const ndviTrace = {
86+
x: ndviTimestamps,
87+
y: ndviValues,
88+
mode: 'lines+markers',
89+
name: 'NDVI',
90+
line: { color: 'blue' },
91+
yaxis: 'y2',
92+
};
93+
94+
const layout = {
95+
title: 'NDVI vs. Monthly Temperature for Tempelhofer Feld (2018-2024)',
96+
xaxis: { title: 'Date', type: 'date', rangeslider: { visible: true } },
97+
yaxis: { title: 'Temperature (°C)' },
98+
yaxis2: {
99+
title: 'NDVI',
100+
overlaying: 'y',
101+
side: 'right',
102+
},
103+
legend: { x: 1.1, y: 0.5 },
104+
template: 'plotly_white'
105+
};
106+
107+
Plotly.newPlot('plotlyGraphTemperatureNdvi', [tempTrace, ndviTrace], layout);
108+
}
109+
}
110+
111+
onMounted(() => {
112+
fetchTemperatureData()
113+
fetchNdviData()
114+
})
115+
}
116+
}
117+
</script>
118+
119+
<style scoped></style>
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<template>
2+
<v-container>
3+
<v-row justify="center" class="pb-16">
4+
<div
5+
id="plotlyScatterTemperatureNdvi"
6+
style="width: 100%; height: 400px"
7+
class="d-flex justify-center"
8+
></div>
9+
</v-row>
10+
</v-container>
11+
</template>
12+
13+
<script>
14+
import axios from 'axios'
15+
import Plotly from 'plotly.js-dist-min'
16+
import { onMounted, ref } from 'vue'
17+
18+
export default {
19+
name: 'TemperatureNdviScatter',
20+
setup() {
21+
const temperatureData = ref(null)
22+
const ndviData = ref(null)
23+
const startDate = ref(1514761200) // 2018-01-01
24+
const endDate = ref(1733007599) // 2024-11-30
25+
const temporalResolution = ref("Monthly") // options: "Daily", "Monthly"
26+
const aggregation = ref("Mean") // options: "Mean", "Median", "Max", "Min"
27+
28+
const fetchTemperatureData = async () => {
29+
const apiUrl = 'https://thf-climate-run-1020174331409.europe-west3.run.app/weather/index'
30+
const params = {
31+
weatherVariable: "temperature_2m",
32+
startDate: startDate.value,
33+
endDate: endDate.value,
34+
location: "TEMPELHOFER_FELD",
35+
temporalResolution: temporalResolution.value.toUpperCase(),
36+
aggregation: aggregation.value.toUpperCase()
37+
}
38+
39+
try {
40+
const response = await axios.get(apiUrl, { params })
41+
temperatureData.value = response.data
42+
renderPlot()
43+
} catch (error) {
44+
console.error("Error fetching temperature data:", error)
45+
}
46+
}
47+
48+
const fetchNdviData = async () => {
49+
const apiUrl = 'https://thf-climate-run-1020174331409.europe-west3.run.app/index/ndvi'
50+
const params = {
51+
startDate: startDate.value,
52+
endDate: endDate.value,
53+
location: "TEMPELHOFER_FELD",
54+
temporalResolution: temporalResolution.value.toUpperCase(),
55+
aggregation: aggregation.value.toUpperCase()
56+
}
57+
58+
try {
59+
const response = await axios.get(apiUrl, { params })
60+
ndviData.value = response.data
61+
renderPlot()
62+
} catch (error) {
63+
console.error("Error fetching NDVI data:", error)
64+
}
65+
}
66+
67+
const renderPlot = () => {
68+
if (temperatureData.value && ndviData.value) {
69+
const tempValues = temperatureData.value.data.map(entry => entry.value)
70+
const ndviValues = ndviData.value.data.map(entry => entry.value)
71+
72+
const timestamps = temperatureData.value.data.map(entry => entry.timestamp);
73+
const monthsAndYears = timestamps.map(ts => {
74+
const date = new Date(ts * 1000)
75+
const month = date.toLocaleString('default', { month: 'long' })
76+
const year = date.getFullYear()
77+
return { month, year }
78+
})
79+
80+
// Map months to a circular grayscale gradient
81+
const months = monthsAndYears.map(({ month }) => new Date(Date.parse(month + " 1")).getMonth())
82+
const monthColors = months.map(month => {
83+
// Convert month index (0-11) to circular position
84+
const angle = (month / 12) * 2 * Math.PI // 0-2π range
85+
// Use cosine to create smooth gradient: July (π) = 0 (black), January (0) = 1 (white)
86+
const intensity = Math.round((Math.cos(angle) + 1) / 2 * 255) // Scale cosine to 0-255
87+
return `rgb(${intensity}, ${intensity}, ${intensity})`
88+
})
89+
90+
const scatterTrace = {
91+
x: tempValues,
92+
y: ndviValues,
93+
mode: 'markers',
94+
marker: {
95+
color: monthColors,
96+
size: 8,
97+
line: {
98+
color: 'black',
99+
width: 1,
100+
},
101+
},
102+
type: 'scatter',
103+
name: 'Temperature vs. NDVI',
104+
hovertemplate: '%{x}°C<br>NDVI: %{y}<br>%{customdata.month} %{customdata.year}<extra></extra>',
105+
customdata: monthsAndYears,
106+
};
107+
108+
const layout = {
109+
title: 'Scatter Plot of Temperature vs. NDVI',
110+
xaxis: { title: 'Temperature (°C)' },
111+
yaxis: { title: 'NDVI' },
112+
template: 'plotly_white',
113+
};
114+
115+
Plotly.newPlot('plotlyScatterTemperatureNdvi', [scatterTrace], layout)
116+
}
117+
}
118+
119+
onMounted(() => {
120+
fetchTemperatureData()
121+
fetchNdviData()
122+
})
123+
}
124+
}
125+
</script>
126+
127+
<style scoped></style>
128+

frontend/src/components/MainSection.vue

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
<template>
22
<v-container class="main-section" fluid>
3-
<Images />
3+
<TemperatureNdviBarAndLine />
4+
<TemperatureNdviScatter />
5+
<TempDifferenceGraph />
6+
<SoilTempDifferenceGraph />
7+
<NdviDifferenceGraph />
8+
<NdviOverlayGraph />
9+
<!-- <Images />
410
<NdviComparisonGraph />
511
<NdviSelectMonthGraph />
6-
<NdviOverlayGraph />
712
<MedianTempGraph />
813
<MeanSoilTempGraph />
914
<MeanSoilMoistureGraph />
1015
<AugustMeanSoilTempGraph />
11-
<SelectMonthMeanSoilTempGraph />
16+
<SelectMonthMeanSoilTempGraph /> -->
1217
</v-container>
1318
</template>
1419

@@ -22,6 +27,11 @@ import MeanSoilTempGraph from "./WeatherGraphs/SoilTempGraph.vue"
2227
import MeanSoilMoistureGraph from "./WeatherGraphs/SoilMoistureGraph.vue"
2328
import AugustMeanSoilTempGraph from "./WeatherGraphs/AugustSoilTempGraph.vue"
2429
import SelectMonthMeanSoilTempGraph from "./WeatherGraphs/SelectMonthSoilTempGraph.vue"
30+
import TemperatureNdviBarAndLine from "./CorrelationGraphs/TemperatureNdviBarAndLine.vue"
31+
import TemperatureNdviScatter from "./CorrelationGraphs/TemperatureNdviScatter.vue"
32+
import TempDifferenceGraph from "./WeatherGraphs/TempDifferenceGraph.vue"
33+
import SoilTempDifferenceGraph from "./WeatherGraphs/SoilTempDifferenceGraph.vue"
34+
import NdviDifferenceGraph from "./NdviGraphs/NdviDifferenceGraph.vue"
2535
2636
export default {
2737
name: 'MainSection',
@@ -34,7 +44,12 @@ export default {
3444
MeanSoilTempGraph,
3545
MeanSoilMoistureGraph,
3646
AugustMeanSoilTempGraph,
37-
SelectMonthMeanSoilTempGraph
47+
SelectMonthMeanSoilTempGraph,
48+
TempDifferenceGraph,
49+
SoilTempDifferenceGraph,
50+
TemperatureNdviBarAndLine,
51+
TemperatureNdviScatter,
52+
NdviDifferenceGraph
3853
}
3954
}
4055
</script>

frontend/src/components/NdviGraphs/NdviComparisonGraph.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default {
5454
const aggregationOptions = ["Mean", "Median", "Max", "Min"]
5555
5656
const fetchNdviData = async (params) => {
57-
const apiUrl = 'http://localhost:8000/index/ndvi'
57+
const apiUrl = 'https://thf-climate-run-1020174331409.europe-west3.run.app/index/ndvi'
5858
try {
5959
const response = await axios.get(apiUrl, { params })
6060
ndviData.value = response.data

0 commit comments

Comments
 (0)