Skip to content

Commit

Permalink
Merge pull request #39 from CUAHSI/download-map-metadata
Browse files Browse the repository at this point in the history
Ability to download metadata related to searched/filtered results.
  • Loading branch information
devincowan authored Jan 23, 2025
2 parents d73bf9f + ffd68e6 commit 248bf02
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 2 deletions.
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
}
})

0 comments on commit 248bf02

Please sign in to comment.