mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-25 07:49:37 +02:00
feat: Implement chronological itinerary path visualization with GeoJSON for adventures, transportation, and lodging
This commit is contained in:
parent
1dc8e10758
commit
876c5e83b4
1 changed files with 167 additions and 28 deletions
|
@ -136,6 +136,66 @@
|
|||
|
||||
let adventures: Adventure[] = [];
|
||||
|
||||
// Add this after your existing MapLibre markers
|
||||
|
||||
// Add this after your existing MapLibre markers
|
||||
|
||||
// Create line data from orderedItems
|
||||
$: lineData = createLineData(orderedItems);
|
||||
|
||||
// Function to create GeoJSON line data from ordered items
|
||||
function createLineData(
|
||||
items: Array<{
|
||||
item: Adventure | Transportation | Lodging | Note | Checklist;
|
||||
start: string;
|
||||
end: string;
|
||||
}>
|
||||
) {
|
||||
if (items.length < 2) return null;
|
||||
|
||||
const coordinates: [number, number][] = [];
|
||||
|
||||
// Extract coordinates from each item
|
||||
for (const orderItem of items) {
|
||||
const item = orderItem.item;
|
||||
|
||||
if (
|
||||
'origin_longitude' in item &&
|
||||
'origin_latitude' in item &&
|
||||
'destination_longitude' in item &&
|
||||
'destination_latitude' in item &&
|
||||
item.origin_longitude &&
|
||||
item.origin_latitude &&
|
||||
item.destination_longitude &&
|
||||
item.destination_latitude
|
||||
) {
|
||||
// For Transportation, add both origin and destination points
|
||||
coordinates.push([item.origin_longitude, item.origin_latitude]);
|
||||
coordinates.push([item.destination_longitude, item.destination_latitude]);
|
||||
} else if ('longitude' in item && 'latitude' in item && item.longitude && item.latitude) {
|
||||
// Handle Adventure and Lodging types
|
||||
coordinates.push([item.longitude, item.latitude]);
|
||||
}
|
||||
}
|
||||
|
||||
// Only create line data if we have at least 2 coordinates
|
||||
if (coordinates.length >= 2) {
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
name: 'Itinerary Path',
|
||||
description: 'Path connecting chronological items'
|
||||
},
|
||||
geometry: {
|
||||
type: 'LineString' as const,
|
||||
coordinates: coordinates
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
let numVisited: number = 0;
|
||||
let numAdventures: number = 0;
|
||||
|
||||
|
@ -169,6 +229,63 @@
|
|||
}
|
||||
}
|
||||
|
||||
let orderedItems: Array<{
|
||||
item: Adventure | Transportation | Lodging;
|
||||
type: 'adventure' | 'transportation' | 'lodging';
|
||||
start: string; // ISO date string
|
||||
end: string; // ISO date string
|
||||
}> = [];
|
||||
|
||||
$: {
|
||||
// Reset ordered items
|
||||
orderedItems = [];
|
||||
|
||||
// Add Adventures (using visit dates)
|
||||
adventures.forEach((adventure) => {
|
||||
adventure.visits.forEach((visit) => {
|
||||
orderedItems.push({
|
||||
item: adventure,
|
||||
start: visit.start_date,
|
||||
end: visit.end_date,
|
||||
type: 'adventure'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Add Transportation
|
||||
transportations.forEach((transport) => {
|
||||
if (transport.date) {
|
||||
// Only add if date exists
|
||||
orderedItems.push({
|
||||
item: transport,
|
||||
start: transport.date,
|
||||
end: transport.end_date || transport.date, // Use end_date if available, otherwise use date,
|
||||
type: 'transportation'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add Lodging
|
||||
lodging.forEach((lodging) => {
|
||||
if (lodging.check_in) {
|
||||
// Only add if check_in exists
|
||||
orderedItems.push({
|
||||
item: lodging,
|
||||
start: lodging.check_in,
|
||||
end: lodging.check_out || lodging.check_in, // Use check_out if available, otherwise use check_in,
|
||||
type: 'lodging'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Sort all items chronologically by start date
|
||||
orderedItems.sort((a, b) => {
|
||||
const dateA = new Date(a.start).getTime();
|
||||
const dateB = new Date(b.start).getTime();
|
||||
return dateA - dateB;
|
||||
});
|
||||
}
|
||||
|
||||
$: {
|
||||
numAdventures = adventures.length;
|
||||
numVisited = adventures.filter((adventure) => adventure.is_visited).length;
|
||||
|
@ -994,6 +1111,19 @@
|
|||
</Marker>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if lineData}
|
||||
<GeoJSON data={lineData}>
|
||||
<LineLayer
|
||||
layout={{ 'line-cap': 'round', 'line-join': 'round' }}
|
||||
paint={{
|
||||
'line-width': 4,
|
||||
'line-color': '#0088CC', // Blue line to distinguish from transportation lines
|
||||
'line-opacity': 0.8,
|
||||
'line-dasharray': [2, 1] // Dashed line to differentiate from direct transportation lines
|
||||
}}
|
||||
/>
|
||||
</GeoJSON>
|
||||
{/if}
|
||||
{#each transportations as transportation}
|
||||
{#if transportation.origin_latitude && transportation.origin_longitude && transportation.destination_latitude && transportation.destination_longitude}
|
||||
<!-- Origin Marker -->
|
||||
|
@ -1035,34 +1165,6 @@
|
|||
</p>
|
||||
</Popup>
|
||||
</Marker>
|
||||
|
||||
<!-- Line connecting origin and destination -->
|
||||
<GeoJSON
|
||||
data={{
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
name: transportation.name,
|
||||
type: transportation.type
|
||||
},
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: [
|
||||
[transportation.origin_longitude, transportation.origin_latitude],
|
||||
[transportation.destination_longitude, transportation.destination_latitude]
|
||||
]
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LineLayer
|
||||
layout={{ 'line-cap': 'round', 'line-join': 'round' }}
|
||||
paint={{
|
||||
'line-width': 3,
|
||||
'line-color': '#898989', // customize your line color here
|
||||
'line-opacity': 0.8
|
||||
// 'line-dasharray': [5, 2]
|
||||
}}
|
||||
/>
|
||||
</GeoJSON>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
|
@ -1286,6 +1388,43 @@
|
|||
{/if}
|
||||
{/if}
|
||||
|
||||
{#each orderedItems as orderedItem}
|
||||
<p>{orderedItem.type}</p>
|
||||
{#if orderedItem.type === 'adventure'}
|
||||
{#if orderedItem.item && 'images' in orderedItem.item}
|
||||
<AdventureCard
|
||||
user={data.user}
|
||||
on:edit={editAdventure}
|
||||
on:delete={deleteAdventure}
|
||||
adventure={orderedItem.item}
|
||||
{collection}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if orderedItem.type === 'transportation' && orderedItem.item && 'origin_latitude' in orderedItem.item}
|
||||
<TransportationCard
|
||||
transportation={orderedItem.item}
|
||||
user={data?.user}
|
||||
on:delete={(event) => {
|
||||
transportations = transportations.filter((t) => t.id != event.detail);
|
||||
}}
|
||||
on:edit={editTransportation}
|
||||
{collection}
|
||||
/>
|
||||
{/if}
|
||||
{#if orderedItem.type === 'lodging' && orderedItem.item && 'reservation_number' in orderedItem.item}
|
||||
<LodgingCard
|
||||
lodging={orderedItem.item}
|
||||
user={data?.user}
|
||||
on:delete={(event) => {
|
||||
lodging = lodging.filter((t) => t.id != event.detail);
|
||||
}}
|
||||
on:edit={editLodging}
|
||||
{collection}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<svelte:head>
|
||||
<title
|
||||
>{data.props.adventure && data.props.adventure.name
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue