Skip to content

Commit

Permalink
Merge pull request #11 from aaronczichon/feat/improvements
Browse files Browse the repository at this point in the history
Improvements to map and images
  • Loading branch information
aaronczichon authored Apr 4, 2024
2 parents 4f37448 + 3e6b68f commit fa63725
Show file tree
Hide file tree
Showing 54 changed files with 331 additions and 261 deletions.
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

0 comments on commit fa63725

Please sign in to comment.