From 729cad8bf2dcebb592b8d62a813ed8dd6ad55c0c Mon Sep 17 00:00:00 2001 From: MSDrao Date: Sun, 5 Jan 2025 23:21:34 +0530 Subject: [PATCH 1/3] Added download mep data functionality using papaparse npm package --- frontend/package.json | 1 + frontend/src/stores/map.js | 8 ++- frontend/src/views/MapView.vue | 104 ++++++++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 3 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 7d97fec..2112875 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ "leaflet": "^1.9.4", "leaflet-easybutton": "^2.4.0", "leaflet-iconmaterial": "^1.1.0", + "papaparse": "^5.4.1", "pinia": "^2.1.6", "swagger-ui": "^5.10.5", "vee-validate": "^4.13.2", diff --git a/frontend/src/stores/map.js b/frontend/src/stores/map.js index 9844959..f96b83a 100644 --- a/frontend/src/stores/map.js +++ b/frontend/src/stores/map.js @@ -10,6 +10,7 @@ export const useMapStore = defineStore('map', () => { const modelFeatures = ref({}) const perceptualModelsGeojson = ref([]) const mapLoaded = ref(false) + let currentFilteredData = ref([]) function onEachFeature(feature, layer) { let content = `

Perceptual model of ${feature.properties.location.long_name}

` @@ -74,12 +75,12 @@ export const useMapStore = defineStore('map', () => { content += '

Temporal zone:

' 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) => { @@ -121,6 +122,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) @@ -134,6 +136,7 @@ export const useMapStore = defineStore('map', () => { }, pointToLayer: pointToLayer }) + // add filtered features layerGroup.value.addLayer(modelFeatures.value) } @@ -155,6 +158,7 @@ export const useMapStore = defineStore('map', () => { mapLoaded, fetchPerceptualModelsGeojson, filterFeatures, - resetFilter + resetFilter, + currentFilteredData } }) diff --git a/frontend/src/views/MapView.vue b/frontend/src/views/MapView.vue index 8cf1705..02b8a5a 100644 --- a/frontend/src/views/MapView.vue +++ b/frontend/src/views/MapView.vue @@ -13,6 +13,7 @@ :icon="showDataDrawer ? mdiChevronRight : mdiChevronLeft" size="x-small"> + Download Filtered Map Data @@ -47,7 +48,8 @@ import DataViewDrawer from '@/components/DataViewDrawer.vue'; import TheLeafletMap from '@/components/TheLeafletMap.vue'; import { mdiChevronRight, mdiChevronLeft } from '@mdi/js' import { useMapStore } from '@/stores/map'; -import { useDisplay } from 'vuetify' +import { useDisplay } from 'vuetify'; +import Papa from 'papaparse'; const { mdAndDown } = useDisplay() const mapStore = useMapStore() @@ -55,6 +57,22 @@ const mapStore = useMapStore() const showFilterDrawer = ref(true) const showDataDrawer = ref(true) const dataDrawerRef = ref(null) +const ignoreColumnInCSV = [ 'Type', 'Id', 'Location Id', 'Spatialzone Id', 'Temporalzone Id', 'Three D Info', 'Process Taxonomies Identifier', 'Model Type Id', 'Process Taxonomies Process Level', 'Process Taxonomies Function Id', 'Process Taxonomies Id', 'Process Taxonomies Process Alt Name Id', 'Spatial Zone Type Id', 'Temporal Zone Type Id', 'Location Lon', 'Location Name', 'Location Lat', 'Location Pt','process_taxonomies_identifier','process_taxonomies_process_level','process_taxonomies_function_id','process_taxonomies_id','process_taxonomies_process_alt_name_id'] + +const renameColumnInCSV = { +"figure num": "Figure Number", +"citation citation": "Citation", +"location long name": "Location Name", +"location area km2": "Location Area [km^2]", +"process taxonomies process": "Taxonomy Processes", +"geol info": "Geological Info", +"topo info": "Topographic 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 onFilter = (data) => { const filters = { @@ -111,6 +129,90 @@ const translateData = () => { } } +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); + + // Ignore columns as per requirement + if (ignoreColumnInCSV.includes(newKey)) { + continue; + } + + 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) { + // Ignore columns as per requirement + if (ignoreColumnInCSV.includes(`${key}_${subKey}`)) { + continue; + } + if (Object.prototype.hasOwnProperty.call(item, subKey)) { + if (!arrayValues[subKey]) { + arrayValues[subKey] = []; + } + arrayValues[subKey].push(item[subKey]); + } + } + }); + for (const arrayKey in arrayValues) { + result[renamecsvColumn(`${newKey} ${arrayKey}`)] = arrayValues[arrayKey].join(', '); + } + } + } else { + // Assign values directly + result[newKey] = obj[key]; + } + } + } + return result; +} + +function flattenJSON(data) { + const result = []; + data.forEach(item => { + // As per requirement only include properties, not geometry + result.push(flattenItem(item.properties)); + }); + return result; +} + +const downloadMapData = () => { + const jsonData = mapStore.currentFilteredData; + const flattenedData = flattenJSON(jsonData); + + const csv = Papa.unparse(flattenedData, {}); + + 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); +} + +const renamecsvColumn = (name) => { + let newName = name.replaceAll("_", " "); + newName = newName.replace(/\b\w/g, char => char.toLowerCase()); + if (renameColumnInCSV[newName]) { + newName = renameColumnInCSV[newName]; + } else { + newName = newName.replace(/\b\w/g, char => char.toUpperCase()); + } + return newName; +} + From 0f2388547f71008e4542b013da05561cf398bf1e Mon Sep 17 00:00:00 2001 From: MSDrao Date: Thu, 9 Jan 2025 12:31:55 +0530 Subject: [PATCH 2/3] Updated the Download map data changes --- frontend/src/components/DataViewDrawer.vue | 6 ++ frontend/src/components/DownloadMapData.vue | 105 ++++++++++++++++++++ frontend/src/views/MapView.vue | 104 +------------------ 3 files changed, 112 insertions(+), 103 deletions(-) create mode 100644 frontend/src/components/DownloadMapData.vue diff --git a/frontend/src/components/DataViewDrawer.vue b/frontend/src/components/DataViewDrawer.vue index 863becc..46764e9 100644 --- a/frontend/src/components/DataViewDrawer.vue +++ b/frontend/src/components/DataViewDrawer.vue @@ -30,12 +30,18 @@

{{ totalModels }}

+ + + + + \ No newline at end of file diff --git a/frontend/src/views/MapView.vue b/frontend/src/views/MapView.vue index 02b8a5a..8cf1705 100644 --- a/frontend/src/views/MapView.vue +++ b/frontend/src/views/MapView.vue @@ -13,7 +13,6 @@ :icon="showDataDrawer ? mdiChevronRight : mdiChevronLeft" size="x-small"> - Download Filtered Map Data @@ -48,8 +47,7 @@ import DataViewDrawer from '@/components/DataViewDrawer.vue'; import TheLeafletMap from '@/components/TheLeafletMap.vue'; import { mdiChevronRight, mdiChevronLeft } from '@mdi/js' import { useMapStore } from '@/stores/map'; -import { useDisplay } from 'vuetify'; -import Papa from 'papaparse'; +import { useDisplay } from 'vuetify' const { mdAndDown } = useDisplay() const mapStore = useMapStore() @@ -57,22 +55,6 @@ const mapStore = useMapStore() const showFilterDrawer = ref(true) const showDataDrawer = ref(true) const dataDrawerRef = ref(null) -const ignoreColumnInCSV = [ 'Type', 'Id', 'Location Id', 'Spatialzone Id', 'Temporalzone Id', 'Three D Info', 'Process Taxonomies Identifier', 'Model Type Id', 'Process Taxonomies Process Level', 'Process Taxonomies Function Id', 'Process Taxonomies Id', 'Process Taxonomies Process Alt Name Id', 'Spatial Zone Type Id', 'Temporal Zone Type Id', 'Location Lon', 'Location Name', 'Location Lat', 'Location Pt','process_taxonomies_identifier','process_taxonomies_process_level','process_taxonomies_function_id','process_taxonomies_id','process_taxonomies_process_alt_name_id'] - -const renameColumnInCSV = { -"figure num": "Figure Number", -"citation citation": "Citation", -"location long name": "Location Name", -"location area km2": "Location Area [km^2]", -"process taxonomies process": "Taxonomy Processes", -"geol info": "Geological Info", -"topo info": "Topographic 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 onFilter = (data) => { const filters = { @@ -129,90 +111,6 @@ const translateData = () => { } } -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); - - // Ignore columns as per requirement - if (ignoreColumnInCSV.includes(newKey)) { - continue; - } - - 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) { - // Ignore columns as per requirement - if (ignoreColumnInCSV.includes(`${key}_${subKey}`)) { - continue; - } - if (Object.prototype.hasOwnProperty.call(item, subKey)) { - if (!arrayValues[subKey]) { - arrayValues[subKey] = []; - } - arrayValues[subKey].push(item[subKey]); - } - } - }); - for (const arrayKey in arrayValues) { - result[renamecsvColumn(`${newKey} ${arrayKey}`)] = arrayValues[arrayKey].join(', '); - } - } - } else { - // Assign values directly - result[newKey] = obj[key]; - } - } - } - return result; -} - -function flattenJSON(data) { - const result = []; - data.forEach(item => { - // As per requirement only include properties, not geometry - result.push(flattenItem(item.properties)); - }); - return result; -} - -const downloadMapData = () => { - const jsonData = mapStore.currentFilteredData; - const flattenedData = flattenJSON(jsonData); - - const csv = Papa.unparse(flattenedData, {}); - - 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); -} - -const renamecsvColumn = (name) => { - let newName = name.replaceAll("_", " "); - newName = newName.replace(/\b\w/g, char => char.toLowerCase()); - if (renameColumnInCSV[newName]) { - newName = renameColumnInCSV[newName]; - } else { - newName = newName.replace(/\b\w/g, char => char.toUpperCase()); - } - return newName; -} - From c4f5d67dee6c673b76b3dab0fa087469e6c8186b Mon Sep 17 00:00:00 2001 From: MSDrao Date: Fri, 10 Jan 2025 15:39:14 +0530 Subject: [PATCH 3/3] Modified the csv columns logic --- frontend/src/components/DownloadMapData.vue | 23 +++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/DownloadMapData.vue b/frontend/src/components/DownloadMapData.vue index 2245906..77467f2 100644 --- a/frontend/src/components/DownloadMapData.vue +++ b/frontend/src/components/DownloadMapData.vue @@ -6,23 +6,36 @@ import { useMapStore } from '@/stores/map'; import Papa from 'papaparse'; -const mapStore = useMapStore() +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", - "textmodel snipped": "Textmodel Snippet" + "model type name": "Model Type" } -const csvColumns = ['Figure Number', 'Figure Caption', 'Fgure Url', 'Textmodel Section Number', 'Textmodel Section Name', 'Textmodel Page Number', 'Textmodel Snippet', 'Citation', 'Citation Url', 'Citation Attribution', 'Citation Attribution Url', 'Location Name', 'Location Country', 'Location Area [km^2]', 'Taxonomy Processes', 'Vegetation Info', 'Soil Info', 'Geological Info', 'Topographic Info', 'Uncertainty Info', 'Other Info', 'Number of Spatial Zones', 'Spatial Zone Type', 'Number of Temporal Zones', 'Temporal Zone Type', 'Model Type'] +const csvColumns = Object.values(renameColumnInCSV); function flattenItem(obj, parentKey = '', result = {}) { for (const key in obj) { @@ -97,8 +110,6 @@ function renamecsvColumn(name) { newName = newName.replace(/\b\w/g, char => char.toLowerCase()); if (renameColumnInCSV[newName]) { newName = renameColumnInCSV[newName]; - } else { - newName = newName.replace(/\b\w/g, char => char.toUpperCase()); } return newName; }