Skip to content

Commit

Permalink
Addition of station table and station form.
Browse files Browse the repository at this point in the history
  • Loading branch information
david-i-berry committed Sep 12, 2023
1 parent edca91b commit dbb3978
Show file tree
Hide file tree
Showing 20 changed files with 1,525 additions and 76 deletions.
468 changes: 452 additions & 16 deletions webapp/package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"apexcharts": "^3.41.1",
"core-js": "^3.29.0",
"d3": "^7.8.5",
"geojson-validation": "^1.0.2",
"leaflet": "^1.9.4",
"roboto-fontface": "*",
"vue": "^3.2.0",
"vue-router": "^4.0.0",
Expand Down
8 changes: 8 additions & 0 deletions webapp/public/code_lists/WMORegion.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{"name": "Africa", "id": "I", "description": "Region I - Africa"},
{"name": "Asia", "id": "II", "description": "Region II - Asia"},
{"name": "South America", "id": "III", "description": "Region III - South America"},
{"name": "North America, Central America and the Caribbean", "id": "IV", "description": "Region IV - North America, Central America and the Caribbean"},
{"name": "South-West Pacific", "id": "V", "description": "South-West Pacific"},
{"name": "Europe", "id": "VI", "description": "Europe"}
]
16 changes: 16 additions & 0 deletions webapp/public/code_lists/facilityType.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[
{"name": "Air (fixed)", "id": "Air (fixed)", "description": "Airborne station/platform, at fixed position"},
{"name": "Air (mobile)", "id": "Air (mobile)", "description": "Airborne station/platform, moving around"},
{"name": "Lake/River (fixed)", "id": "Lake/River (fixed)", "description": "Station/platform at lake/river surface, at fixed position"},
{"name": "Lake/River (mobile)", "id": "Lake/River (mobile)", "description": "Station/platform at lake/river surface, moving around"},
{"name": "Land (fixed)", "id": "Land (fixed)", "description": "Station/platform on solid terrain, at fixed position"},
{"name": "Land (mobile)", "id": "Land (mobile)", "description": "Station/platform on solid terrain, moving around"},
{"name": "Land (on ice)", "id": "Land (on ice)", "description": "Station/platform on ice-covered ground, moving with the ice"},
{"name": "Sea (fixed)", "id": "Sea (fixed)", "description": "Station/platform at sea surface, at fixed position"},
{"name": "Sea (mobile)", "id": "Sea (mobile)", "description": "Station/platform at sea surface, moving around"},
{"name": "Sea (on ice)", "id": "Sea (on ice)", "description": "Station/platform on floating ice, moving with the ice"},
{"name": "Space-based", "id": "Space-based", "description": "Satellite platform in orbit"},
{"name": "Underwater (fixed)", "id": "Underwater (fixed)", "description": "Station/platform under water, at fixed horizontal position"},
{"name": "Underwater (mobile)", "id": "Underwater (mobile)", "description": "Station/platform under water, moving around also horizontally"},
{"name": "unknown", "id": "unknown", "description": "The station/platform type is unknown."}
]
7 changes: 7 additions & 0 deletions webapp/public/code_lists/operatingStatus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"name": "Operational",
"id": "operational",
"description": "The station fully complies with the reporting obligations of the observation programme/network concerned."
}
]
7 changes: 7 additions & 0 deletions webapp/public/code_lists/territory.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{"name": "Algeria", "id": "Algeria", "description": "Algeria"},
{"name": "Congo", "id": "Congo", "description": "Congo"},
{"name": "Italy", "id": "Italy", "description": "Italy"},
{"name": "Malawi", "id": "Malawi", "description": "Malawi"},
{"name": "Romania", "id": "Romania", "description": "Romania"}
]
59 changes: 11 additions & 48 deletions webapp/src/components/CsvToBUFRForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@
<v-card>
<v-card-title>Select topic hierarchy</v-card-title>
<v-card-item>
<v-select label="Channel" v-model="hierarchy" :items="hierarchyList" v-if="hierarchyList"
hint="Topic hierarchy for ingestion of data" persistent-hint></v-select>
<TopicHierarchySelector :value="topicSelected" @update:modelValue="newValue => topicSelected = newValue"/>
</v-card-item>
</v-card>
</v-stepper-window-item>
Expand Down Expand Up @@ -231,14 +230,16 @@
import { VStepper, VStepperHeader, VStepperItem, VStepperWindow, VStepperWindowItem, VStepperActions} from 'vuetify/lib/labs/VStepper/index.mjs';
import InspectBufrButton from '@/components/InspectBufrButton.vue';
import DownloadButton from '@/components/DownloadButton.vue';
import TopicHierarchySelector from '@/components/TopicHierarchySelector.vue';
import * as d3 from 'd3';
export default defineComponent({
name: 'CsvToBUFRForm',
components: {
VFileInput, VCardActions, VBtn, VCard, VCardText, VCardItem, VDataTable,
VChip, VTooltip, VListItem, VList, VListSubheader, VSheet, VContainer,
VCardTitle, VIcon, VStepper, VStepperHeader, VStepperItem, VStepperWindow, VStepperWindowItem,
VStepperActions, VDialog, VCardSubtitle, InspectBufrButton, DownloadButton
VStepperActions, VDialog, VCardSubtitle, InspectBufrButton, DownloadButton,
TopicHierarchySelector
},
setup() {
// reactive variables
Expand All @@ -251,8 +252,7 @@
const validationErrors = ref([]);
const status = ref(null);
const token = ref(null);
const hierarchyList = ref(null);
const hierarchy = ref(null);
const topicSelected = ref(null);
const rawCSV = ref(null);
const msg = ref(null);
const showDialog = ref(null);
Expand All @@ -279,9 +279,6 @@
return "WIS2 notifications published: " + messagesPublished;;
});
// life cycle hooks
onBeforeMount( () => {
fetchHierarchy();
});
onMounted( () => {
setTimeout(scrollToRef(200));
});
Expand Down Expand Up @@ -396,50 +393,14 @@
step.value = 1;
};
};
const fetchHierarchy = async() => {
const apiUrl = `${import.meta.env.VITE_API_URL}/collections/discovery-metadata/items?f=json`;
// check if TEST=True is set in .env file
// check if TEST_MODE is set in .env file or if VITE_API_URL is not set
if (import.meta.env.VITE_TEST_MODE === "true" || import.meta.env.VITE_API_URL == undefined) {
console.log("TEST_MODE is enabled");
hierarchyList.value = ["test1", "test2", "test3"];
}
else {
console.log("Fetching topic hierarchy from:", apiUrl);
try {
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
else {
const data = await response.json();
if (data.features) {
// Use Array.map to create a new array of the topic hierarchies
hierarchyList.value = data.features.map(feature => {
if (feature.properties && feature.properties['wmo:topicHierarchy']) {
return feature.properties['wmo:topicHierarchy']
}
});
}
else {
console.error("API response is not an object");
}
}
}
catch (error) {
alert("Error fetching topic hierarchy, please check the API end point");
console.error("Error fetching topic hierarchy:", error)
}
}
};
const submit = async() => {
CsvToBUFR()
};
const CsvToBUFR = async() => {
const payload = {
inputs: {
data: rawCSV.value,
channel: hierarchy.value,
channel: topicSelected.value.id,
notify: true,
template: "aws-template"
}
Expand Down Expand Up @@ -501,6 +462,7 @@
proceed = true;
}else{
showDialog.value = true;
console.log(topicSelected.value)
msg.value = "Please select a topic to publish on before proceeding";
}
break;
Expand All @@ -520,7 +482,8 @@
};
// Define watches
watch( hierarchy, (val) => {
watch( topicSelected, (val) => {
console.log(val);
if( val ){
status.value.topicHierarchy = true;
}else{
Expand Down Expand Up @@ -550,8 +513,8 @@
});
return {theData, headers, incomingFile, loadCSV, step, prev, next, getFileName, scrollToRef,
validationWarnings, validationErrors, status, token, hierarchyList,
hierarchy, submit, msg, showDialog, result, resultTitle, numberNotifications};
validationWarnings, validationErrors, status, token,
topicSelected, submit, msg, showDialog, result, resultTitle, numberNotifications};
},
})
</script>
72 changes: 72 additions & 0 deletions webapp/src/components/FacilityTypeSelector.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<template>
<div v-if="! errorMessage">
<v-autocomplete
v-if="(options !== null)"
:items="options"
item-title="name"
item-value="id"
label="Type of observing facility"
v-model="selected"
:readonly="readonly"
:hint="selected ? selected.description : 'Select facility type'"
persistent-hint
return-object
/>
</div>
<div v-else class="error">
<v-text-field class="text-error" read-only :hint="props.defaultHint" persistent-hint>{{ errorMessage }}</v-text-field>
</div>
</template>

