show marker in WorldMap

This commit is contained in:
adeliptr 2025-06-07 00:54:12 +09:00
parent 422c39e3e1
commit c7c4153220
3 changed files with 92 additions and 32 deletions

View File

@ -83,9 +83,9 @@
{#if fullPlace.desc}
<p class="place-desc">{fullPlace.desc}</p>
{/if}
<div class="plan-time">
<!-- <div class="plan-time">
<button class="edit-time">{fullPlace.time}</button>
</div>
</div> -->
</div>
</div>
{/if}
@ -236,7 +236,7 @@
font-size: 0.9rem;
}
.edit-time {
/* .edit-time {
background-color: var(--gray-200);
border: none;
padding: 0.5rem 1rem;
@ -249,5 +249,5 @@
.edit-time:hover {
opacity: 0.75;
}
} */
</style>

View File

@ -4,38 +4,91 @@
import { feature } from 'topojson-client';
import { Colors } from '../constants/Colors';
import '../../app.css';
import { ref, get } from 'firebase/database';
import { db } from '../../firebase';
let mapContainer: HTMLDivElement;
interface TripLocation {
name: string;
location: {
lat: number;
lng: number;
};
}
async function getPastTripLocations(): Promise<TripLocation[]> {
try {
const tripsRef = ref(db, 'trips');
const snapshot = await get(tripsRef);
if (!snapshot.exists()) return [];
// Get today's date at midnight for comparison
const today = new Date();
today.setHours(0, 0, 0, 0);
// Create a Set to store unique locations
const uniqueLocations = new Map<string, TripLocation>();
// Filter past trips and extract unique destinations
Object.values(snapshot.val()).forEach((trip: any) => {
const endDate = new Date(trip.endDate);
if (endDate < today && trip.destination?.location) {
const locationKey = `${trip.destination.location.lat},${trip.destination.location.lng}`;
if (!uniqueLocations.has(locationKey)) {
uniqueLocations.set(locationKey, {
name: trip.destination.name,
location: trip.destination.location
});
}
}
});
return Array.from(uniqueLocations.values());
} catch (error) {
console.error('Error fetching past trips:', error);
return [];
}
}
let cleanup: (() => void) | undefined;
onMount(() => {
const width = mapContainer.clientWidth;
const height = mapContainer.clientHeight;
let mounted = true;
// Create SVG
const svg = d3.select(mapContainer)
.append('svg')
.attr('width', '100%')
.attr('height', '100%')
.attr('viewBox', `0 0 ${width} ${height}`) // make a coordinate from (0, 0) to (width, height)
.attr('preserveAspectRatio', 'xMidYMid meet') as d3.Selection<SVGSVGElement, unknown, null, undefined>; // center the map
async function initMap() {
if (!mounted) return;
// Add a group for all map elements that will be transformed
const g = svg.append('g');
const width = mapContainer.clientWidth;
const height = mapContainer.clientHeight;
// Create projection
const projection = d3.geoMercator()
.scale(width / (2 * Math.PI))
.translate([width / 2, height / 1.6]); // position the map, horizontally centered but is slighty upward
// Create SVG
const svg = d3.select(mapContainer)
.append('svg')
.attr('width', '100%')
.attr('height', '100%')
.attr('viewBox', `0 0 ${width} ${height}`)
.attr('preserveAspectRatio', 'xMidYMid meet') as d3.Selection<SVGSVGElement, unknown, null, undefined>;
const path = d3.geoPath().projection(projection);
// Add a group for all map elements that will be transformed
const g = svg.append('g');
// Tokyo coordinates [longitude, latitude]
const tokyo: [number, number] = [139.6917, 35.6895];
// Create projection
const projection = d3.geoMercator()
.scale(width / (2 * Math.PI))
.translate([width / 2, height / 1.6]); // position the map, horizontally centered but is slighty upward
const path = d3.geoPath().projection(projection);
const initMap = async () => {
try {
// Get past trip locations
const pastLocations = await getPastTripLocations();
if (!mounted) return;
// Load world map data
const response = await fetch('https://unpkg.com/world-atlas@2/countries-110m.json');
if (!mounted) return;
const world = await response.json();
// Convert TopoJSON to GeoJSON
@ -53,13 +106,16 @@
.attr('stroke', Colors.gray.light50)
.attr('stroke-width', '0.5');
// Add Tokyo marker
g.append('circle')
.attr('cx', projection(tokyo)![0])
.attr('cy', projection(tokyo)![1])
// Add markers for past trip locations
g.selectAll('circle')
.data(pastLocations)
.enter()
.append('circle')
.attr('cx', d => projection([d.location.lng, d.location.lat])![0])
.attr('cy', d => projection([d.location.lng, d.location.lat])![1])
.attr('r', 5)
.attr('class', 'marker')
.attr('fill', Colors.planner.med400);
.attr('fill', Colors.planner.med400)
// Add zoom behavior
const zoom = d3.zoom<SVGSVGElement, unknown>()
@ -70,16 +126,20 @@
svg.call(zoom)
.call(zoom.transform, d3.zoomIdentity);
} catch (error) {
console.error('Error loading map:', error);
console.error('Error initializing map:', error);
}
};
}
initMap();
return () => {
cleanup = () => {
mounted = false;
d3.select(mapContainer).selectAll('*').remove();
};
return cleanup;
});
</script>

View File

@ -259,7 +259,7 @@
tripDates = dates;
// initialize expanded states for dates
expandedDates = Object.fromEntries(dates.map(date => [date, false]));
expandedDates = Object.fromEntries(dates.map(date => [date, true]));
// initialize placesToVisit from database or empty array
placesToVisit = tripData.placesToVisit || [];