Skip to content
Snippets Groups Projects
Commit 3c507eb3 authored by Remi  PLANEL's avatar Remi PLANEL
Browse files

Resolve "Being able to download the distributions in refseqDB as a svg/png/csv"

parent 6225734c
No related branches found
No related tags found
1 merge request!169Resolve "Being able to download the distributions in refseqDB as a svg/png/csv"
......@@ -4,14 +4,22 @@ import PlotFigure from "~/components/PlotFigure";
import { useDisplay } from "vuetify";
import type { SortItem } from "@/components/ServerDbTable.vue"
import { ServerDbTable } from "#components"
import { useSerialize } from "@/composables/useSerialize";
import { useRasterize } from "@/composables/useRasterize";
import { useDownloadBlob } from '@/composables/useDownloadBlob';
import type { ComponentPublicInstance } from 'vue'
const sortBy: Ref<SortItem[]> = ref([{ key: 'type', order: "asc" }])
const itemValue = ref("id");
const { width } = useDisplay();
const scaleTransform: Ref<string[]> = ref([])
const { serialize } = useSerialize()
const { rasterize } = useRasterize()
const { download } = useDownloadBlob()
const facets = ref([
"replicon",
......@@ -32,6 +40,8 @@ const availableTaxo: Ref<string[]> = ref([
"phylum",
"Superkingdom"
]);
const scaleTypes = ref<string[]>(['linear', 'sqrt', 'log', 'symlog'])
const selectedTaxoRank = ref("phylum");
const headers = ref([
......@@ -121,22 +131,25 @@ const dataTableServerProps = computed(() => {
const defaultBarPlotOptions = computed(() => {
return {
x: { label: null, tickRotate: 45, ticks: 10 },
y: { grid: true, type: scaleType.value },
color: { legend: true },
width: computedWidth.value,
x: { label: null, tickRotate: 45 },
y: { grid: true, clamp: true },
// height: plotHeight.value + 100,
}
})
// system distri
const computedSystemDistribution = computed(() => {
if (toValue(msResult)?.facetDistribution?.type) {
return Object.entries(toValue(msResult).facetDistribution.type).map(([key, value]) => {
return {
type: key,
count: value
}
}).sort()
return Object.entries(toValue(msResult).facetDistribution.type)
.map(([key, value]) => {
return {
type: key,
count: value
}
}).sort()
} else { return [] }
})
......@@ -144,13 +157,15 @@ const computedDistriSystemOptions = computed(() => {
return {
...defaultBarPlotOptions.value,
marginBottom: 100,
y: { ...defaultBarPlotOptions.value.y, type: toValue(scaleType), label: "Count" },
x: { ...defaultBarPlotOptions.value.x, label: "Systems" },
width: computedWidth.value,
marks: [
// Plot.frame(),
Plot.barY(
toValue(computedSystemDistribution),
{
y: "count", x: 'type', tip: true,
// fill: "#6750a4",
sort: { x: "-y" },
},
......@@ -158,6 +173,9 @@ const computedDistriSystemOptions = computed(() => {
],
};
});
// Taxo distri
const computedTaxonomyDistribution = computed(() => {
if (toValue(msResult)?.facetDistribution?.[selectedTaxoRank.value]) {
return Object.entries(toValue(msResult).facetDistribution[selectedTaxoRank.value]).map(([key, value]) => {
......@@ -174,6 +192,9 @@ const computedDistriTaxoOptions = computed(() => {
return {
...defaultBarPlotOptions.value,
marginBottom: 100,
x: { ...defaultBarPlotOptions.value.x, label: selectedTaxoRank.value },
y: { ...defaultBarPlotOptions.value.y, type: toValue(scaleType), label: "Count" },
width: computedWidth.value,
marks: [
Plot.barY(
toValue(computedTaxonomyDistribution),
......@@ -212,8 +233,8 @@ const binPlotOptions = ref({
marginBottom: 200,
padding: 0,
grid: true,
x: { tickRotate: 90, tip: true, },
x: { tickRotate: 90, tip: true, label: "Systems" },
// y: { tickFormat: 's' },
color: { scheme: "turbo", legend: true },
})
......@@ -225,7 +246,11 @@ const binPlotDataOptions = computed(() => {
color: {
...binPlotOptions.value.color,
type: scaleType.value
type: scaleType.value,
tickFormat: '~s',
ticks: scaleType.value === 'symlog' ? 3 : 5,
// width: 350
},
// fy: { domain: groupSortDomain.value },
marks: [
......@@ -236,6 +261,23 @@ const binPlotDataOptions = computed(() => {
})
const scaleType = ref("linear")
const systemsDistributionPlot = ref<ComponentPublicInstance | null>(null)
const taxonomicDistributionPlot = ref<ComponentPublicInstance | null>(null)
const heatmapPlot = ref<ComponentPublicInstance | null>(null)
function downloadSvg(component: ComponentPublicInstance | null, filename: string) {
const blob = toValue(serialize(toValue(component)))
if (blob !== undefined) {
download(blob, filename)
}
}
async function downloadPng(component: ComponentPublicInstance | null, filename: string) {
const blob = await rasterize(toValue(component), filename)?.then((blob) => {
download(blob, filename)
})
}
</script>
<template>
......@@ -253,21 +295,72 @@ const scaleType = ref("linear")
<v-btn icon="md:grid_view" value="grid"></v-btn>
<v-btn icon="md:view_agenda" value="fullwidth"></v-btn>
</v-btn-toggle>
<v-select v-model="scaleType" class="mx-2" density="compact"
:items="['linear', 'sqrt', 'symlog']" label="Scale Type" hide-details="auto"></v-select>
<v-select v-model="scaleType" class="mx-2" density="compact" :items="scaleTypes"
label="Scale Type" hide-details="auto"></v-select>
<v-select v-model="selectedTaxoRank" :items="availableTaxo" density="compact"
label="Select taxonomic rank" hide-details="auto" class="mx-2"></v-select>
</v-toolbar>
<v-row align="start">
<v-col :cols="fullWidth ? 12 : 6">
<PlotFigure :options="unref(computedDistriSystemOptions)" defer></PlotFigure>
<v-card variant="flat">
<v-toolbar density="compact" color="transparent">
<v-spacer></v-spacer>
<v-menu>
<template v-slot:activator="{ props }">
<v-btn color="primary" prepend-icon="md:download" v-bind="props">
export
</v-btn>
</template>
<v-list>
<v-list-item value="svg">
<v-list-item-title
@click="downloadSvg(systemsDistributionPlot, 'df-systems-distribution.svg')">to
svg</v-list-item-title>
</v-list-item>
<v-list-item value="png">
<v-list-item-title
@click="downloadPng(systemsDistributionPlot, 'df-systems-distribution.png')">to
png</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-toolbar>
<PlotFigure ref="systemsDistributionPlot"
:options="unref(computedDistriSystemOptions)" defer></PlotFigure>
</v-card>
</v-col>
<v-col :cols="fullWidth ? 12 : 6">
<PlotFigure defer :options="unref(computedDistriTaxoOptions)"></PlotFigure>
<v-card variant="flat">
<v-toolbar density="compact" color="transparent">
<v-spacer></v-spacer>
<v-menu>
<template v-slot:activator="{ props }">
<v-btn color="primary" prepend-icon="md:download" v-bind="props">
export
</v-btn>
</template>
<v-list>
<v-list-item value="svg">
<v-list-item-title
@click="downloadSvg(taxonomicDistributionPlot, 'df-taxonomic-distribution.svg')">to
svg</v-list-item-title>
</v-list-item>
<v-list-item value="png">
<v-list-item-title
@click="downloadPng(taxonomicDistributionPlot, 'df-taxonomic-distribution.png')">to
png</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-toolbar>
<PlotFigure ref="taxonomicDistributionPlot" defer
:options="unref(computedDistriTaxoOptions)"></PlotFigure>
</v-card>
</v-col>
</v-row>
</v-card>
......@@ -285,19 +378,42 @@ const scaleType = ref("linear")
<v-expansion-panel-text>
<v-card v-if="pendingAllHits === false" flat color="transparent">
<v-toolbar flat color="transparent" density="compact">
<v-select v-model="scaleType" class="mx-2" density="compact"
:items="['linear', 'sqrt', 'symlog']" label="Scale Type" hide-details="auto"></v-select>
<v-select v-model="scaleType" class="mx-2" density="compact" :items="scaleTypes"
label="Scale Type" hide-details="auto"></v-select>
<v-select v-model="selectedTaxoRank" :items="availableTaxo" density="compact"
label="Select taxonomic rank" hide-details="auto" class="mx-2"></v-select>
</v-toolbar>
<PlotFigure v-if="toValue(binPlotDataOptions) !== null" :options="unref(binPlotDataOptions)"
defer>
</PlotFigure>
<v-card v-if="toValue(binPlotDataOptions) !== null" variant="flat">
<v-toolbar density="compact" color="transparent">
<v-spacer></v-spacer>
<v-menu>
<template v-slot:activator="{ props }">
<v-btn color="primary" prepend-icon="md:download" v-bind="props">
export
</v-btn>
</template>
<v-list>
<v-list-item value="svg">
<v-list-item-title
@click="downloadSvg(heatmapPlot, 'df-heatmap-systems-taxonomy.svg')">to
svg</v-list-item-title>
</v-list-item>
<v-list-item value="png">
<v-list-item-title
@click="downloadPng(heatmapPlot, 'df-heatmap-systems-taxonomy.png')">to
png</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-toolbar>
<PlotFigure ref="heatmapPlot" :options="unref(binPlotDataOptions)" defer>
</PlotFigure>
</v-card>
</v-card>
<v-card v-else flat color="transparent">
<v-skeleton-loader type="card" :loading="pendingAllHits"
height="400"></v-skeleton-loader>
<v-skeleton-loader type="card" :loading="pendingAllHits" height="400"></v-skeleton-loader>
</v-card>
</v-expansion-panel-text>
</v-expansion-panel>
......
import Papa from 'papaparse';
// import { saveAs } from "file-saver";
import { useDownloadBlob } from './useDownloadBlob';
const { download } = useDownloadBlob()
export function useCsvDownload(
rawData: MaybeRef<Record<string, any>>,
......@@ -29,12 +31,7 @@ export function useCsvDownload(
})
const csvContent = Papa.unparse(toValue(data), { columns: toValue(columns) });
blob.value = new Blob([csvContent], { type: "text/csv" });
var a = document.createElement("a");
a.href = URL.createObjectURL(blob.value);
a.download = filename.value;
a.click();
URL.revokeObjectURL(a.href);
download(blob, filename)
}
return { data, filename }
}
export function useDownloadBlob() {
function download(blob: MaybeRef<Blob>, filename: MaybeRef<string>) {
const toValueBlob = toValue(blob)
const toValueFilename = toValue(filename)
var a = document.createElement("a");
a.href = URL.createObjectURL(toValueBlob);
a.download = toValueFilename;
a.click();
URL.revokeObjectURL(a.href);
}
return { download }
}
\ No newline at end of file
import { useDownloadBlob } from './useDownloadBlob';
import { useSerialize } from './useSerialize';
import { useSvgPlot } from './useSvgPlot';
const { serialize } = useSerialize()
export function useRasterize() {
function rasterize(component: MaybeRef<ComponentPublicInstance | null>, filename: MaybeRef<string>) {
const toValueCompo = toValue(component)
if (toValueCompo !== null) {
const { svg } = useSvgPlot(toValueCompo)
const toValueSvg = toValue(svg)
if (toValueSvg !== null) {
let resolve, reject;
const promise: Promise<Blob> = new Promise((y, n) => (resolve = y, reject = n));
const image = new Image;
image.onerror = reject;
console.log(toValueSvg)
image.onload = () => {
console.log("try to get boundingclientRect")
const rect = toValueSvg.getBoundingClientRect();
console.log(rect)
const canvas = document.createElement("canvas");
canvas.width = rect.width
canvas.height = rect.height
const ctx = canvas.getContext("2d")
if (ctx !== null) {
ctx.drawImage(image, 0, 0, rect.width, rect.height);
ctx.canvas.toBlob(resolve);
}
}
const blob = toValue(serialize(component))
if (blob !== undefined) {
image.src = URL.createObjectURL(blob);
}
return promise;
}
}
}
return { rasterize }
}
\ No newline at end of file
import { useSvgPlot } from './useSvgPlot';
export function useSerialize() {
const xmlns = ref("http://www.w3.org/2000/xmlns/");
const xlinkns = ref("http://www.w3.org/1999/xlink");
const svgns = ref("http://www.w3.org/2000/svg");
const blob = ref<Blob>()
function serialize(compo: MaybeRef<ComponentPublicInstance | null>) {
const toValueCompo = toValue(compo)
if (toValueCompo !== null) {
const { svg } = useSvgPlot(toValueCompo)
const toValueSvg = toValue(svg)
if (toValueSvg !== null) {
const clonedSvg = toValueSvg.cloneNode(true);
const fragment = window.location.href + "#";
const walker = document.createTreeWalker(toValueSvg, NodeFilter.SHOW_ELEMENT);
while (walker.nextNode()) {
for (const attr of walker.currentNode.attributes) {
if (attr.value.includes(fragment)) {
attr.value = attr.value.replace(fragment, "#");
}
}
}
clonedSvg.setAttributeNS(xmlns.value, "xmlns", svgns.value);
clonedSvg.setAttributeNS(xmlns.value, "xmlns:xlink", xlinkns.value);
const serializer = new window.XMLSerializer;
const string = serializer.serializeToString(clonedSvg);
blob.value = new Blob([string], { type: "image/svg+xml" });
return blob
}
else { return undefined }
}
}
return { serialize }
}
\ No newline at end of file
export function useSvgPlot(component: MaybeRef<ComponentPublicInstance>) {
const svg = ref<SVGElement | null>(null)
const toValueCompo = toValue(component)
const rootElem = toValueCompo.$el
svg.value = rootElem.querySelector("svg.plot")
return { svg }
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment