Skip to content

Commit

Permalink
feat: add ability to bulk upload πŸŽ‰
Browse files Browse the repository at this point in the history
Signed-off-by: Vinayak Kulkarni <19776877+vinayakkulkarni@users.noreply.github.com>
  • Loading branch information
vinayakkulkarni committed Mar 22, 2023
1 parent fa9b12e commit d2593f3
Show file tree
Hide file tree
Showing 10 changed files with 513 additions and 160 deletions.
62 changes: 62 additions & 0 deletions assets/workers/csv.worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { DSVRowArray } from 'd3-dsv';
import type {
ExpenseFeature,
ExpenseFeatureCollection,
} from '~/@types/expense';
import { csvParse } from 'd3-dsv';

/**
* Waits for some seconds.
*
* @param {number} ms - milliseconds to wait
* @returns {Promise<void>}
*/
function snooze(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

addEventListener('message', async (event: MessageEvent) => {
const { payload, sleep } = event.data.message;
await snooze(sleep);
const data = csvParse(payload);
const geojson = await transformGeoJSON(data);
postMessage(geojson);
});

/**
* Transforms a CSV to GeoJSON
*
* @param {DSVRowArray<string>} payload - CSV data parsed by csvParse()
* @returns {Promise<ExpenseFeature>} - GeoJSON data
*/
async function transformGeoJSON(
payload: DSVRowArray<string>,
): Promise<ExpenseFeatureCollection> {
const geojson = {
id: useUuid(),
type: 'FeatureCollection',
features: [],
} as ExpenseFeatureCollection;
for await (const t of payload) {
geojson.features.push({
type: 'Feature',
properties: {
expense: {
amount: t.amount,
description: t.description,
type: t.type,
},
date: parseInt(t.date as string, 10),
},
geometry: {
type: 'Point',
coordinates: [
parseFloat(t?.lng as string) || 0,
parseFloat(t?.lat as string) || 0,
],
},
} as unknown as ExpenseFeature);
}
console.log('built geojson: ', geojson);
return geojson;
}
13 changes: 11 additions & 2 deletions components/map/CommonMap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,18 @@
* @param {Map} e - Mapbox GL Map object
* @returns {void}
*/
function onMapLoaded(e: Map): void {
async function onMapLoaded(e: Map): Promise<void> {
map = e;
syncMap();
}
/**
* Sync the map
*
* @returns {void}
*/
function syncMap(): void {
const events: string[] = [
'idle',
'moveend',
'touchend',
'style.load',
Expand Down Expand Up @@ -256,6 +264,7 @@
store.setStyleChanged(true);
store.setTilesLoaded(true);
store.setLoaded(true);
emit('on-loaded', map);
setMapState();
}
/**
Expand Down
Empty file.
108 changes: 108 additions & 0 deletions components/map/_partials/Upload.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<template>
<div
class="w-full p-2"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<div class="w-full min-w-max">
<div class="rounded p-2">
<p class="text-xl pb-4">Upload CSV</p>
<div
class="flex justify-center max-w-lg px-6 pt-5 pb-6 shadow-md border-2 border-gray-600 border-dashed rounded-md dark:border-gray-200"
>
<div v-if="file.name === null" class="space-y-1 text-center">
<svg
class="w-12 h-12 mx-auto"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"
/>
</svg>
<div class="flex items-center justify-center flex-col text-sm">
<label
for="file-upload"
class="relative font-medium text-blue-600 rounded-md cursor-pointer hover:text-blue-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"
>
<span>Upload a file</span>
<!-- Accept only csv -->
<input
id="file-upload"
name="file-upload"
type="file"
class="hidden sr-only"
accept=".csv,.tsv"
@change="onFileChange"
/>
</label>
</div>
<div class="text-xs text-gray-600 dark:text-gray-400">
<pre>.csv, .tsv only</pre>
</div>
</div>
<div v-else :title="file.name" class="w-min">
<p class="w-40 break-all">
{{ file.name }}
</p>
</div>
</div>
<p class="font-semibold pt-4">
<a
href="/demo.csv"
class="rounded-full bg-white py-1.5 px-3 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
>
Download Sample File
</a>
</p>
</div>
</div>
</div>
</template>

<script lang="ts">
export default defineComponent({
name: 'ExpenseUpload',
setup() {
const { $worker } = useNuxtApp();
const file = ref({
name: null as string | null,
});
/**
* Update file event
*
* @param {any} e - Event
*/
function onFileChange(e: unknown) {
const files = (e as any).target.files || (e as any).dataTransfer.files;
if (!files.length) return;
file.value.name = files[0].name;
uploadFile(files[0]);
}
/**
* Upload the file
*
* @param {File} file - The file
*/
function uploadFile(file: File) {
const reader = new FileReader();
reader.onload = (e: ProgressEvent<FileReader>) => {
$worker.send('csv', {
sleep: 0,
payload: e.target?.result,
});
};
reader.readAsBinaryString(file);
}
return { file, onFileChange };
},
});
</script>
9 changes: 7 additions & 2 deletions composables/useMap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MapOptions } from 'maplibre-gl';
import type { MapOptions } from 'maplibre-gl';
import type { Basemaps } from '~/@types/map';
import { defineStore } from 'pinia';
import { Basemaps } from '~/@types/map';

/**
* Map Store
Expand Down Expand Up @@ -125,6 +125,11 @@ export const useMap = defineStore({
},
toggleBasemapsWidget(): void {
this.utils.basemaps.shown = !this.utils.basemaps.shown;
this.utils.upload.shown = false;
},
toggleUploadsWidget(): void {
this.utils.upload.shown = !this.utils.upload.shown;
this.utils.basemaps.shown = false;
},
toggleCompassWidget(): void {
this.utils.compass.shown = !this.utils.compass.shown;
Expand Down
5 changes: 3 additions & 2 deletions config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ const nitro: NuxtConfig['nitro'] = {
};

const plugins: NuxtConfig['plugins'] = [
'~/plugins/v-mapbox.ts',
'~/plugins/v-click-outside.ts',
{ src: '~/plugins/v-click-outside.ts', mode: 'client' },
{ src: '~/plugins/v-mapbox.ts', mode: 'client' },
{ src: '~/plugins/v-web-workers.ts', mode: 'client' },
];

const runtimeConfig: NuxtConfig['runtimeConfig'] = {
Expand Down
4 changes: 2 additions & 2 deletions lib/v-mapbox/layers/deck.gl/VLayerDeckGeojson.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { MapboxLayer } from '@deck.gl/mapbox';
import { FeatureCollection } from 'geojson';
import type { PropType, Ref } from 'vue';
import { defineComponent } from 'vue';
import { h, defineComponent } from 'vue';
import { injectStrict, MapKey } from '../../utils';
export default defineComponent({
Expand All @@ -14,7 +14,7 @@
layerId: {
type: String as PropType<string>,
default: 'deck.gl-geojson-layer',
required: true,
required: false,
},
data: {
type: Object as PropType<FeatureCollection>,
Expand Down
Loading

0 comments on commit d2593f3

Please sign in to comment.