From ce6c391d8186f00a98408f0c1a9ca883cc3f24ae Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Thu, 12 Jun 2025 23:50:24 +0100 Subject: [PATCH] feat: sort by location --- src/lib/index.ts | 17 +++++ src/routes/+page.svelte | 99 ++++++++++++++++++++++++- src/routes/space/[id]/+page.svelte | 4 +- src/routes/space/[id]/edit/+page.svelte | 2 +- 4 files changed, 115 insertions(+), 7 deletions(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 290e2b5..eef7a5f 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -66,3 +66,20 @@ export function timeToMins(time: string) { const [hour, min] = time.split(":"); return Number(hour) * 60 + Number(min); } + +export function haversineDistance( + lat1Deg: number, + lng1Deg: number, + lat2Deg: number, + lng2Deg: number, + radius: number = 6371e3 +): number { + const lat1 = lat1Deg * (Math.PI / 180); + const lat2 = lat2Deg * (Math.PI / 180); + const deltaLat = (lat2Deg - lat1Deg) * (Math.PI / 180); + const deltaLng = (lng2Deg - lng1Deg) * (Math.PI / 180); + const e1 = + Math.pow(Math.sin(deltaLat / 2), 2) + + Math.pow(Math.sin(deltaLng / 2), 2) * Math.cos(lat1) * Math.cos(lat2); + return radius * 2 * Math.asin(Math.sqrt(e1)); +} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index fa3f5b8..f2e2943 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -3,8 +3,16 @@ import defaultImg from "$lib/assets/study_space.png"; import crossUrl from "$lib/assets/cross.svg"; import Navbar from "$lib/components/Navbar.svelte"; - import { allTags, volumeTags, wifiTags, powerOutletTags } from "$lib"; + import { + allTags, + volumeTags, + wifiTags, + powerOutletTags, + gmapsLoader, + haversineDistance + } from "$lib"; import Button from "$lib/components/Button.svelte"; + import { onMount } from "svelte"; const { data } = $props(); const { @@ -137,6 +145,60 @@ tagFilter = ""; }; } + + let sortMapElem = $state(); + let sortNear = $state<{ lat: number; lng: number }>(); + const sortedStudySpaces = $derived( + sortNear + ? studySpaces.toSorted((a, b) => { + if (!sortNear) return 0; + type DBLatLng = { lat: number; lng: number } | undefined; + const aLoc = a.building_location as unknown as google.maps.places.PlaceResult; + const bLoc = b.building_location as unknown as google.maps.places.PlaceResult; + const aLatLng = aLoc.geometry?.location as DBLatLng; + const bLatLng = bLoc.geometry?.location as DBLatLng; + const aDistance = haversineDistance( + sortNear.lat, + sortNear.lng, + aLatLng?.lat || sortNear.lat, + aLatLng?.lng || sortNear.lng + ); + const bDistance = haversineDistance( + sortNear.lat, + sortNear.lng, + bLatLng?.lat || sortNear.lat, + bLatLng?.lng || sortNear.lng + ); + return aDistance - bDistance; + }) + : filteredStudySpaces + ); + let marker: google.maps.marker.AdvancedMarkerElement | undefined; + onMount(async () => { + if (!sortMapElem) return console.error("sortMapElem is not defined"); + const loader = await gmapsLoader(); + const { Map } = await loader.importLibrary("maps"); + const { AdvancedMarkerElement } = await loader.importLibrary("marker"); + const map = new Map(sortMapElem, { + center: { lat: 53.6, lng: -1.56 }, + zoom: 5, + mapId: "9f4993cd3fb1504d495821a5" + }); + marker = new AdvancedMarkerElement({ + map, + title: "Find near here" + }); + map.addListener("click", (e: google.maps.MapMouseEvent) => { + console.log("Clicked map at", e.latLng); + sortNear = e.latLng + ? { + lat: e.latLng.lat(), + lng: e.latLng.lng() + } + : sortNear; + if (marker) marker.position = e.latLng; + }); + }); @@ -169,7 +231,7 @@
- {#each selectedTags as tagName (tagName)} + {#each selectedTags as tagName, idx (tagName + idx)} {#if dropdownVisible}
- {#each filteredTags as avaliableTag (avaliableTag)} + {#each filteredTags as avaliableTag, idx (avaliableTag + idx)}
+
+

Find nearby:

+ +
+
- {#each filteredStudySpaces as studySpace (studySpace.id)} + {#each sortedStudySpaces as studySpace (studySpace.id)} {@const imgUrl = studySpace.study_space_images.length > 0 ? supabase.storage @@ -284,6 +363,13 @@ justify-content: center; margin-bottom: 0.5rem; } + .location-filter-container { + grid-column: 1 / -1; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + } .time-filter-container label { display: flex; align-items: center; @@ -403,6 +489,11 @@ text-decoration: underline; } + .sortMap { + aspect-ratio: 1 / 1; + width: 100%; + } + @media (max-width: 20rem) { main { grid-template-columns: 1fr; diff --git a/src/routes/space/[id]/+page.svelte b/src/routes/space/[id]/+page.svelte index 9e37048..eed68de 100644 --- a/src/routes/space/[id]/+page.svelte +++ b/src/routes/space/[id]/+page.svelte @@ -137,7 +137,7 @@
{#if space.tags.length > 0}
- {#each space.tags as tag (tag)} + {#each space.tags as tag, idx (tag + idx)} {tag} @@ -172,7 +172,7 @@ {#if place.name} {place.name}
{/if} - {#each place.formatted_address?.split(",") || [] as line (line)} + {#each place.formatted_address?.split(",") || [] as line, idx (line + idx)} {line.trim()}
{/each}

diff --git a/src/routes/space/[id]/edit/+page.svelte b/src/routes/space/[id]/edit/+page.svelte index 067053d..78d9479 100644 --- a/src/routes/space/[id]/edit/+page.svelte +++ b/src/routes/space/[id]/edit/+page.svelte @@ -370,7 +370,7 @@
- {#each studySpaceData.opening_times as opening_time, index (index)} + {#each studySpaceData.opening_times as opening_time, index (opening_time)}