<script>
import { defineComponent, ref, onBeforeMount, watch, onErrorCaptured, computed} from 'vue';
import {VAutocomplete, VTextField} from 'vuetify/lib/components/index.mjs';
export default defineComponent({
name: 'FacilityTypeSelector',
components: {
VAutocomplete, VTextField
},
props: {
modelValue: {},
readonly: false
},
emits: ["update:modelValue"],
setup(props, {emit}){
const apiUrl = "/code_lists/facilityType.json";
const options = ref(null);
const selected = ref(null);
const errorMessage = ref(null);
const fetchOptions = async () => {
try {
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
options.value = data; // Assuming the API response contains the options in the expected format
} catch (error) {
errorMessage.value = 'Error fetching facility type options.';
console.error('Error fetching facility type options:', error);
}
};
onBeforeMount( async () => {
await fetchOptions() ;
// update if we have an initial value
if( props.modelValue.length ){
selected.value = options.value.find(option => option.id === props.modelValue) ;
}
});
watch( selected, (newValue) => {
if( selected.value ){
emit("update:modelValue", newValue);
}
});
return {selected, options, errorMessage};
}
});
</script>
95 changes: 95 additions & 0 deletions webapp/src/components/LocatorMap.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<template>
<v-card style="width: 100%; height: 100%; min-height: 400px" :id="id" ></v-card>
</template>

