Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to map and images #11

Merged
merged 13 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion canada/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export default defineConfig({
integrations: [mdx(), preact()],
site: 'https://canada.aaronczichon.de',
image: {
domains: ['api.mapbox.com']
domains: ['api.mapbox.com', 'directus.aaronczichon.de']
}
});
23 changes: 20 additions & 3 deletions canada/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions canada/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/mapbox-gl": "^3.1.0",
"prettier": "^3.2.5",
"prettier-plugin-astro": "^0.13.0"
}
Expand Down
43 changes: 43 additions & 0 deletions canada/src/components/DirectusImage.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
import { Image } from "astro:assets";
import { GLOBAL_CONFIG } from "../config";

const { assetId, alt, width, height, quality, leading } = Astro.props;

let imgUrl = `${GLOBAL_CONFIG.imageEndpoint}${GLOBAL_CONFIG.directusAssetEndpoint}${assetId}`;
const altSrc = `${GLOBAL_CONFIG.imageEndpoint}/files/${assetId}?fields[]=description`;
const queries = ["download"];

if (leading === "width" || !leading)
width
? queries.push(`width=${width}`)
: queries.push(`width=${GLOBAL_CONFIG.defaultWidth}`);
if (leading === "height")
height
? queries.push(`height=${height}`)
: queries.push(`height=${GLOBAL_CONFIG.defaultHeight}`);

const q = quality || GLOBAL_CONFIG.defaultQuality;
queries.push(`quality=${q}`);

const imgSrc = `${imgUrl}?${queries.join("&")}`;

let result: any = await fetch(altSrc);
result = await result.json();
const altText = result?.data?.description || "";
---

<a
target="_blank"
href={imgUrl}
title="Klicken um das Bild in Orginalgröße zu betrachten"
>
<Image
src={imgSrc}
alt={altText}
width={GLOBAL_CONFIG.defaultWidth}
height={GLOBAL_CONFIG.defaultHeight}
loading="lazy"
fetchpriority="auto"
/>
</a>
89 changes: 17 additions & 72 deletions canada/src/components/dynamic/PathMap.jsx
Original file line number Diff line number Diff line change
@@ -1,88 +1,33 @@
import { useState, useEffect } from 'preact/hooks';
import * as mapboxgl from 'mapbox-gl';
import gpxParser from 'gpxparser';

import BasicMap from './BasicMap';
import {addTooltipToMap, addRoutesToMap, fetchGpxFile, findCenter } from '../functions/map.functions';

// Function to parse GPX file and extract route coordinates
function parseGPX(gpxData) {
const parser = new gpxParser();
parser.parse(gpxData);
const route = parser.tracks[0].points.map(point => [point.lat, point.lon]);
return route;
}

export default function PathDynamicMap({gpxUrl, zoom, tooltip}) {
export default function PathDynamicMap({gpxInfo, zoom, tooltip}) {
const [map, setMap] = useState(null);
const [popup, setPopup] = useState(null);
const [routeCoordinates, setRouteCoordinates] = useState(null);
const [routeData, setRouteData] = useState(null);

useEffect(() => {
// Fetch and parse GPX file
if (gpxUrl && map) {
fetch(gpxUrl)
.then(response => response.text())
.then(gpxData => {
const route = parseGPX(gpxData);
const switchedRoute = route.map(subArray => {
return [subArray[1], subArray[0]];
});
setRouteCoordinates(switchedRoute);
})
.catch(error => {
console.error('Error fetching GPX file:', error);
});
}
}, [gpxUrl, map]);
if (!map || !gpxInfo || gpxInfo.length === 0) return;

Promise.all(gpxInfo.map((info) => fetchGpxFile(info.url)
.then(route => ({ tooltip: info.tooltip, url: info.url, routeCoordinates: route, color: '#' + Math.floor(Math.random()*16777215).toString(16) }))))
.then((routes) => setRouteData(routes));
}, [gpxInfo, map]);

