Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"leaflet": "^1.9.4",
"leaflet-easybutton": "^2.4.0",
"leaflet-iconmaterial": "^1.1.0",
"papaparse": "^5.4.1",
"leaflet.markercluster": "^1.5.3",
"pinia": "^2.1.6",
"swagger-ui": "^5.10.5",
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/components/DataViewDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,18 @@
<p>{{ totalModels }}</p>
</v-card-text>
</v-card>
<v-card>
<v-card-text>
<DownloadMapData />
</v-card-text>
</v-card>
</v-sheet>
</template>

<script setup>
import { ref } from 'vue'
import { ENDPOINTS } from '../constants';
import DownloadMapData from '@/components/DownloadMapData.vue';

let querying = ref(true)

Expand Down
116 changes: 116 additions & 0 deletions frontend/src/components/DownloadMapData.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<template>
<v-btn block @click="downloadMapData">Download Map Data</v-btn>
</template>

<script setup>
import { useMapStore } from '@/stores/map';
import Papa from 'papaparse';

const mapStore = useMapStore();
const renameColumnInCSV = {
"figure num": "Figure Number",
"figure caption": "Figure Caption",
"figure url": "Figure Url",
"textmodel section number": "Textmodel Section Number",
"textmodel section name": "Textmodel Section Name",
"textmodel page number": "Textmodel Page Number",
"textmodel snipped": "Textmodel Snippet",
"citation citation": "Citation",
"citation url": "Citation Url",
"citation attribution": "Citation Attribution",
"citation attribution url": "Citation Attribution Url",
"location long name": "Location Name",
"location country": "Location Country",
"location area km2": "Location Area [km^2]",
"process taxonomies process": "Taxonomy Processes",
"vegetation info": "Vegetation Info",
"soil info": "Soil Info",
"geol info": "Geological Info",
"topo info": "Topographic Info",
"uncertainty info": "Uncertainty Info",
"other info": "Other Info",
"num spatial zones": "Number of Spatial Zones",
"spatial zone type spatial property": "Spatial Zone Type",
"num temporal zones": "Number of Temporal Zones",
"temporal zone type temporal property": "Temporal Zone Type",
"model type name": "Model Type"
}
const csvColumns = Object.values(renameColumnInCSV);

function flattenItem(obj, parentKey = '', result = {}) {
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
let newKey = parentKey ? `${parentKey} ${key}` : key;

// Convert column names as per requirement
newKey = renamecsvColumn(newKey);

if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
// Recursive call for nested objects
flattenItem(obj[key], newKey, result);
} else if (Array.isArray(obj[key])) {
if (obj[key].every(item => typeof item === 'string' || typeof item === 'number')) {
result[newKey] = obj[key].join(', ');
} else {
const arrayValues = {};
obj[key].forEach(item => {
for (const subKey in item) {
if (Object.prototype.hasOwnProperty.call(item, subKey)) {
if (!arrayValues[subKey]) {
arrayValues[subKey] = [];
}
arrayValues[subKey].push(item[subKey]);
}
}
});

for (const arrayKey in arrayValues) {
const renamedValue = renamecsvColumn(`${newKey} ${arrayKey}`);
if (csvColumns.includes(renamedValue))
result[renamedValue] = arrayValues[arrayKey].join(', ');
}
}
} else {
if (csvColumns.includes(newKey))
result[newKey] = obj[key];
}
}
}
return result;
}

function flattenMapDataJSON(data) {
const result = [];
data.forEach(item => {
// As per requirement only include properties, no need of geometry info
result.push(flattenItem(item.properties));
});
return result;
}

function downloadMapData() {
const mapData = mapStore.currentFilteredData;
const flattenedMapData = flattenMapDataJSON(mapData);

const csv = Papa.unparse(flattenedMapData, {
columns: csvColumns
});

const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });

const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.setAttribute('download', 'data.csv');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function renamecsvColumn(name) {
let newName = name.replaceAll("_", " ");
newName = newName.replace(/\b\w/g, char => char.toLowerCase());
if (renameColumnInCSV[newName]) {
newName = renameColumnInCSV[newName];
}
return newName;
}
</script>
8 changes: 6 additions & 2 deletions frontend/src/stores/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const useMapStore = defineStore('map', () => {
const modelFeatures = ref({})
const perceptualModelsGeojson = ref([])
const mapLoaded = ref(false)
let currentFilteredData = ref([])
const markerClusterGroup = L.markerClusterGroup({
iconCreateFunction: (cluster) => {
const childCount = cluster.getChildCount();
Expand Down Expand Up @@ -103,12 +104,12 @@ export const useMapStore = defineStore('map', () => {
content += '<h4>Temporal zone:</h4>'
content += `${feature.properties.temporal_zone_type.temporal_property}`
}

layer.bindPopup(content, {
maxWidth: 400,
maxHeight: 300,
keepInView: true
})
currentFilteredData.value.push(feature);
}

const pointToLayer = (feature, latlng) => {
Expand Down Expand Up @@ -151,6 +152,7 @@ export const useMapStore = defineStore('map', () => {
}

function filterFeatures(filterFunction) {
currentFilteredData.value =[];
// TODO enable multiple filters at the same time
// first remove all layers
layerGroup.value.removeLayer(modelFeatures.value)
Expand All @@ -164,6 +166,7 @@ export const useMapStore = defineStore('map', () => {
},
pointToLayer: pointToLayer
})

// add filtered features
markerClusterGroup.addLayer(modelFeatures.value);
layerGroup.value.addLayer(markerClusterGroup);
Expand All @@ -187,6 +190,7 @@ export const useMapStore = defineStore('map', () => {
mapLoaded,
fetchPerceptualModelsGeojson,
filterFeatures,
resetFilter
resetFilter,
currentFilteredData
}
})
Loading