From e5ef48c0f986b7cb387a68e2c77d46fb3bf936bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katharina=20W=C3=BCnsche?= Date: Tue, 19 Nov 2024 13:48:08 +0100 Subject: [PATCH 1/7] feat: add custom petal markers to geojson map --- assets/petal.svg | 3 ++ components/geo-map.client.vue | 3 ++ components/geojson-map-window-content.vue | 1 + components/geojson-table-window-content.vue | 3 +- composables/use-petal-marker.ts | 46 +++++++++++++++++++++ tailwind.config.ts | 1 + types/global.d.ts | 3 ++ 7 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 assets/petal.svg create mode 100644 composables/use-petal-marker.ts diff --git a/assets/petal.svg b/assets/petal.svg new file mode 100644 index 0000000..1fb52be --- /dev/null +++ b/assets/petal.svg @@ -0,0 +1,3 @@ + + + diff --git a/components/geo-map.client.vue b/components/geo-map.client.vue index 207c732..ff221c0 100644 --- a/components/geo-map.client.vue +++ b/components/geo-map.client.vue @@ -14,11 +14,13 @@ import { import { type GeoMapContext, key, type MarkerProperties } from "@/components/geo-map.context"; import GeoMapPopupContent from "@/components/geo-map-popup-content.vue"; +import type { MarkerType } from "@/types/global"; interface Props { height: number; markers: Array>; width: number; + markerType?: MarkerType; } const props = defineProps(); @@ -182,6 +184,7 @@ onMounted(async () => { }); }, pointToLayer(feature, latlng) { + if (props.markerType === "petal") return usePetalMarker(feature, latlng); if (feature.properties.type === "reg") { return circleMarker(latlng, config.marker.region); } diff --git a/components/geojson-map-window-content.vue b/components/geojson-map-window-content.vue index be8e3aa..cc1ed31 100644 --- a/components/geojson-map-window-content.vue +++ b/components/geojson-map-window-content.vue @@ -33,6 +33,7 @@ const filteredMarkers = computed(() => { diff --git a/components/geojson-table-window-content.vue b/components/geojson-table-window-content.vue index 0f4dfe4..84bcf8d 100644 --- a/components/geojson-table-window-content.vue +++ b/components/geojson-table-window-content.vue @@ -8,7 +8,7 @@ import { } from "@tanstack/vue-table"; import { useGeojsonStore } from "@/stores/use-geojson-store.ts"; -import type { FeatureType } from "@/types/global"; +import type { FeatureType, MarkerType } from "@/types/global"; const GeojsonStore = useGeojsonStore(); const { addWindow, findWindowByTypeAndParam } = useWindowsStore(); @@ -132,6 +132,7 @@ function registerTable(table: Table) { targetType: "GeojsonMap", params: { url, + markerType: "petal" as MarkerType, }, title: "Variety Data - Map View", }); diff --git a/composables/use-petal-marker.ts b/composables/use-petal-marker.ts new file mode 100644 index 0000000..efca51f --- /dev/null +++ b/composables/use-petal-marker.ts @@ -0,0 +1,46 @@ +import { divIcon, type LatLng, marker } from "leaflet"; + +//@ts-expect-error asset not found +import petal from "~/assets/petal.svg?raw"; + +const sampleColors = [ + "#555b6e", + "#6f868e", + "#89b0ae", + "#a4cac5", + "#bee3db", + "#CF9997", + "#C14444", + "#ffd6ba", +]; + +function getPetalSVG() { + const div = document.createElement("div"); + div.className = "hover:scale-150 transition origin-center relative size-6"; + const NUM_PETALS = Math.round(Math.random() * 12) + 1; + for (let i = 0; i < NUM_PETALS; i++) { + const svg = document.createElement("svg"); + svg.setHTMLUnsafe(String(petal)); + svg.setAttribute("fill", sampleColors[i % sampleColors.length] ?? "#cccccc"); + svg.style.transformOrigin = "bottom"; + svg.style.transform = `rotate(${String((i * 360) / NUM_PETALS)}deg)`; + svg.className = "size-3 absolute ml-1.5"; + div.appendChild(svg); + } + + return div; +} + +export function usePetalMarker(_geoJSONPoint: unknown, latlng: LatLng) { + const htmlContent = getPetalSVG().outerHTML; // Example HTML content + const customIcon = divIcon({ + html: htmlContent, + className: "custom-marker-icon size-5", // Add custom CSS class for styling + // iconSize: [30, 30], // Adjust size as needed + }); + + const leafletMarker = marker(latlng, { + icon: customIcon, + }); + return leafletMarker; +} diff --git a/tailwind.config.ts b/tailwind.config.ts index 6f26ab6..6d7bc5b 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -10,6 +10,7 @@ const config = { "./layouts/**/*.@(ts|tsx|vue)", "./pages/**/*.@(ts|tsx|vue)", "./styles/**/*.css", + "./composables/**/*.@(ts|tsx|vue)", ], darkMode: ["class", 'data-ui-color-scheme="dark"'], plugins: [animatePlugin, typographyPlugin], diff --git a/types/global.d.ts b/types/global.d.ts index b850644..f402fa4 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -184,10 +184,13 @@ export const ListMapSchema = z.object({ }); export type ListMapWindowItem = WindowItemBase & z.infer; +export const MarkerEnum = z.enum(["petal", "default"]); +export type MarkerType = z.infer; export const GeojsonMapSchema = z.object({ targetType: z.literal("GeojsonMap"), params: z.object({ url: z.string(), + markerType: MarkerEnum.optional(), }), }); export type GeojsonMapWindowItem = WindowItemBase & z.infer; From 0c4e87f90936609e52890d281521bd4d3242ffc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katharina=20W=C3=BCnsche?= Date: Tue, 19 Nov 2024 14:53:47 +0100 Subject: [PATCH 2/7] chore: map petals to table columns --- composables/use-petal-marker.ts | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/composables/use-petal-marker.ts b/composables/use-petal-marker.ts index efca51f..417d4e4 100644 --- a/composables/use-petal-marker.ts +++ b/composables/use-petal-marker.ts @@ -1,7 +1,15 @@ +import type { Column } from "@tanstack/vue-table"; +import type { Feature as GeoJsonFeature, Point } from "geojson"; import { divIcon, type LatLng, marker } from "leaflet"; //@ts-expect-error asset not found -import petal from "~/assets/petal.svg?raw"; +import petal from "@/assets/petal.svg?raw"; +import type { MarkerProperties } from "@/lib/api-client"; +import { useGeojsonStore } from "@/stores/use-geojson-store.ts"; + +const GeojsonStore = useGeojsonStore(); +const { tables } = storeToRefs(GeojsonStore); +const url = "https://raw.githubusercontent.com/wibarab/wibarab-data/main/wibarab_varieties.geojson"; const sampleColors = [ "#555b6e", @@ -14,25 +22,33 @@ const sampleColors = [ "#ffd6ba", ]; -function getPetalSVG() { +function getPetalSVG(entries: Array>) { const div = document.createElement("div"); div.className = "hover:scale-150 transition origin-center relative size-6"; - const NUM_PETALS = Math.round(Math.random() * 12) + 1; - for (let i = 0; i < NUM_PETALS; i++) { + const NUM_PETALS = entries.length; + for (const [i, value] of entries.entries()) { const svg = document.createElement("svg"); svg.setHTMLUnsafe(String(petal)); svg.setAttribute("fill", sampleColors[i % sampleColors.length] ?? "#cccccc"); svg.style.transformOrigin = "bottom"; svg.style.transform = `rotate(${String((i * 360) / NUM_PETALS)}deg)`; svg.className = "size-3 absolute ml-1.5"; + svg.title = value.id; div.appendChild(svg); } return div; } -export function usePetalMarker(_geoJSONPoint: unknown, latlng: LatLng) { - const htmlContent = getPetalSVG().outerHTML; // Example HTML content +export function usePetalMarker(feature: GeoJsonFeature, latlng: LatLng) { + const table = tables.value.get(url); + const columns = table + ?.getVisibleLeafColumns() + .filter( + (col) => col.getCanFilter() && Object.keys(feature.properties).find((k) => k === col.id), + ); + //@ts-expect-error missing accessorFn + const htmlContent = getPetalSVG(columns).outerHTML; // Example HTML content const customIcon = divIcon({ html: htmlContent, className: "custom-marker-icon size-5", // Add custom CSS class for styling From 30fcd4024811f16da20d731ff3869924b92735c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katharina=20W=C3=BCnsche?= Date: Tue, 26 Nov 2024 09:34:43 +0100 Subject: [PATCH 3/7] chore: reuse petal.svg in markers --- assets/petal.svg | 3 -- assets/svg/petal.svg | 8 ++++++ components/geo-map.client.vue | 1 + composables/use-petal-marker.ts | 26 ++++++++++------- nuxt.config.ts | 19 ++++++++++++- package.json | 1 + pnpm-lock.yaml | 50 +++++++++++++++++++++++++++++++++ 7 files changed, 94 insertions(+), 14 deletions(-) delete mode 100644 assets/petal.svg create mode 100644 assets/svg/petal.svg diff --git a/assets/petal.svg b/assets/petal.svg deleted file mode 100644 index 1fb52be..0000000 --- a/assets/petal.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/svg/petal.svg b/assets/svg/petal.svg new file mode 100644 index 0000000..31f9e7b --- /dev/null +++ b/assets/svg/petal.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/components/geo-map.client.vue b/components/geo-map.client.vue index ff221c0..1bf7396 100644 --- a/components/geo-map.client.vue +++ b/components/geo-map.client.vue @@ -246,6 +246,7 @@ provide(key, context);