useEffect(() => {
// Add route to map
if (map && routeCoordinates) {
map.on('load', () => {

map.addLayer({
id: 'route',
type: 'line',
source: {
type: 'geojson',
data: {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: routeCoordinates
}
}
},
layout: {
'line-join': 'round',
'line-cap': 'round'
},
paint: {
'line-color': '#b33335',
'line-width': 7
}
});
});
map.setCenter(routeCoordinates[0]);

// Add popup on hover
if (!tooltip) return;

const popup = new mapboxgl.Popup({ closeOnClick: false })
.setHTML(tooltip);
// Add popup on hover over the route
map.on('mouseenter', 'route', (e) => {
map.getCanvas().style.cursor = 'pointer';
popup.setLngLat(e.lngLat).addTo(map);
});

map.on('mouseleave', 'route', () => {
map.getCanvas().style.cursor = ''; // Reset cursor style
popup.remove(); // Remove popup on mouse leave
if (!map || !routeData || routeData.length === 0) return;
addRoutesToMap(map, routeData);
routeData.forEach((element, index) => {
if (element.tooltip) addTooltipToMap(map, element.tooltip, `route-${index}`);
});
}
}, [map, routeCoordinates, popup, tooltip]);
const allCoordinates = routeData.map((route) => route.routeCoordinates).flat();
const center = findCenter(allCoordinates);
map.setCenter(center);
}, [map, routeData, popup, tooltip]);

return (
<BasicMap zoom={zoom} setMap={setMap} />
Expand Down
6 changes: 6 additions & 0 deletions canada/src/components/dynamic/route.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type RouteData = {
url: string;
routeCoordinates?: number[][];
color?: string;
tooltip?: string;
}
109 changes: 109 additions & 0 deletions canada/src/components/functions/map.functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import gpxParser from 'gpxparser';
import * as mapboxgl from 'mapbox-gl';
import type { RouteData } from '../dynamic/route.type';

/**
* This functions prints multiple routes onto the map
* @param map map element reference
* @param routeData array of RouteData objects
*/
export const addRoutesToMap = (map: any, routeData: RouteData[]) => {
map.on('load', () => {
routeData.forEach((element, index) => {
map.addLayer({
id: `route-${index}`,
type: 'line',
source: {
type: 'geojson',
data: {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: element.routeCoordinates
}
}
},
layout: {
'line-join': 'round',
'line-cap': 'round'
},
paint: {
'line-color': element.color ? element.color : '#b33335',
'line-width': 7
}
});
});
});
}

/**
* Adds a tooltip for an route layer
* @param map map element reference
* @param tooltip the actual tooltip which should be shown
* @param layerName name if the route where the tooltip should be shown
*/
export const addTooltipToMap = (map: any, tooltip: string, layerName: string) => {
const popup = new mapboxgl.Popup({ closeOnClick: false })
.setHTML(tooltip);
// Add popup on hover over the route
map.on('mouseenter', layerName, (e: any) => {
map.getCanvas().style.cursor = 'pointer';
popup.setLngLat(e.lngLat).addTo(map);
});

map.on('mouseleave', layerName, () => {
map.getCanvas().style.cursor = ''; // Reset cursor style
popup.remove(); // Remove popup on mouse leave
});
}

// Function to parse GPX file and extract route coordinates
function parseGPX(gpxData: any) {
const parser = new gpxParser();
parser.parse(gpxData);
const route = parser.tracks[0].points.map(point => [point.lat, point.lon]);
return route;
}

/**
* Loads a GPX file from the provided route URL and returns the route coordinates
* @param url url of the GPX file
* @returns route coordinates
*/
export const fetchGpxFile = async (url: string) => {
return fetch(url)
.then(response => response.text())
.then(gpxData => {
const route = parseGPX(gpxData);
const switchedRoute = route.map(subArray => {
return [subArray[1], subArray[0]];
});
return switchedRoute;
})
.catch(error => {
console.error('Error fetching GPX file:', error);
throw error;
});
}

const getMiddle = (prop: 'lat' | 'lng', coordinates: number[]) => {
let min = Math.min(...coordinates);
let max = Math.max(...coordinates);
if (prop === 'lng' && (max - min > 180)) {
coordinates = coordinates.map(val => val < max - 180 ? val + 360 : val);
min = Math.min(...coordinates);
max = Math.max(...coordinates);
}
let result = (min + max) / 2;
if (prop === 'lng' && result > 180) {
result -= 360
}
return result;
}

