stash some shit
Signed-off-by: Frank Villaro-Dixon <frank@villaro-dixon.eu>
This commit is contained in:
parent
3a3d997d49
commit
311ce09e15
23 changed files with 3454 additions and 0 deletions
3
ui/.gitignore
vendored
Normal file
3
ui/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
db.sqlite
|
||||||
|
node_modules/
|
||||||
|
public/rivers.json
|
8
ui/Dockerfile
Normal file
8
ui/Dockerfile
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
FROM node:20
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
5
ui/README.md
Normal file
5
ui/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# vue3-openlayers Starter Template
|
||||||
|
|
||||||
|
See [vue3-openlayers](https://github.com/MelihAltintas/vue3-openlayers)
|
||||||
|
|
||||||
|
You can use this starter template as a playground or for providing reproduction steps, when opening new [issues](https://github.com/MelihAltintas/vue3-openlayers/issues).
|
24
ui/_gitignore
Normal file
24
ui/_gitignore
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
37
ui/generate-db.py
Normal file
37
ui/generate-db.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import json
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
def db_to_js(db_name: str):
|
||||||
|
conn = sqlite3.connect(db_name)
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute("SELECT wayid, name, length, start_ele, end_ele, slope_correct, slope_confidence, centroid_lat, centroid_lon FROM rivers")
|
||||||
|
|
||||||
|
rows = c.fetchall()
|
||||||
|
ret = []
|
||||||
|
for river in rows:
|
||||||
|
sc = river[5]
|
||||||
|
if sc == 1:
|
||||||
|
#print(f"OK: {river}")
|
||||||
|
continue
|
||||||
|
r = {
|
||||||
|
'wayid': river[0],
|
||||||
|
'name': river[1],
|
||||||
|
'length': river[2],
|
||||||
|
'start_ele': river[3],
|
||||||
|
'end_ele': river[4],
|
||||||
|
'slope_correct': river[5],
|
||||||
|
'slope_confidence': river[6],
|
||||||
|
'centroid_lat': river[7],
|
||||||
|
'centroid_lon': river[8]
|
||||||
|
}
|
||||||
|
if r['slope_confidence'] > 0.4:
|
||||||
|
print(f"KO: {r}")
|
||||||
|
if r['start_ele'] + 20 < r['end_ele']:
|
||||||
|
print(f"KO: {r}")
|
||||||
|
ret.append(r)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
rivers = db_to_js('db.sqlite')
|
||||||
|
with open('public/rivers.json', 'w') as f:
|
||||||
|
f.write(json.dumps(rivers, indent=1))
|
13
ui/index.html
Normal file
13
ui/index.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>OSM QA: uphill rivers</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
2650
ui/package-lock.json
generated
Normal file
2650
ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
26
ui/package.json
Normal file
26
ui/package.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "vue3-openlayers-starter",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --mode development",
|
||||||
|
"build": "vue-tsc && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.5.1",
|
||||||
|
"bootstrap": "^5.3.2",
|
||||||
|
"chart.js": "^4.4.0",
|
||||||
|
"vue": "^3.2.47",
|
||||||
|
"vue-chartjs": "^5.2.0",
|
||||||
|
"vue-router": "^4.2.5",
|
||||||
|
"vue3-openlayers": "*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^4.1.0",
|
||||||
|
"typescript": "^4.9.3",
|
||||||
|
"vite": "^4.2.1",
|
||||||
|
"vue-tsc": "^1.2.0"
|
||||||
|
}
|
||||||
|
}
|
1
ui/public/.gitignore
vendored
Normal file
1
ui/public/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rivers.json
|
0
ui/public/.gitkeep
Normal file
0
ui/public/.gitkeep
Normal file
23
ui/src/App.vue
Normal file
23
ui/src/App.vue
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<template>
|
||||||
|
<router-link :to="{ path: `/` }">
|
||||||
|
<h2 class="text-center">Openstreetmap QA: uphill rivers</h2>
|
||||||
|
</router-link>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "App",
|
||||||
|
components: {},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
</style>
|
7
ui/src/App2.vue
Normal file
7
ui/src/App2.vue
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import TheMap from './components/TheMap.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TheMap />
|
||||||
|
</template>
|
0
ui/src/assets/.gitkeep
Normal file
0
ui/src/assets/.gitkeep
Normal file
246
ui/src/components/Home copy.vue
Normal file
246
ui/src/components/Home copy.vue
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
<template>
|
||||||
|
<notifications
|
||||||
|
class="mt-3 ms-3"
|
||||||
|
:duration="2000"
|
||||||
|
:width="250"
|
||||||
|
animation-name="v-fade-left"
|
||||||
|
position="top left"
|
||||||
|
/>
|
||||||
|
<div class="container my-5">
|
||||||
|
<h3 class="text-center">Liste des clusters</h3>
|
||||||
|
<!--
|
||||||
|
<div class="row d-flex justify-content-center">
|
||||||
|
<div class="col-xs-12 col-lg-6 my-3">
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="todo" class="form-label">Add ToDo</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-10">
|
||||||
|
<input
|
||||||
|
v-model="todo"
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
name="todo"
|
||||||
|
id="todo"
|
||||||
|
placeholder="Enter New Todo"
|
||||||
|
v-bind:class="{ 'is-invalid': input_errors.length > 0, 'is-valid': input_errors.length == 0 && todo != '' }"
|
||||||
|
/>
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
<span :key="key" v-for="(error,key) in input_errors">{{ error }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 d-grid gap-2">
|
||||||
|
<button class="btn btn-primary btn-s float-end" @click="save">Add</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="row d-flex justify-content-center mt-3" v-if="Clusters.length">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h5 class="mb-3">Liste des paravalanches:</h5>
|
||||||
|
Trier par:
|
||||||
|
<select v-model="sortby">
|
||||||
|
<option value="altitude">Altitude</option>
|
||||||
|
<option value="length">Long. de paravalanche</option>
|
||||||
|
<option value="energy_yr">Energie annuelle</option>
|
||||||
|
<option value="efficiency">Rendement</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div style="list-style-type: none;">
|
||||||
|
<li
|
||||||
|
v-for="cluster in sortedClusters"
|
||||||
|
:key="cluster.id"
|
||||||
|
class="row"
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border: 2px solid hsla(0, 0%, 0%, 0.35);
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="col-8">
|
||||||
|
<span
|
||||||
|
v-bind:style="cluster.done ? 'text-decoration:line-through;' : ''"
|
||||||
|
>📏 {{ parseInt(cluster.length) }} m - ⛰️ {{ cluster.alt }} m<br>
|
||||||
|
{{ parseInt(cluster.yield) }} kWh/kWp - {{ parseInt(cluster.energy_total/1000) }} MWh/yr
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 d-flex justify-content-center">
|
||||||
|
<span
|
||||||
|
class="form-check form-switch"
|
||||||
|
@click="done_todo(cluster)"
|
||||||
|
style="margin-left:20px;"
|
||||||
|
> Installer
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
:id="cluster.id"
|
||||||
|
:value="cluster.id"
|
||||||
|
:key="cluster.id"
|
||||||
|
v-model="choosen_installs"
|
||||||
|
data-onstyle="#1f2023"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 d-flex justify-content-center">
|
||||||
|
<!--
|
||||||
|
<button class="btn btn-primary btn-sm" @click="delete_todo(cluster.id)">+ details</button>
|
||||||
|
-->
|
||||||
|
<router-link :to="{ path: `/clusters/${cluster.id}`}">
|
||||||
|
<button class="btn btn-primary btn-sm">+ details</button>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h4>Statistiques</h4>
|
||||||
|
(cocher sur "installer" pour ajouter aux statistiques)
|
||||||
|
<ul>
|
||||||
|
<li>Longueur totale: {{ Math.round(stats['length']/1000 * 10)/10 }} km</li>
|
||||||
|
<li>Energie totale: {{ Math.round(stats['energy']/1000) }} MWh/yr</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
sortby: 'length',
|
||||||
|
stats_length: 0,
|
||||||
|
choosen_installs: [],
|
||||||
|
todo: "",
|
||||||
|
Clusters: [],
|
||||||
|
errors: [],
|
||||||
|
newTodo: {
|
||||||
|
todo: "",
|
||||||
|
done: false
|
||||||
|
},
|
||||||
|
responseData: null,
|
||||||
|
input_errors: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sortedClusters() {
|
||||||
|
var clusters = this.Clusters.slice();
|
||||||
|
|
||||||
|
if(this.sortby == "length"){
|
||||||
|
return clusters.sort(function(a,b){
|
||||||
|
return b.length - a.length;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if(this.sortby == "altitude") {
|
||||||
|
return clusters.sort(function(a,b){
|
||||||
|
return b.alt - a.alt;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if(this.sortby == "energy_yr") {
|
||||||
|
return clusters.sort(function(a,b){
|
||||||
|
return b.energy_total - a.energy_total;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if(this.sortby == "efficiency") {
|
||||||
|
return clusters.sort(function(a,b){
|
||||||
|
return b.yield - a.yield;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return clusters;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stats() {
|
||||||
|
let installed = this.Clusters.filter((item) => this.choosen_installs.includes(item.id))
|
||||||
|
|
||||||
|
let energy_sum = 0;
|
||||||
|
let length_sum = 0;
|
||||||
|
installed.forEach(function (value) {
|
||||||
|
energy_sum += value.energy_total;
|
||||||
|
length_sum += value.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
energy: energy_sum,
|
||||||
|
length: length_sum,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getClusters();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getClusters() {
|
||||||
|
try {
|
||||||
|
const response = await this.$axios.get("/paralist.js");
|
||||||
|
console.log('YEAH !')
|
||||||
|
this.Clusters = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
this.errors.push(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
async save() {
|
||||||
|
if (this.input_errors.length > 0 || this.todo == '') {
|
||||||
|
if (this.todo == '' && this.input_errors.length == 0)
|
||||||
|
this.input_errors.push('ToDo field cannot be left blank')
|
||||||
|
this.input_errors.forEach((value) => {
|
||||||
|
this.$notify(value);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
this.newTodo.todo = this.todo;
|
||||||
|
const response = await this.$axios.post("/todos", this.newTodo);
|
||||||
|
this.responseData = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
this.errors.push(error);
|
||||||
|
}
|
||||||
|
this.getTodos();
|
||||||
|
this.$notify("Added Succesfully");
|
||||||
|
this.todo = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async delete_todo(index) {
|
||||||
|
try {
|
||||||
|
const response = await this.$axios.delete("/todos/" + index);
|
||||||
|
this.responseData = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
this.errors.push(error);
|
||||||
|
}
|
||||||
|
this.getTodos();
|
||||||
|
this.$notify("Deleted Succesfully");
|
||||||
|
},
|
||||||
|
async done_todo(todo) {
|
||||||
|
try {
|
||||||
|
const response = await this.$axios.put("/todos/" + todo.id, { "todo": todo.todo, "done": !todo.done });
|
||||||
|
this.responseData = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
this.errors.push(error);
|
||||||
|
}
|
||||||
|
this.getTodos();
|
||||||
|
this.$notify("Updated Succesfully");
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
todo(val) {
|
||||||
|
this.input_errors = [];
|
||||||
|
if (val == '') {
|
||||||
|
this.input_errors.push('ToDo field cannot be left blank')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (val.length < 3 || val.length > 40) {
|
||||||
|
this.input_errors.push('ToDo field be Minimum 6, Maximum 25 characters')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
153
ui/src/components/Home.vue
Normal file
153
ui/src/components/Home.vue
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
<template>
|
||||||
|
<div class="container my-5">
|
||||||
|
<span v-if="!Rivers.length">
|
||||||
|
Loading... If nothing happens in a while, please reload the page
|
||||||
|
</span>
|
||||||
|
<div class="row d-flex justify-content-center mt-3" v-if="Rivers.length">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h5 class="mb-3">Uphill rivers:</h5>
|
||||||
|
Sort by:
|
||||||
|
<select v-model="sortby">
|
||||||
|
<option value="wayid">Way id</option>
|
||||||
|
<option value="alt-diff">altitude difference</option>
|
||||||
|
<option value="confidence">confidence</option>
|
||||||
|
<option value="length">length</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div style="list-style-type: none;">
|
||||||
|
<li v-for="river in sortedRivers" :key="river.id" class="row" style="
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border: 2px solid hsla(0, 0%, 0%, 0.35);
|
||||||
|
">
|
||||||
|
<div class="col-7">
|
||||||
|
<span v-if="river.name">
|
||||||
|
{{ river.name }}
|
||||||
|
</span>
|
||||||
|
<b>{{ parseInt(river.length) }} m</b>
|
||||||
|
( {{ river.slope_confidence }})<br>
|
||||||
|
Start ele.: {{ river.start_ele | round }} m - End ele.: {{ river.end_ele | round }}m<br>
|
||||||
|
{{ river.wayid }}
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
|
<div class="col-2 d-flex justify-content-center">
|
||||||
|
<span class="form-check form-switch" style="margin-left:20px;"> Choisir
|
||||||
|
<input class="form-check-input" type="checkbox" :id="cluster.id" :value="cluster.id"
|
||||||
|
:key="cluster.id" v-model="choosen_installs" data-onstyle="#1f2023" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<br>
|
||||||
|
<hr>
|
||||||
|
<div class="col-2 d-flex justify-content-center">
|
||||||
|
<router-link :to="{ path: `/river/${river.wayid}` }">
|
||||||
|
<button class="btn btn-primary btn-sm">+ details</button>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 d-flex justify-content-center">
|
||||||
|
<button class="btn btn-primary btn-sm" @click="openOnJOSM(river.wayid)">+ JOSM</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 d-flex justify-content-center">
|
||||||
|
<a :href="`https://https://www.openstreetmap.org/way/${ river.wayid }`">
|
||||||
|
<button class="btn btn-primary btn-sm">OSM</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h4>Somme des installations choisies</h4>
|
||||||
|
(cocher sur "installer" pour ajouter aux statistiques)
|
||||||
|
<ul>
|
||||||
|
<li>Longueur totale: {{ Math.round(stats['length'] / 1000 * 10) / 10 }} km</li>
|
||||||
|
<li>Energie totale: {{ Math.round(stats['energy'] / 1000) }} MWh/yr</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
Contact: f@vi-di.fr
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
sortby: 'length',
|
||||||
|
stats_length: 0,
|
||||||
|
sortedRivers: [],
|
||||||
|
Rivers: [],
|
||||||
|
errors: [],
|
||||||
|
responseData: null,
|
||||||
|
input_errors: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sortedRivers() {
|
||||||
|
var rivers = this.Rivers.slice();
|
||||||
|
|
||||||
|
if (this.sortby == "length") {
|
||||||
|
return rivers.sort(function (a, b) {
|
||||||
|
return b.length - a.length;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (this.sortby == "confidence") {
|
||||||
|
return rivers.sort(function (a, b) {
|
||||||
|
return b.slope_confidence - a.slope_confidence;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (this.sortby == "alt-diff") {
|
||||||
|
return rivers.sort(function (a, b) {
|
||||||
|
return Math.abs(b.start_ele - b.end_ele) > Math.abs(a.start_ele - a.end_ele);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Unknown sortby value: " + this.sortby)
|
||||||
|
return rivers;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getRivers();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getRivers() {
|
||||||
|
try {
|
||||||
|
const response = await this.$axios.get("/rivers.json");
|
||||||
|
console.log('YEAH !')
|
||||||
|
this.Rivers = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
this.errors.push(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async openOnJOSM(wayid) {
|
||||||
|
console.log('Will open on josm!')
|
||||||
|
try {
|
||||||
|
await this.$axios.get("http://localhost:8111/load_object?new_layer=true&objects=w"+wayid);
|
||||||
|
console.log('YEAH !')
|
||||||
|
} catch (error) {
|
||||||
|
this.errors.push(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
todo(val) {
|
||||||
|
this.input_errors = [];
|
||||||
|
if (val == '') {
|
||||||
|
this.input_errors.push('ToDo field cannot be left blank')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (val.length < 3 || val.length > 40) {
|
||||||
|
this.input_errors.push('ToDo field be Minimum 6, Maximum 25 characters')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
124
ui/src/components/River.vue
Normal file
124
ui/src/components/River.vue
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
<template>
|
||||||
|
<div class="container my-5">
|
||||||
|
<h2 class="text-center">River details</h2>
|
||||||
|
<div class="row d-flex justify-content-center mt-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h3>Characteristics
|
||||||
|
</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Est. length: {{ Math.round(river.length) }}m</li>
|
||||||
|
<li>Altitude moyenne: {{ cluster.alt }} m</li>
|
||||||
|
<li>Commune: {{cluster.city}}
|
||||||
|
<span v-if="cluster.city == 'Bellwald'">☀️</span>
|
||||||
|
</li>
|
||||||
|
<li>Puissance installable: {{ Math.round(cluster.num_panels * .4) }} kWp</li>
|
||||||
|
<li>Azimuth: {{Math.round(cluster.azimuth)}}°</li>
|
||||||
|
<li>Energie annuelle: {{ Math.round(cluster.energy_total) }} MWh</li>
|
||||||
|
<li>Efficience: {{ Math.round(cluster.yield) }} kWh/kWp</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<Bar id="my-chart-id" :options="chartOptions" :data="chartData" />
|
||||||
|
(ceci est une estimation, sans prise en compte de ombrage)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<ol-map :loadTilesWhileAnimating="true" :loadTilesWhileInteracting="true" style="height: 400px">
|
||||||
|
<ol-view ref="view" :center="center" zoom="13" :projection="projection" />
|
||||||
|
|
||||||
|
<ol-tile-layer>
|
||||||
|
<ol-source-osm />
|
||||||
|
</ol-tile-layer>
|
||||||
|
<ol-vector-layer>
|
||||||
|
<ol-source-vector>
|
||||||
|
<ol-feature v-for="c in cluster_geom['features']">
|
||||||
|
<ol-geom-line-string :coordinates="c['geometry']['coordinates']"></ol-geom-line-string>
|
||||||
|
<ol-style>
|
||||||
|
<ol-style-stroke color="blue" width="2"></ol-style-stroke>
|
||||||
|
</ol-style>
|
||||||
|
</ol-feature>
|
||||||
|
</ol-source-vector>
|
||||||
|
</ol-vector-layer>
|
||||||
|
|
||||||
|
</ol-map>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, inject } from 'vue';
|
||||||
|
import { Bar } from 'vue-chartjs'
|
||||||
|
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js'
|
||||||
|
|
||||||
|
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BarChart',
|
||||||
|
components: { Bar },
|
||||||
|
data() {
|
||||||
|
const projection = ref('EPSG:3857');
|
||||||
|
|
||||||
|
const format = inject('ol-format');
|
||||||
|
const geoJson = new format.GeoJSON();
|
||||||
|
|
||||||
|
return {
|
||||||
|
projection,
|
||||||
|
geoJson,
|
||||||
|
cluster: {},
|
||||||
|
cluster_geom: [],
|
||||||
|
data_loaded: false,
|
||||||
|
chartOptions: {
|
||||||
|
responsive: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
center() {
|
||||||
|
if (this.data_loaded) {
|
||||||
|
console.log(this.cluster.centroid);
|
||||||
|
return this.cluster.centroid
|
||||||
|
}
|
||||||
|
return ref([40, 40]);
|
||||||
|
},
|
||||||
|
chartData() {
|
||||||
|
if(this.data_loaded) {
|
||||||
|
let dats = this.cluster['pv_norm']['outputs']['monthly']['fixed'];
|
||||||
|
let graphVals = dats.map((d) => d.E_m)
|
||||||
|
return {
|
||||||
|
labels: ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jun', 'Jul', 'Aou', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||||
|
datasets: [{
|
||||||
|
data: graphVals,
|
||||||
|
label: 'Energie annuelle/kWp',
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
labels: [],
|
||||||
|
datasets: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getCluster();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getCluster() {
|
||||||
|
try {
|
||||||
|
let id = this.$route.params.clusterid;
|
||||||
|
|
||||||
|
const rgeom = await this.$axios.get(`/cluster-geom-${id}.json`);
|
||||||
|
this.cluster_geom = rgeom.data;
|
||||||
|
|
||||||
|
const r = await this.$axios.get(`/cluster-meta-${id}.json`);
|
||||||
|
this.cluster = r.data;
|
||||||
|
this.data_loaded = true;
|
||||||
|
console.log(rgeom.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
this.errors.push(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
43
ui/src/components/TheMap.vue
Normal file
43
ui/src/components/TheMap.vue
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<ol-map
|
||||||
|
:loadTilesWhileAnimating="true"
|
||||||
|
:loadTilesWhileInteracting="true"
|
||||||
|
style="height: 400px"
|
||||||
|
>
|
||||||
|
<ol-view
|
||||||
|
ref="view"
|
||||||
|
:center="center"
|
||||||
|
:rotation="rotation"
|
||||||
|
:zoom="zoom"
|
||||||
|
:projection="projection"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ol-tile-layer>
|
||||||
|
<ol-source-osm />
|
||||||
|
</ol-tile-layer>
|
||||||
|
</ol-map>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, inject } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const center = ref([40, 40]);
|
||||||
|
const projection = ref('EPSG:4326');
|
||||||
|
const zoom = ref(3);
|
||||||
|
const rotation = ref(0);
|
||||||
|
|
||||||
|
const format = inject('ol-format');
|
||||||
|
const geoJson = new format.GeoJSON();
|
||||||
|
|
||||||
|
return {
|
||||||
|
center,
|
||||||
|
projection,
|
||||||
|
zoom,
|
||||||
|
rotation,
|
||||||
|
geoJson,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
32
ui/src/main.ts
Normal file
32
ui/src/main.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
|
||||||
|
import VueRouter from './router';
|
||||||
|
import App from './App.vue';
|
||||||
|
|
||||||
|
import OpenLayersMap from 'vue3-openlayers';
|
||||||
|
import 'vue3-openlayers/dist/vue3-openlayers.css';
|
||||||
|
|
||||||
|
import { Bar } from 'vue-chartjs';
|
||||||
|
|
||||||
|
import axios from "axios";
|
||||||
|
import "bootstrap/dist/css/bootstrap.min.css"
|
||||||
|
import "bootstrap"
|
||||||
|
|
||||||
|
let baseUrl;
|
||||||
|
if (window.location.hostname === "localhost") {
|
||||||
|
baseUrl = "./public/"
|
||||||
|
}
|
||||||
|
|
||||||
|
const axiosInstance = axios.create({
|
||||||
|
baseURL: baseUrl,
|
||||||
|
//baseURL: "https://parapower.k3s.fr/data/"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.config.globalProperties.$axios = axiosInstance;
|
||||||
|
app.use(OpenLayersMap)
|
||||||
|
app.use(VueRouter)
|
||||||
|
|
||||||
|
app.mount('#app');
|
21
ui/src/router/index.ts
Normal file
21
ui/src/router/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
|
import Home from "../components/Home.vue";
|
||||||
|
import River from "../components/River.vue";
|
||||||
|
|
||||||
|
const routerHistory = createWebHistory();
|
||||||
|
const router = createRouter({
|
||||||
|
history: routerHistory,
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
name: "Home",
|
||||||
|
component: Home,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/river/:riverid",
|
||||||
|
name: "River",
|
||||||
|
component: River,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
export default router;
|
1
ui/src/vite-env.d.ts
vendored
Normal file
1
ui/src/vite-env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="vite/client" />
|
19
ui/tsconfig.json
Normal file
19
ui/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"allowJs": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
9
ui/tsconfig.node.json
Normal file
9
ui/tsconfig.node.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
9
ui/vite.config.ts
Normal file
9
ui/vite.config.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
resolve: {
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in a new issue