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

View File

@ -4,10 +4,62 @@
import { feature } from 'topojson-client'; import { feature } from 'topojson-client';
import { Colors } from '../constants/Colors'; import { Colors } from '../constants/Colors';
import '../../app.css'; import '../../app.css';
import { ref, get } from 'firebase/database';
import { db } from '../../firebase';
let mapContainer: HTMLDivElement; 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(() => { onMount(() => {
let mounted = true;
async function initMap() {
if (!mounted) return;
const width = mapContainer.clientWidth; const width = mapContainer.clientWidth;
const height = mapContainer.clientHeight; const height = mapContainer.clientHeight;
@ -16,8 +68,8 @@
.append('svg') .append('svg')
.attr('width', '100%') .attr('width', '100%')
.attr('height', '100%') .attr('height', '100%')
.attr('viewBox', `0 0 ${width} ${height}`) // make a coordinate from (0, 0) to (width, height) .attr('viewBox', `0 0 ${width} ${height}`)
.attr('preserveAspectRatio', 'xMidYMid meet') as d3.Selection<SVGSVGElement, unknown, null, undefined>; // center the map .attr('preserveAspectRatio', 'xMidYMid meet') as d3.Selection<SVGSVGElement, unknown, null, undefined>;
// Add a group for all map elements that will be transformed // Add a group for all map elements that will be transformed
const g = svg.append('g'); const g = svg.append('g');
@ -29,13 +81,14 @@
const path = d3.geoPath().projection(projection); const path = d3.geoPath().projection(projection);
// Tokyo coordinates [longitude, latitude]
const tokyo: [number, number] = [139.6917, 35.6895];
const initMap = async () => {
try { try {
// Get past trip locations
const pastLocations = await getPastTripLocations();
if (!mounted) return;
// Load world map data // Load world map data
const response = await fetch('https://unpkg.com/world-atlas@2/countries-110m.json'); const response = await fetch('https://unpkg.com/world-atlas@2/countries-110m.json');
if (!mounted) return;
const world = await response.json(); const world = await response.json();
// Convert TopoJSON to GeoJSON // Convert TopoJSON to GeoJSON
@ -53,13 +106,16 @@
.attr('stroke', Colors.gray.light50) .attr('stroke', Colors.gray.light50)
.attr('stroke-width', '0.5'); .attr('stroke-width', '0.5');
// Add Tokyo marker // Add markers for past trip locations
g.append('circle') g.selectAll('circle')
.attr('cx', projection(tokyo)![0]) .data(pastLocations)
.attr('cy', projection(tokyo)![1]) .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('r', 5)
.attr('class', 'marker') .attr('class', 'marker')
.attr('fill', Colors.planner.med400); .attr('fill', Colors.planner.med400)
// Add zoom behavior // Add zoom behavior
const zoom = d3.zoom<SVGSVGElement, unknown>() const zoom = d3.zoom<SVGSVGElement, unknown>()
@ -70,16 +126,20 @@
svg.call(zoom) svg.call(zoom)
.call(zoom.transform, d3.zoomIdentity); .call(zoom.transform, d3.zoomIdentity);
} catch (error) { } catch (error) {
console.error('Error loading map:', error); console.error('Error initializing map:', error);
}
} }
};
initMap(); initMap();
return () => { cleanup = () => {
mounted = false;
d3.select(mapContainer).selectAll('*').remove(); d3.select(mapContainer).selectAll('*').remove();
}; };
return cleanup;
}); });
</script> </script>

View File

@ -259,7 +259,7 @@
tripDates = dates; tripDates = dates;
// initialize expanded states for 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 // initialize placesToVisit from database or empty array
placesToVisit = tripData.placesToVisit || []; placesToVisit = tripData.placesToVisit || [];