mirror of
https://github.com/System-End/daydream-phoenix.git
synced 2026-04-19 19:45:13 +00:00
Use new Leaflet map
This commit is contained in:
parent
8fff005ae5
commit
82fd1aaa6d
6 changed files with 156 additions and 6 deletions
8
bun.lock
8
bun.lock
|
|
@ -7,8 +7,10 @@
|
|||
"@fontsource/atkinson-hyperlegible": "^5.2.6",
|
||||
"@sveltejs/adapter-node": "^5.2.13",
|
||||
"@sveltejs/adapter-static": "^3.0.8",
|
||||
"@types/leaflet": "^1.9.20",
|
||||
"airtable": "^0.12.2",
|
||||
"gsap": "^3.13.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"lenis": "^1.3.4",
|
||||
"winston": "^3.17.0",
|
||||
},
|
||||
|
|
@ -196,6 +198,10 @@
|
|||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
|
||||
|
||||
"@types/leaflet": ["@types/leaflet@1.9.20", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-rooalPMlk61LCaLOvBF2VIf9M47HgMQqi5xQ9QRi7c8PkdIe0WrIi5IxXUXQjAdL0c+vcQ01mYWbthzmp9GHWw=="],
|
||||
|
||||
"@types/node": ["@types/node@14.18.63", "", {}, "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="],
|
||||
|
||||
"@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
|
||||
|
|
@ -292,6 +298,8 @@
|
|||
|
||||
"kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="],
|
||||
|
||||
"leaflet": ["leaflet@1.9.4", "", {}, "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="],
|
||||
|
||||
"lenis": ["lenis@1.3.4", "", { "peerDependencies": { "@nuxt/kit": ">=3.0.0", "react": ">=17.0.0", "vue": ">=3.0.0" }, "optionalPeers": ["@nuxt/kit", "react", "vue"] }, "sha512-WIGk8wiV2ABm/T7M+NC+tAV8fjzNJD1J4z11aZ3mTtx7WAZX/4QdCNhBO0g/TqXISA+/3hTbzrPC4FW1nhoNMQ=="],
|
||||
|
||||
"lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
|
||||
|
|
|
|||
|
|
@ -30,8 +30,10 @@
|
|||
"@fontsource/atkinson-hyperlegible": "^5.2.6",
|
||||
"@sveltejs/adapter-node": "^5.2.13",
|
||||
"@sveltejs/adapter-static": "^3.0.8",
|
||||
"@types/leaflet": "^1.9.20",
|
||||
"airtable": "^0.12.2",
|
||||
"gsap": "^3.13.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"lenis": "^1.3.4",
|
||||
"winston": "^3.17.0"
|
||||
}
|
||||
|
|
|
|||
77
src/components/MapComponent.svelte
Normal file
77
src/components/MapComponent.svelte
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
let mapContainer: HTMLElement;
|
||||
let map: any;
|
||||
|
||||
onMount(async () => {
|
||||
if (browser) {
|
||||
// Dynamically import Leaflet to avoid SSR issues
|
||||
const L = await import('leaflet');
|
||||
|
||||
// Load CSS
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
|
||||
document.head.appendChild(link);
|
||||
|
||||
// Add custom popup styles
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.leaflet-tooltip.custom-tooltip {
|
||||
background: #FFFBDF !important;
|
||||
border: 1px solid #D3B180 !important;
|
||||
box-shadow: 0 3px 14px rgba(211, 177, 128, 0.15) !important;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
|
||||
color: #78531D !important;
|
||||
}
|
||||
.leaflet-tooltip.custom-tooltip:before {
|
||||
border-top-color: #D3B180 !important;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Initialize map
|
||||
map = L.map(mapContainer).setView([20, 0], 2);
|
||||
|
||||
// Add tile layer
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
}).addTo(map);
|
||||
|
||||
// Fetch and add event locations
|
||||
try {
|
||||
const response = await fetch('/api/map-data');
|
||||
if (response.ok) {
|
||||
const locations = await response.json();
|
||||
|
||||
// Create custom icon
|
||||
const customIcon = L.icon({
|
||||
iconUrl: '/map-flag.png',
|
||||
iconSize: [32, 41],
|
||||
iconAnchor: [16, 41],
|
||||
popupAnchor: [1, -34]
|
||||
});
|
||||
|
||||
// Add markers for each location
|
||||
locations.forEach((location: any) => {
|
||||
if (location.lat && location.lng) {
|
||||
L.marker([location.lat, location.lng], { icon: customIcon })
|
||||
.bindTooltip(`<strong>${location.event_name}</strong>`, {
|
||||
permanent: false,
|
||||
direction: 'top',
|
||||
className: 'custom-tooltip'
|
||||
})
|
||||
.addTo(map);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load map data:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={mapContainer} class="w-full h-full"></div>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
import { onMount } from "svelte";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||
import MapComponent from "../components/MapComponent.svelte";
|
||||
|
||||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
|
|
@ -883,13 +884,11 @@ Mumbai`.split("\n")
|
|||
<div class="relative w-full max-w-3xl mx-auto min-w-72 max-md:mx-0">
|
||||
<img src="banner.png" alt="100 Cities Worldwide" class="absolute top-0 left-1/2 -translate-x-1/2 max-md:-translate-y-1/2 max-sm:translate-y-[calc(-50%-4rem)] h-48 w-auto z-100 scale-150 saturate-70 brightness-110 object-contain px-4 pointer-events-none">
|
||||
<img src="hole.png" alt="" class="w-full h-full max-w-3xl max-sm:scale-200 pointer-events-none">
|
||||
<iframe
|
||||
src="https://felt.com/embed/map/Daydream-Events-pPFQnT34SOq6tYlb2S1IdC?loc=0%2C-73.3%2C1.7z&legend=0&cooperativeGestures=1&link=0&geolocation=0&zoomControls=1&scaleBar=0"
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full border-0 max-sm:scale-200"
|
||||
style="mask: url('hole.png') no-repeat center; -webkit-mask: url('hole.png') no-repeat center; mask-size: contain; -webkit-mask-size: contain;"
|
||||
title="Felt Map"
|
||||
referrerpolicy="strict-origin-when-cross-origin">
|
||||
</iframe>
|
||||
style="mask: url('hole.png') no-repeat center; -webkit-mask: url('hole.png') no-repeat center; mask-size: contain; -webkit-mask-size: contain;">
|
||||
<MapComponent />
|
||||
</div>
|
||||
<p class="absolute left-1/2 -translate-x-1/2 font-sans text-center text-2xl pt-12 max-sm:pt-40 max-sm:text-xl w-max max-w-[80vh] max-md:max-w-full md:px-12 text-[#60574b] z-10000 ">All daydream events are organized by high school students like yourself! <br> <span class="font-bold"><a class="underline hover:text-pink" href="https://forms.hackclub.com/daydream">Sign up</a> to organize now!</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
64
src/routes/api/map-data/+server.js
Normal file
64
src/routes/api/map-data/+server.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { AIRTABLE_API_KEY, AIRTABLE_BASE_ID, GEOCODER_API_KEY } from '$env/static/private';
|
||||
import { json } from '@sveltejs/kit';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function GET() {
|
||||
if (!AIRTABLE_API_KEY || !AIRTABLE_BASE_ID || !GEOCODER_API_KEY) {
|
||||
return json({ error: 'Missing required environment variables' }, { status: 500 });
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch approved events from Airtable
|
||||
const airtableUrl = `https://api.airtable.com/v0/${AIRTABLE_BASE_ID}/events?filterByFormula={triage_status}="Approved"`;
|
||||
const airtableResponse = await fetch(airtableUrl, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${AIRTABLE_API_KEY}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!airtableResponse.ok) {
|
||||
throw new Error(`Airtable API error: ${airtableResponse.status}`);
|
||||
}
|
||||
|
||||
const airtableData = await airtableResponse.json();
|
||||
const events = airtableData.records;
|
||||
|
||||
// Geocode each event location
|
||||
const locations = [];
|
||||
for (const event of events) {
|
||||
const { city, state, country, event_name } = event.fields;
|
||||
|
||||
if (!city || !event_name) continue;
|
||||
|
||||
// Build address string
|
||||
const addressParts = [city, state, country].filter(Boolean);
|
||||
const address = addressParts.join(', ');
|
||||
|
||||
try {
|
||||
const geocodeUrl = `https://geocoder.hackclub.com/v1/geocode?address=${encodeURIComponent(address)}&key=${GEOCODER_API_KEY}`;
|
||||
const geocodeResponse = await fetch(geocodeUrl);
|
||||
|
||||
if (geocodeResponse.ok) {
|
||||
const geocodeData = await geocodeResponse.json();
|
||||
locations.push({
|
||||
lat: geocodeData.lat,
|
||||
lng: geocodeData.lng,
|
||||
event_name,
|
||||
city,
|
||||
state,
|
||||
country
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to geocode ${address}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return json(locations);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch map data:', error);
|
||||
return json({ error: 'Failed to fetch map data' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
BIN
static/map-flag.png
Normal file
BIN
static/map-flag.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 427 B |
Loading…
Add table
Reference in a new issue