<script>
import { defineComponent, ref, computed, onMounted, watch } from 'vue';
import { VCard, VCardTitle, VCardText } from 'vuetify/lib/components/index.mjs';
import "leaflet/dist/leaflet.css";
import L from 'leaflet'
// geojson validator
import * as gjv from 'geojson-validation';
export default defineComponent({
name: "LocatorMap",
props: {
center: {
type: Object,
default: () => ({ lat: -6.155595235231087, lng: 106.84202325990253})
},
zoom: {
type: Number,
default: 10
},
id: {
type: String,
default: "map"
},
longitude: {
type: Number,
default: 0
},
latitude: {
type: Number,
default: 0
}
},
data(){
return {
}
},
components: {
VCard,
VCardTitle,
VCardText,
},
setup( props, {emit} ) {
const mapContainer = ref(null);
const map = ref(null);
const markerLayer = ref(null);
mapContainer.value = props.id;
const geom = computed( () => ({
type: "Feature",
geometry: {
type: "Point",
coordinates: [props.longitude, props.latitude]
},
properties: {}
}));
onMounted( () =>{
map.value = L.map(props.id, {zoomAnimation:false, fadeAnimation:true, markerZoomAnimation:true}).setView( props.center, props.zoom );
map.value.attributionControl.setPrefix('<a href="https://leafletjs.com/">Leaflet</a>')
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '&copy; OpenStreetMap contributors'}).addTo(map.value);
// check whether we have a location to show
if( geom.value.geometry ){
updateMarker();
};
emit('mapLoaded', map.value);
});
const updateMarker = async () => {
if( markerLayer.value ){
markerLayer.value.remove();
}
// now check whether the new marker is valid and plot
if( ! (isNaN(geom.value.geometry.coordinates[0]) || isNaN(geom.value.geometry.coordinates[1])) ){
if( gjv.isFeature(geom.value) ){
markerLayer.value = L.geoJSON(geom.value).addTo(map.value);
map.value.fitBounds(markerLayer.value.getBounds());
map.value.setZoom(12);
}
}
};
watch( geom, () => {
updateMarker();
})
return {mapContainer};
}
})
</script>
Loading

0 comments on commit dbb3978

Please sign in to comment.