export const findCenter = (coordinates: number[][]) => {
const lat = coordinates.map(coord => coord[0]);
const lng = coordinates.map(coord => coord[1]);
return [getMiddle('lat', lat), getMiddle('lng', lng)];
}
7 changes: 7 additions & 0 deletions canada/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const GLOBAL_CONFIG = {
imageEndpoint: 'https://directus.aaronczichon.de',
directusAssetEndpoint: '/assets/',
defaultWidth: 720,
defaultHeight: 450,
defaultQuality: 80,
}
27 changes: 5 additions & 22 deletions canada/src/content/blog/01-welcome-to-canada.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,30 @@ tags:
]
---

import ContentImage from "../../components/ContentImage.astro";

import IMG_E3412 from "./images/01/IMG_E3412.jpg";
import IMG_E3427 from "./images/01/IMG_E3427.jpg";
import IMG_E8691 from "./images/01/IMG_E8691.jpeg";
import IMG_E3453 from "./images/01/IMG_E3453.jpeg";
import DirectusImage from "../../components/DirectusImage.astro";

Es ist nun schon eine Woche her, dass wir in Halifax angekommen sind. Nach den üblichen Ankunftsthemen wie Wohnung beziehen, Jetlag bewältigen und 1-2 grundlegende Einkäufe an Lebensmittel sind wir nun gut angekommen.

<ContentImage
src={IMG_E3412}
alt="Blick aus einem Flugzeugfenster über den Flügel bei Regen"
/>{" "}
<DirectusImage assetId="d51c7e25-09c5-48f6-8245-f15e2d1a4766" />

> Ankunft in Halifax bei Regen und kalten -10 Grad.

Das Wetter spielt ihr an der Küste etwas verrückt und erinnert eher an einen "klassischen" April aus alten Tagen. Mal gibt es strahlenden Sonnenschein, viel Wind und Wolken oder jede Menge Regen. Oder alles zusammen.
Auch die Temperaturen schwanken aktuell stark zwischen -10 Grad bei unserer Ankunft bis zu teilweise +11 Grad.

<ContentImage
src={IMG_E3427}
alt="Sonnenuntergang von der Halifax Transit Ferry"
/>
<DirectusImage assetId="b482ede3-6881-44ec-b921-3c385475e731.jpeg" />

Unsere Wohnung bzw. unser Apartment liegt im süd-östlichen Teil von Halifax in der South Street. Das ist nicht direkt Downtown aber es ist alles gut fußläufig erreichbar. Bis zum Hafen sind es etwa 600m, bis Downtown und Citadel ca. 1,5km und bis zum Point Pleasant Park gute 2km.
Alles was man zum Leben braucht (in erster Linie Lebensmittel) sind ebenfalls in etwa 5 Minuten zu Fuß erreichbar.
Da wir aktuell noch kein Auto haben, erleichtert uns das vieles.

<ContentImage
src={IMG_E8691}
alt="Häuserzug in Halifax mit unterschiedlichen Farben aus gelb, militärgrün, hellblau"
/>
<DirectusImage assetId="7c6808cc-06f1-4f11-8754-2cde9f69b419" />

Bisher ist es noch so, dass man jeden Tag etwas Neues entdecken (oder etwas schon bekanntes von letztem Jahr wieder entdecken) möchte. Daher wurde das erste Wochenende und die Nachmittage nach der Arbeit intensiv genutzt.
Von Spaziergängen am Hafen und Point Pleasant Park, sowie Fährfahrten nach Dartmouth und ein Open Mic Besuch im `The Loose Cannon` war alles dabei.
Heute geht es dann, ganz klassisch, zu einem Eishockeyspiel der Halifax Mooseheads.

<ContentImage
src={IMG_E3453}
alt="Blick aus dem 5. Stock nach unten in der Halifax Public Library"
/>
<DirectusImage assetId="d7ac175d-77a3-4528-9f01-5c64d2318195" />

Da wir auch direkt wieder "normal" weiterarbeiten, gilt es natürlich auch hier in einen entsprechenden Modus zu wechseln. Man versucht noch das richtige Arbeitsumfeld zu finden und wechselt auch ganz gerne Mal den Arbeitsort (zwischen kleinem Schreibtisch, Esstisch oder der Halifax Library).

Expand Down
Loading