@@ -152,11 +152,15 @@ | |||
title="Aller à la page de recherche" | |||
rel="search" data-no-instant>Recherche</a> | |||
• <a rel="next" | |||
href="/david/2024/03/26/" | |||
title="Publication suivante : GPX Viewer">Suivant →</a> | |||
</p> | |||
</nav> | |||
<p>Dans le cadre de <del><a data-link-domain="en.wikipedia.org" href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">manger ma propre bouffe pour chien</a></del> mon <a data-link-domain="fr.wikipedia.org" href="https://fr.wikipedia.org/wiki/Auto%C3%A9quipement">autoéquipement</a>, je voulais vérifier si l’implémentation de <a data-link-domain="oembed.com" href="https://oembed.com/">oEmbed</a> dans <a data-link-domain="umap-project.org" href="https://umap-project.org/fr/">uMap</a> était utilisable… et il se trouve que ce n’était pas le cas avant aujourd’hui. Il y aurait encore des pistes d’améliorations mais au moins ça affiche une carte (si vous autorisez les requêtes externes en JS) :</p> | |||
<style type="text/css" nonce="oembed-default-style"> | |||
<style type="text/css"> | |||
o-embed { | |||
/* Size of the oembed + paragraph + margins. */ | |||
height: calc(300px + 1rem + 3rem); | |||
@@ -170,7 +174,7 @@ | |||
</p> | |||
</o-embed> | |||
<script type="module" nonce="oembed-web-component"> | |||
<script type="module"> | |||
class OEmbed extends HTMLElement { | |||
static tagName = 'o-embed' | |||
@@ -317,6 +321,10 @@ | |||
<a href="/david/2024/" title="Liste des publications récentes">↑ En 2024</a> | |||
• <a rel="next" | |||
href="/david/2024/03/26/" | |||
title="Publication suivante : GPX Viewer">Suivant →</a> | |||
</p> | |||
</nav> | |||
@@ -0,0 +1,534 @@ | |||
<!DOCTYPE html><!-- This is a valid HTML5 document. --> | |||
<!-- Screen readers, SEO, extensions and so on. --> | |||
<html lang="fr"> | |||
<!-- Has to be within the first 1024 bytes, hence before the `title` element | |||
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset --> | |||
<meta charset="utf-8"> | |||
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 --> | |||
<!-- The viewport meta is quite crowded and we are responsible for that. | |||
See: https://codepen.io/tigt/post/meta-viewport-for-2015 --> | |||
<meta name="viewport" content="width=device-width,initial-scale=1"> | |||
<!-- Required to make a valid HTML5 document. --> | |||
<title> | |||
GPX Viewer | |||
— David Larlet</title> | |||
<script> | |||
function toggleTheme(themeName) { | |||
document.documentElement.classList.toggle( | |||
'forced-dark', | |||
themeName === 'dark' | |||
) | |||
document.documentElement.classList.toggle( | |||
'forced-light', | |||
themeName === 'light' | |||
) | |||
} | |||
const selectedTheme = localStorage.getItem('theme') | |||
if (selectedTheme !== 'undefined') { | |||
toggleTheme(selectedTheme) | |||
} | |||
</script> | |||
<!-- Documented, feel free to shoot an email. --> | |||
<link rel="stylesheet" href="/static/david/css/style_2024-03-09.css"> | |||
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. --> | |||
<link rel="preload" | |||
href="/static/david/css/fonts/century_supra_ot_a_regular.woff2" | |||
as="font" | |||
type="font/woff2" | |||
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" | |||
crossorigin> | |||
<link rel="preload" | |||
href="/static/david/css/fonts/century_supra_ot_a_bold.woff2" | |||
as="font" | |||
type="font/woff2" | |||
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" | |||
crossorigin> | |||
<link rel="preload" | |||
href="/static/david/css/fonts/century_supra_ot_a_italic.woff2" | |||
as="font" | |||
type="font/woff2" | |||
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" | |||
crossorigin> | |||
<link rel="preload" | |||
href="/static/david/css/fonts/century_supra_ot_b_regular.woff2" | |||
as="font" | |||
type="font/woff2" | |||
media="(prefers-color-scheme: dark)" | |||
crossorigin> | |||
<link rel="preload" | |||
href="/static/david/css/fonts/century_supra_ot_b_bold.woff2" | |||
as="font" | |||
type="font/woff2" | |||
media="(prefers-color-scheme: dark)" | |||
crossorigin> | |||
<link rel="preload" | |||
href="/static/david/css/fonts/century_supra_ot_b_italic.woff2" | |||
as="font" | |||
type="font/woff2" | |||
media="(prefers-color-scheme: dark)" | |||
crossorigin> | |||
<meta name="description" content="Suite de mes expérimentations de la veille à la fois en cartographie et en web components. Je me suis demandé s’il était possible de faire un composant qui ne dépende pas de uMap pour afficher une trace GPX. Le fait d’avoir passé une heure à trouver la bonne CSP pour arriver à afficher une carte m’a d’une certaine manière motivé (c’était une journée galère)."> | |||
<!-- That good ol' feed, subscribe :). --> | |||
<link rel="alternate" | |||
type="application/atom+xml" | |||
title="Feed" | |||
href="/david/log/"> | |||
<!-- Generated from https://realfavicongenerator.net/ such a mess. --> | |||
<link rel="apple-touch-icon" | |||
sizes="180x180" | |||
href="/static/david/icons2/apple-touch-icon.png"> | |||
<link rel="icon" | |||
type="image/png" | |||
sizes="32x32" | |||
href="/static/david/icons2/favicon-32x32.png"> | |||
<link rel="icon" | |||
type="image/png" | |||
sizes="16x16" | |||
href="/static/david/icons2/favicon-16x16.png"> | |||
<link rel="manifest" href="/static/david/icons2/site.webmanifest"> | |||
<link rel="mask-icon" | |||
href="/static/david/icons2/safari-pinned-tab.svg" | |||
color="#07486c"> | |||
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico"> | |||
<meta name="msapplication-TileColor" content="#f7f7f7"> | |||
<meta name="msapplication-config" | |||
content="/static/david/icons2/browserconfig.xml"> | |||
<meta name="theme-color" | |||
content="#f7f7f7" | |||
media="(prefers-color-scheme: light)"> | |||
<meta name="theme-color" | |||
content="#272727" | |||
media="(prefers-color-scheme: dark)"> | |||
<!-- Is that even respected? Retrospectively? What a shAItshow… | |||
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ --> | |||
<meta name="robots" content="noai, noimageai"> | |||
<style type="text/css"> | |||
.tippy-content { | |||
min-width: 280px; | |||
padding: .5rem; | |||
font-size: calc(var(--fluid-0) * 0.8); | |||
font-family: var(--labor-font); | |||
letter-spacing: initial; | |||
text-align: left; | |||
} | |||
.tippy-content h3 { | |||
margin-top: 0; | |||
} | |||
.tippy-content h3 img { | |||
max-width: 2rem; | |||
max-height: 2rem; | |||
display: inline-block; | |||
} | |||
.tippy-content .tippy-links { | |||
display: flex; | |||
justify-content: space-around; | |||
} | |||
.tippy-content a { | |||
padding: .4rem; | |||
color: #F06048; | |||
} | |||
</style> | |||
<body data-instant-intensity="viewport-all"> | |||
<article> | |||
<header> | |||
<hgroup> | |||
<h1>GPX Viewer</h1> | |||
<p>Le <time datetime="2024-03-26">26 mars 2024</time></p> | |||
</hgroup> | |||
</header> | |||
<nav> | |||
<p> | |||
<a rel="prev" | |||
href="/david/2024/03/25/" | |||
title="Publication précédente : Inclusion">← Précédent</a> • | |||
<a href="/david/" title="Aller à l’accueil" rel="up">Accueil</a> | |||
• | |||
<a href="/david/recherche/" | |||
title="Aller à la page de recherche" | |||
rel="search" data-no-instant>Recherche</a> | |||
</p> | |||
</nav> | |||
<p><a href="/david/2024/03/25/" title="Inclusion">Suite de mes expérimentations</a> de la veille à la fois en cartographie et en <em>web components</em>. Je me suis demandé s’il était possible de faire un composant qui ne dépende pas de uMap pour afficher une trace GPX. Le fait d’avoir passé une heure à trouver la bonne <a data-link-domain="MDN" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">CSP</a> pour arriver à afficher une carte m’a d’une certaine manière motivé (c’était une journée galère).</p> | |||
<p>Il se trouve qu’il existe <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx">leaflet-gpx</a> qui fait exactement ce que je voulais. Il ne me restait « plus qu’à » en faire un composant web, ce que j’avais déjà <a data-link-domain="gitlab.com" href="https://gitlab.com/umap-project/leaflet-webcomponents/">fait par ailleurs</a> :</p> | |||
<gpx-viewer data-height="500px" data-width="800px"> | |||
<p> | |||
Vous devriez voir s’afficher une carte de mon « Grand Tour de la forêt de Ouareau », | |||
qui contient <a data-gpx href="/static/david/2024/grand_tour_de_la_foret_de_ouareau.gpx">cette trace GPX</a>, | |||
centrée sur <span data-latitude>46.2117</span>, <span data-longitude>-73.9335</span> | |||
avec un zoom de <span data-zoom>12</span>. | |||
Il est probable que cela ne s’exécute pas dans un agrégateur par exemple. | |||
</p> | |||
</gpx-viewer> | |||
<script type="module"> | |||
import * as L from '/static/david/2024/leaflet.1.9.4/leaflet.1.9.4.js' | |||
window.L = L // Hack for leaflet-gpx, youpi. | |||
</script> | |||
<script type="module" nonce="oembed-web-component"> | |||
class GPXViewer extends HTMLElement { | |||
static tagName = 'gpx-viewer' | |||
static register(tagName, registry) { | |||
if(!registry && ('customElements' in globalThis)) { | |||
registry = globalThis.customElements | |||
} | |||
registry?.define(tagName || this.tagName, this) | |||
} | |||
#attachCSS(path) { | |||
const linkElement = document.createElement('link') | |||
linkElement.setAttribute('rel', 'stylesheet') | |||
linkElement.setAttribute('href', path) | |||
this.shadowRoot.appendChild(linkElement) | |||
} | |||
#createMapContainer() { | |||
const mapContainer = document.createElement('div') | |||
mapContainer.style.height = this.dataset.height | |||
mapContainer.style.width = this.dataset.width | |||
this.shadowRoot.appendChild(mapContainer) | |||
return mapContainer | |||
} | |||
#createMap(mapContainer) { | |||
const map = L.map(mapContainer).setView( | |||
[ | |||
this.querySelector('[data-latitude]').textContent, | |||
this.querySelector('[data-longitude]').textContent, | |||
], | |||
this.querySelector('[data-zoom]').textContent | |||
) | |||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { | |||
attribution: | |||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', | |||
}).addTo(map) | |||
return map | |||
} | |||
#attachGPX(map) { | |||
const gpxUrl = this.querySelector('[data-gpx]').href | |||
new GPX(gpxUrl, { | |||
async: true, | |||
marker_options: { | |||
startIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-start.png', | |||
endIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-end.png', | |||
shadowUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-shadow.png' | |||
} | |||
}).on('loaded', (e) => { | |||
map.fitBounds(e.target.getBounds()) | |||
}).addTo(map) | |||
} | |||
constructor() { | |||
super() | |||
this.attachShadow({ mode: 'open' }) | |||
} | |||
async connectedCallback() { | |||
this.#attachCSS('/static/david/2024/leaflet.1.9.4/leaflet.css') | |||
const mapContainer = this.#createMapContainer() | |||
const map = this.#createMap(mapContainer) | |||
this.#attachGPX(map) | |||
} | |||
} | |||
import GPX from '/static/david/2024/leaflet-gpx.1.7.0-custom/gpx.1.7.0-custom.js' | |||
GPXViewer.register() | |||
</script> | |||
<p>Alors en fait, comme rien n’est jamais simple en JS, il a fallu que j’adapte le plugin qui n’était pas compatible avec les modules JS et que je fasse des galipettes pour que ça finisse par tomber en marche 🤸. Cet écosystème est merveilleux (quel métier !). J’ai quand même <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx/issues/153">remonté le problème</a>.</p> | |||
<p>Au passage, inspiré par <a data-link-domain="blog.k-nut.eu" href="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent" hreflang="en" | |||
title="Consultation de l’article (anglais)">le travail de Knut Hühne</a> | |||
<a href="/david/cache/2024/65fba9cd025cd2403f932cb2c928cf14/" hreflang="en" | |||
data-tippy data-description="An example of using schema.org microdata to build a HTML Webcomponent for Leaflet" | |||
data-source="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent" | |||
data-date="2024-03-25" | |||
data-favicon="https://blog.k-nut.eu/favicon-32x32.png" | |||
data-domain="blog.k-nut.eu" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a> sur la sémantique des données d’une carte, je me suis demandé si j’allais définir un <a data-link-domain="schema.org" href="https://schema.org/itinerary">itinerary</a> pour un <a data-link-domain="schema.org" href="https://schema.org/Trip">Trip</a> avec des <a data-link-domain="schema.org" href="https://schema.org/GeoCoordinates">GeoCoordinates</a> mais ça m’a fait tirer un fil beaucoup trop long. Je garde l’idée sous le coude <a data-link-domain="umap-project.gitlab.io" href="https://umap-project.gitlab.io/leaflet-webcomponents/">pour mes propres recherches</a> dans le domaine cela dit.</p> | |||
<p>Un composant de plus, je ne suis pas très satisfait de celui-ci car j’avais l’espoir d’arriver à quelque chose de facilement réutilisable. J’aurais au moins commencé à mettre les doigts dans le XML du format GPX. Il est possible de définir plusieurs segments au sein d’une même trace. Il peut y avoir pas mal de métadonnées, notamment de mise en forme de la trace mais aussi de caractéristiques (biologiques, physiques, etc).</p> | |||
<a href="#hr-138" title="Lien vers cette section de la page"><hr id="hr-138" /></a> | |||
<blockquote lang="en"> | |||
<p>The fact is that we can’t rely on any single website to hold the whole world’s knowledge, because it can be corrupted sooner or later. The only solution is a distributed architecture, with many smaller websites connecting with each other and sharing information. This is where ActivityPub comes in, the protocol used by Mastodon, Lemmy, Peertube and many other federated social media projects.</p> | |||
<p>I have worked on Lemmy for the past four years, bringing it from a prototype to a fully functional Reddit alternative. I wrote the entire federation code and became very familiar with the protocol. <mark>I realized that the same technology easily be used to create a federated encyclopedia.</mark> As no one else took up such a project, I finally decided to do it on my own and create Ibis. Thanks to my previous experience with the tech stack and the ActivityPub library I created, I was able to complete a proof of concept in a relatively short time of four months.</p> | |||
<p><cite><em><a data-link-domain="ibis.wiki" href="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki" hreflang="en" | |||
title="Consultation de l’article (anglais)">Announcing Ibis, the federated Wikipedia Alternative</a> | |||
<a href="/david/cache/2024/691120ebac09f68413501b7f5daa2db7/" hreflang="en" | |||
data-tippy data-description="" | |||
data-source="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki" | |||
data-date="2024-03-26" | |||
data-favicon="" | |||
data-domain="ibis.wiki" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a></em></cite></p> | |||
</blockquote> | |||
<p>Wikipedia décentralisé, je dis oui ! Merci <a data-link-domain="alexsirac.com" href="https://alexsirac.com/activitypub-powered-wikis/" hreflang="fr" | |||
title="Consultation de l’article">Alex</a> | |||
<a href="/david/cache/2024/55c19feff784a41d2527b5f1589d931d/" hreflang="fr" | |||
data-tippy data-description="" | |||
data-source="https://alexsirac.com/activitypub-powered-wikis/" | |||
data-date="2024-03-26" | |||
data-favicon="https://alexsirac.com/wp-content/uploads/2023/01/cropped-cropped-portraitplante-32x32.webp" | |||
data-domain="alexsirac.com" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a>.</p> | |||
<p>C’est important pour <a href="/david/2024/02/29/" title="Wikipédia">diverses</a> <a href="/david/2024/03/07/" title="Sondages">raisons</a>.</p> | |||
<a href="#hr-139" title="Lien vers cette section de la page"><hr id="hr-139" /></a> | |||
<blockquote lang="en"> | |||
<p>I think it’s time to admit that my hobby is buying stuff for my new hobby.</p> | |||
<p><cite><em>Chris Hallbeck</em>, <a data-link-domain="mastodon.social" href="https://mastodon.social/@Chrishallbeck/112153815963114685">sur masto</a></cite></p> | |||
</blockquote> | |||
<p><em>Touché.</em></p> | |||
<nav> | |||
<p> | |||
<a href="/david/2024/apprentissage/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#apprentissage</a> | |||
<a href="/david/2024/technique/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#technique</a> | |||
<a href="/david/2024/web/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#web</a> | |||
<a href="/david/2024/#tags" title="Liste de toutes les étiquettes 2024">tous ?</a> | |||
</p> | |||
</nav> | |||
<nav> | |||
<p> | |||
<a rel="prev" | |||
href="/david/2024/03/25/" | |||
title="Publication précédente : Inclusion">← Précédent</a> • | |||
<a href="/david/2024/" title="Liste des publications récentes">↑ En 2024</a> | |||
</p> | |||
</nav> | |||
<form action="/david/recherche/" method="get"> | |||
<fieldset> | |||
<legend>Recherche</legend> | |||
<label for="input-search">Termes de votre recherche :</label> | |||
<input id="input-search" type="search" name="s" aria-describedby="indexation-infos" required> | |||
<input type="submit" value="Chercher"> | |||
<p id="indexation-infos"> | |||
<small> | |||
Seuls les contenus de ces 8 dernières années sont indexés. | |||
</small> | |||
</p> | |||
</fieldset> | |||
</form> | |||
<aside> | |||
<theme-toggle></theme-toggle> | |||
</aside> | |||
</article> | |||
<hr> | |||
<footer> | |||
<p> | |||
<a href="/david/" title="Aller à l’accueil">Accueil</a> | |||
• | |||
<a href="/david/log/" title="Accès au flux RSS">Suivre</a> | |||
• | |||
<a href="http://larlet.com" | |||
title="Go to my English profile" | |||
data-instant>Pro</a> | |||
• | |||
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel">Email</a> | |||
• | |||
<abbr title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340">Légal</abbr> | |||
</p> | |||
<template id="theme-selector"> | |||
<form> | |||
<style type="text/css"> | |||
fieldset div { | |||
text-align: center; | |||
} | |||
</style> | |||
<fieldset> | |||
<legend>Thème</legend> | |||
<div> | |||
<label> | |||
<input type="radio" value="auto" name="chosen-color-scheme" checked> | |||
Auto | |||
</label> | |||
<label> | |||
<input type="radio" value="dark" name="chosen-color-scheme"> | |||
Foncé | |||
</label> | |||
<label> | |||
<input type="radio" value="light" name="chosen-color-scheme"> | |||
Clair | |||
</label> | |||
</div> | |||
</fieldset> | |||
</form> | |||
</template> | |||
</footer> | |||
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script> | |||
<script> | |||
class ThemeToggle extends HTMLElement { | |||
constructor() { | |||
super() | |||
const themeSelectorTemplate = document.querySelector('#theme-selector') | |||
const form = themeSelectorTemplate.content.firstElementChild | |||
this.attachShadow({ mode: 'open' }) | |||
this.shadowRoot.appendChild(form.cloneNode(true)) | |||
} | |||
connectedCallback() { | |||
const form = this.shadowRoot.querySelector('form') | |||
form.addEventListener('change', (e) => { | |||
const chosenColorScheme = e.target.value | |||
localStorage.setItem('theme', chosenColorScheme) | |||
toggleTheme(chosenColorScheme) | |||
}) | |||
const selectedTheme = localStorage.getItem('theme') | |||
if (selectedTheme && selectedTheme !== 'undefined') { | |||
form.querySelector(`[value="${selectedTheme}"]`).checked = true | |||
} | |||
} | |||
} | |||
const prefersColorSchemeDark = '(prefers-color-scheme: dark)' | |||
window.addEventListener('load', () => { | |||
let colorsLayer = undefined | |||
let hasDarkRules = false | |||
for (const styleSheet of Array.from(document.styleSheets)) { | |||
let mediaRules = [] | |||
for (const layerRule of styleSheet.cssRules) { | |||
if (!(layerRule instanceof CSSLayerBlockRule)) { | |||
continue | |||
} | |||
if (layerRule.name === 'colors') { | |||
colorsLayer = layerRule | |||
} | |||
for (const cssRule of layerRule.cssRules) { | |||
if (cssRule.type !== CSSRule.MEDIA_RULE) { | |||
continue | |||
} | |||
// WARNING: Safari does not have/supports `conditionText`. | |||
if (cssRule.conditionText) { | |||
if (cssRule.conditionText !== prefersColorSchemeDark) { | |||
continue | |||
} | |||
} else { | |||
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) { | |||
continue | |||
} | |||
} | |||
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules)) | |||
} | |||
} | |||
// WARNING: do not try to insert a Rule to a styleSheet you are | |||
// currently iterating on, otherwise the browser will be stuck | |||
// in a infinite loop… | |||
for (const mediaRule of mediaRules) { | |||
// Safari requires the `0` second parameter (even if default). | |||
colorsLayer.insertRule(mediaRule.cssText, 0) | |||
hasDarkRules = true | |||
} | |||
} | |||
if (hasDarkRules) { | |||
if ('customElements' in window && !customElements.get('theme-toggle')) { | |||
customElements.define('theme-toggle', ThemeToggle) | |||
} | |||
} | |||
}) | |||
</script> | |||
<script src="/static/david/js/popper-2.11.8.min.js"></script> | |||
<script src="/static/david/js/tippy-bundle-6.3.7.umd.min.js"></script> | |||
<script> | |||
tippy('[data-tippy]', { | |||
content(reference) { | |||
reference.addEventListener('click', (e) => e.preventDefault()) | |||
return ` | |||
<h3 lang="fr"> | |||
<img src="${reference.dataset.favicon}" loading="lazy"> | |||
<a href="${reference.dataset.source}" | |||
>Article sur ${reference.dataset.domain}</a></h3> | |||
<p lang="${reference.hreflang}"><em>${reference.dataset.description}</em></p> | |||
<div class="tippy-links" lang="fr"> | |||
<a href="${reference.href}">Archive au ${reference.dataset.date}</a> | |||
</div> | |||
` | |||
}, | |||
allowHTML: true, | |||
interactive: true, | |||
delay: [150, 700], | |||
hideOnClick: false | |||
}) | |||
</script> | |||
<script type="module"> | |||
import { annotate } from '/static/david/js/rough-notation-0.5.1.esm.min.js' | |||
const markObserver = new IntersectionObserver((entries, observer) => { | |||
const computedStyle = getComputedStyle(document.documentElement) | |||
const markBackground = computedStyle.getPropertyValue('--mark-background') | |||
for (const entry of entries) { | |||
if (entry.intersectionRatio === 0) continue | |||
const markElement = entry.target | |||
markElement.style.backgroundColor = 'inherit' | |||
const annotation = annotate( | |||
markElement, { | |||
type: 'highlight', | |||
multiline: true, | |||
color: markBackground, | |||
// animate: !window.matchMedia('(prefers-reduced-motion: reduce)').matches | |||
animate: false | |||
} | |||
) | |||
annotation.show() | |||
observer.unobserve(markElement) | |||
} | |||
}, {threshold: 1.0}) | |||
for (const markElement of document.querySelectorAll('mark')) { | |||
markObserver.observe(markElement) | |||
} | |||
</script> | |||
</body> | |||
</html> |
@@ -1,4 +1,4 @@ | |||
<style type="text/css" nonce="oembed-default-style"> | |||
<style type="text/css"> | |||
o-embed { | |||
/* Size of the oembed + paragraph + margins. */ | |||
height: calc(300px + 1rem + 3rem); | |||
@@ -12,7 +12,7 @@ | |||
</p> | |||
</o-embed> | |||
<script type="module" nonce="oembed-web-component"> | |||
<script type="module"> | |||
class OEmbed extends HTMLElement { | |||
static tagName = 'o-embed' | |||
@@ -0,0 +1,87 @@ | |||
<gpx-viewer data-height="500px" data-width="800px"> | |||
<p> | |||
Vous devriez voir s’afficher une carte de mon « Grand Tour de la forêt de Ouareau », | |||
qui contient <a data-gpx href="/static/david/2024/grand_tour_de_la_foret_de_ouareau.gpx">cette trace GPX</a>, | |||
centrée sur <span data-latitude>46.2117</span>, <span data-longitude>-73.9335</span> | |||
avec un zoom de <span data-zoom>12</span>. | |||
Il est probable que cela ne s’exécute pas dans un agrégateur par exemple. | |||
</p> | |||
</gpx-viewer> | |||
<script type="module"> | |||
import * as L from '/static/david/2024/leaflet.1.9.4/leaflet.1.9.4.js' | |||
window.L = L // Hack for leaflet-gpx, youpi. | |||
</script> | |||
<script type="module" nonce="oembed-web-component"> | |||
class GPXViewer extends HTMLElement { | |||
static tagName = 'gpx-viewer' | |||
static register(tagName, registry) { | |||
if(!registry && ('customElements' in globalThis)) { | |||
registry = globalThis.customElements | |||
} | |||
registry?.define(tagName || this.tagName, this) | |||
} | |||
#attachCSS(path) { | |||
const linkElement = document.createElement('link') | |||
linkElement.setAttribute('rel', 'stylesheet') | |||
linkElement.setAttribute('href', path) | |||
this.shadowRoot.appendChild(linkElement) | |||
} | |||
#createMapContainer() { | |||
const mapContainer = document.createElement('div') | |||
mapContainer.style.height = this.dataset.height | |||
mapContainer.style.width = this.dataset.width | |||
this.shadowRoot.appendChild(mapContainer) | |||
return mapContainer | |||
} | |||
#createMap(mapContainer) { | |||
const map = L.map(mapContainer).setView( | |||
[ | |||
this.querySelector('[data-latitude]').textContent, | |||
this.querySelector('[data-longitude]').textContent, | |||
], | |||
this.querySelector('[data-zoom]').textContent | |||
) | |||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { | |||
attribution: | |||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', | |||
}).addTo(map) | |||
return map | |||
} | |||
#attachGPX(map) { | |||
const gpxUrl = this.querySelector('[data-gpx]').href | |||
new GPX(gpxUrl, { | |||
async: true, | |||
marker_options: { | |||
startIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-start.png', | |||
endIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-end.png', | |||
shadowUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-shadow.png' | |||
} | |||
}).on('loaded', (e) => { | |||
map.fitBounds(e.target.getBounds()) | |||
}).addTo(map) | |||
} | |||
constructor() { | |||
super() | |||
this.attachShadow({ mode: 'open' }) | |||
} | |||
async connectedCallback() { | |||
this.#attachCSS('/static/david/2024/leaflet.1.9.4/leaflet.css') | |||
const mapContainer = this.#createMapContainer() | |||
const map = this.#createMap(mapContainer) | |||
this.#attachGPX(map) | |||
} | |||
} | |||
import GPX from '/static/david/2024/leaflet-gpx.1.7.0-custom/gpx.1.7.0-custom.js' | |||
GPXViewer.register() | |||
</script> |
@@ -0,0 +1,35 @@ | |||
# GPX Viewer | |||
[Suite de mes expérimentations](/david/2024/03/25/) de la veille à la fois en cartographie et en *web components*. Je me suis demandé s’il était possible de faire un composant qui ne dépende pas de uMap pour afficher une trace GPX. Le fait d’avoir passé une heure à trouver la bonne [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) pour arriver à afficher une carte m’a d’une certaine manière motivé (c’était une journée galère). | |||
Il se trouve qu’il existe [leaflet-gpx](https://github.com/mpetazzoni/leaflet-gpx) qui fait exactement ce que je voulais. Il ne me restait « plus qu’à » en faire un composant web, ce que j’avais déjà [fait par ailleurs](https://gitlab.com/umap-project/leaflet-webcomponents/) : | |||
{{ extra }} | |||
Alors en fait, comme rien n’est jamais simple en JS, il a fallu que j’adapte le plugin qui n’était pas compatible avec les modules JS et que je fasse des galipettes pour que ça finisse par tomber en marche 🤸. Cet écosystème est merveilleux (quel métier !). J’ai quand même [remonté le problème](https://github.com/mpetazzoni/leaflet-gpx/issues/153). | |||
Au passage, inspiré par [le travail de Knut Hühne](https://blog.k-nut.eu/leaflet-microdata-html-webcomponent) sur la sémantique des données d’une carte, je me suis demandé si j’allais définir un [itinerary](https://schema.org/itinerary) pour un [Trip](https://schema.org/Trip) avec des [GeoCoordinates](https://schema.org/GeoCoordinates) mais ça m’a fait tirer un fil beaucoup trop long. Je garde l’idée sous le coude [pour mes propres recherches](https://umap-project.gitlab.io/leaflet-webcomponents/) dans le domaine cela dit. | |||
Un composant de plus, je ne suis pas très satisfait de celui-ci car j’avais l’espoir d’arriver à quelque chose de facilement réutilisable. J’aurais au moins commencé à mettre les doigts dans le XML du format GPX. Il est possible de définir plusieurs segments au sein d’une même trace. Il peut y avoir pas mal de métadonnées, notamment de mise en forme de la trace mais aussi de caractéristiques (biologiques, physiques, etc). | |||
--- | |||
> [en] The fact is that we can’t rely on any single website to hold the whole world’s knowledge, because it can be corrupted sooner or later. The only solution is a distributed architecture, with many smaller websites connecting with each other and sharing information. This is where ActivityPub comes in, the protocol used by Mastodon, Lemmy, Peertube and many other federated social media projects. | |||
> | |||
> I have worked on Lemmy for the past four years, bringing it from a prototype to a fully functional Reddit alternative. I wrote the entire federation code and became very familiar with the protocol. ==I realized that the same technology easily be used to create a federated encyclopedia.== As no one else took up such a project, I finally decided to do it on my own and create Ibis. Thanks to my previous experience with the tech stack and the ActivityPub library I created, I was able to complete a proof of concept in a relatively short time of four months. | |||
> | |||
> <cite>*[Announcing Ibis, the federated Wikipedia Alternative](https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki)*</cite> | |||
Wikipedia décentralisé, je dis oui ! Merci [Alex](https://alexsirac.com/activitypub-powered-wikis/). | |||
C’est important pour [diverses](/david/2024/02/29/) [raisons](/david/2024/03/07/). | |||
--- | |||
> [en] I think it’s time to admit that my hobby is buying stuff for my new hobby. | |||
> | |||
> <cite>*Chris Hallbeck*, [sur masto](https://mastodon.social/@Chrishallbeck/112153815963114685)</cite> | |||
*Touché.* | |||
#apprentissage #technique #web |
@@ -134,6 +134,186 @@ | |||
</p> | |||
</nav> | |||
<h2> | |||
<a href="/david/2024/03/26/" title="Lien permanent vers cet article">GPX Viewer</a> <time datetime="2024-03-26">26 mars 2024</time> | |||
</h2> | |||
<p><a href="/david/2024/03/25/" title="Inclusion">Suite de mes expérimentations</a> de la veille à la fois en cartographie et en <em>web components</em>. Je me suis demandé s’il était possible de faire un composant qui ne dépende pas de uMap pour afficher une trace GPX. Le fait d’avoir passé une heure à trouver la bonne <a data-link-domain="MDN" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">CSP</a> pour arriver à afficher une carte m’a d’une certaine manière motivé (c’était une journée galère).</p> | |||
<p>Il se trouve qu’il existe <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx">leaflet-gpx</a> qui fait exactement ce que je voulais. Il ne me restait « plus qu’à » en faire un composant web, ce que j’avais déjà <a data-link-domain="gitlab.com" href="https://gitlab.com/umap-project/leaflet-webcomponents/">fait par ailleurs</a> :</p> | |||
<gpx-viewer data-height="500px" data-width="800px"> | |||
<p> | |||
Vous devriez voir s’afficher une carte de mon « Grand Tour de la forêt de Ouareau », | |||
qui contient <a data-gpx href="/static/david/2024/grand_tour_de_la_foret_de_ouareau.gpx">cette trace GPX</a>, | |||
centrée sur <span data-latitude>46.2117</span>, <span data-longitude>-73.9335</span> | |||
avec un zoom de <span data-zoom>12</span>. | |||
Il est probable que cela ne s’exécute pas dans un agrégateur par exemple. | |||
</p> | |||
</gpx-viewer> | |||
<script type="module"> | |||
import * as L from '/static/david/2024/leaflet.1.9.4/leaflet.1.9.4.js' | |||
window.L = L // Hack for leaflet-gpx, youpi. | |||
</script> | |||
<script type="module" nonce="oembed-web-component"> | |||
class GPXViewer extends HTMLElement { | |||
static tagName = 'gpx-viewer' | |||
static register(tagName, registry) { | |||
if(!registry && ('customElements' in globalThis)) { | |||
registry = globalThis.customElements | |||
} | |||
registry?.define(tagName || this.tagName, this) | |||
} | |||
#attachCSS(path) { | |||
const linkElement = document.createElement('link') | |||
linkElement.setAttribute('rel', 'stylesheet') | |||
linkElement.setAttribute('href', path) | |||
this.shadowRoot.appendChild(linkElement) | |||
} | |||
#createMapContainer() { | |||
const mapContainer = document.createElement('div') | |||
mapContainer.style.height = this.dataset.height | |||
mapContainer.style.width = this.dataset.width | |||
this.shadowRoot.appendChild(mapContainer) | |||
return mapContainer | |||
} | |||
#createMap(mapContainer) { | |||
const map = L.map(mapContainer).setView( | |||
[ | |||
this.querySelector('[data-latitude]').textContent, | |||
this.querySelector('[data-longitude]').textContent, | |||
], | |||
this.querySelector('[data-zoom]').textContent | |||
) | |||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { | |||
attribution: | |||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', | |||
}).addTo(map) | |||
return map | |||
} | |||
#attachGPX(map) { | |||
const gpxUrl = this.querySelector('[data-gpx]').href | |||
new GPX(gpxUrl, { | |||
async: true, | |||
marker_options: { | |||
startIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-start.png', | |||
endIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-end.png', | |||
shadowUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-shadow.png' | |||
} | |||
}).on('loaded', (e) => { | |||
map.fitBounds(e.target.getBounds()) | |||
}).addTo(map) | |||
} | |||
constructor() { | |||
super() | |||
this.attachShadow({ mode: 'open' }) | |||
} | |||
async connectedCallback() { | |||
this.#attachCSS('/static/david/2024/leaflet.1.9.4/leaflet.css') | |||
const mapContainer = this.#createMapContainer() | |||
const map = this.#createMap(mapContainer) | |||
this.#attachGPX(map) | |||
} | |||
} | |||
import GPX from '/static/david/2024/leaflet-gpx.1.7.0-custom/gpx.1.7.0-custom.js' | |||
GPXViewer.register() | |||
</script> | |||
<p>Alors en fait, comme rien n’est jamais simple en JS, il a fallu que j’adapte le plugin qui n’était pas compatible avec les modules JS et que je fasse des galipettes pour que ça finisse par tomber en marche 🤸. Cet écosystème est merveilleux (quel métier !). J’ai quand même <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx/issues/153">remonté le problème</a>.</p> | |||
<p>Au passage, inspiré par <a data-link-domain="blog.k-nut.eu" href="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent" hreflang="en" | |||
title="Consultation de l’article (anglais)">le travail de Knut Hühne</a> | |||
<a href="/david/cache/2024/65fba9cd025cd2403f932cb2c928cf14/" hreflang="en" | |||
data-tippy data-description="An example of using schema.org microdata to build a HTML Webcomponent for Leaflet" | |||
data-source="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent" | |||
data-date="2024-03-25" | |||
data-favicon="https://blog.k-nut.eu/favicon-32x32.png" | |||
data-domain="blog.k-nut.eu" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a> sur la sémantique des données d’une carte, je me suis demandé si j’allais définir un <a data-link-domain="schema.org" href="https://schema.org/itinerary">itinerary</a> pour un <a data-link-domain="schema.org" href="https://schema.org/Trip">Trip</a> avec des <a data-link-domain="schema.org" href="https://schema.org/GeoCoordinates">GeoCoordinates</a> mais ça m’a fait tirer un fil beaucoup trop long. Je garde l’idée sous le coude <a data-link-domain="umap-project.gitlab.io" href="https://umap-project.gitlab.io/leaflet-webcomponents/">pour mes propres recherches</a> dans le domaine cela dit.</p> | |||
<p>Un composant de plus, je ne suis pas très satisfait de celui-ci car j’avais l’espoir d’arriver à quelque chose de facilement réutilisable. J’aurais au moins commencé à mettre les doigts dans le XML du format GPX. Il est possible de définir plusieurs segments au sein d’une même trace. Il peut y avoir pas mal de métadonnées, notamment de mise en forme de la trace mais aussi de caractéristiques (biologiques, physiques, etc).</p> | |||
<a href="#hr-138" title="Lien vers cette section de la page"><hr id="hr-138" /></a> | |||
<blockquote lang="en"> | |||
<p>The fact is that we can’t rely on any single website to hold the whole world’s knowledge, because it can be corrupted sooner or later. The only solution is a distributed architecture, with many smaller websites connecting with each other and sharing information. This is where ActivityPub comes in, the protocol used by Mastodon, Lemmy, Peertube and many other federated social media projects.</p> | |||
<p>I have worked on Lemmy for the past four years, bringing it from a prototype to a fully functional Reddit alternative. I wrote the entire federation code and became very familiar with the protocol. <mark>I realized that the same technology easily be used to create a federated encyclopedia.</mark> As no one else took up such a project, I finally decided to do it on my own and create Ibis. Thanks to my previous experience with the tech stack and the ActivityPub library I created, I was able to complete a proof of concept in a relatively short time of four months.</p> | |||
<p><cite><em><a data-link-domain="ibis.wiki" href="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki" hreflang="en" | |||
title="Consultation de l’article (anglais)">Announcing Ibis, the federated Wikipedia Alternative</a> | |||
<a href="/david/cache/2024/691120ebac09f68413501b7f5daa2db7/" hreflang="en" | |||
data-tippy data-description="" | |||
data-source="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki" | |||
data-date="2024-03-26" | |||
data-favicon="" | |||
data-domain="ibis.wiki" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a></em></cite></p> | |||
</blockquote> | |||
<p>Wikipedia décentralisé, je dis oui ! Merci <a data-link-domain="alexsirac.com" href="https://alexsirac.com/activitypub-powered-wikis/" hreflang="fr" | |||
title="Consultation de l’article">Alex</a> | |||
<a href="/david/cache/2024/55c19feff784a41d2527b5f1589d931d/" hreflang="fr" | |||
data-tippy data-description="" | |||
data-source="https://alexsirac.com/activitypub-powered-wikis/" | |||
data-date="2024-03-26" | |||
data-favicon="https://alexsirac.com/wp-content/uploads/2023/01/cropped-cropped-portraitplante-32x32.webp" | |||
data-domain="alexsirac.com" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a>.</p> | |||
<p>C’est important pour <a href="/david/2024/02/29/" title="Wikipédia">diverses</a> <a href="/david/2024/03/07/" title="Sondages">raisons</a>.</p> | |||
<a href="#hr-139" title="Lien vers cette section de la page"><hr id="hr-139" /></a> | |||
<blockquote lang="en"> | |||
<p>I think it’s time to admit that my hobby is buying stuff for my new hobby.</p> | |||
<p><cite><em>Chris Hallbeck</em>, <a data-link-domain="mastodon.social" href="https://mastodon.social/@Chrishallbeck/112153815963114685">sur masto</a></cite></p> | |||
</blockquote> | |||
<p><em>Touché.</em></p> | |||
<nav> | |||
<p> | |||
<a href="/david/2024/apprentissage/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#apprentissage</a> | |||
<a href="/david/2024/technique/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#technique</a> | |||
<a href="/david/2024/web/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#web</a> | |||
<a href="/david/2024/#tags" title="Liste de toutes les étiquettes 2024">tous ?</a> | |||
</p> | |||
</nav> | |||
<h2> | |||
<a href="/david/2024/03/19/" title="Lien permanent vers cet article">Excitation</a> <time datetime="2024-03-19">19 mars 2024</time> | |||
</h2> |
@@ -139,7 +139,7 @@ | |||
</h2> | |||
<p>Dans le cadre de <del><a data-link-domain="en.wikipedia.org" href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">manger ma propre bouffe pour chien</a></del> mon <a data-link-domain="fr.wikipedia.org" href="https://fr.wikipedia.org/wiki/Auto%C3%A9quipement">autoéquipement</a>, je voulais vérifier si l’implémentation de <a data-link-domain="oembed.com" href="https://oembed.com/">oEmbed</a> dans <a data-link-domain="umap-project.org" href="https://umap-project.org/fr/">uMap</a> était utilisable… et il se trouve que ce n’était pas le cas avant aujourd’hui. Il y aurait encore des pistes d’améliorations mais au moins ça affiche une carte (si vous autorisez les requêtes externes en JS) :</p> | |||
<style type="text/css" nonce="oembed-default-style"> | |||
<style type="text/css"> | |||
o-embed { | |||
/* Size of the oembed + paragraph + margins. */ | |||
height: calc(300px + 1rem + 3rem); | |||
@@ -153,7 +153,7 @@ | |||
</p> | |||
</o-embed> | |||
<script type="module" nonce="oembed-web-component"> | |||
<script type="module"> | |||
class OEmbed extends HTMLElement { | |||
static tagName = 'o-embed' | |||
@@ -220,7 +220,8 @@ | |||
<a href="/david/2024/03/21/">Fourchette</a>, | |||
<a href="/david/2024/03/23/">Intendant</a>, | |||
<a href="/david/2024/03/24/">Cage</a>, | |||
<a href="/david/2024/03/25/">Inclusion</a>. | |||
<a href="/david/2024/03/25/">Inclusion</a>, | |||
<a href="/david/2024/03/26/">GPX Viewer</a>. | |||
</p> | |||
@@ -231,7 +232,7 @@ | |||
<a href="/david/2024/accompagnement/" rel="tag">#accompagnement (2)</a>, | |||
<a href="/david/2024/adaptation/" rel="tag">#adaptation (3)</a>, | |||
<a href="/david/2024/addiction/" rel="tag">#addiction (5)</a>, | |||
<a href="/david/2024/apprentissage/" rel="tag">#apprentissage (15)</a>, | |||
<a href="/david/2024/apprentissage/" rel="tag">#apprentissage (16)</a>, | |||
<a href="/david/2024/aventure/" rel="tag">#aventure (4)</a>, | |||
<a href="/david/2024/cinema/" rel="tag">#cinéma (1)</a>, | |||
<a href="/david/2024/commun/" rel="tag">#commun (9)</a>, | |||
@@ -270,9 +271,9 @@ | |||
<a href="/david/2024/psychologie/" rel="tag">#psychologie (11)</a>, | |||
<a href="/david/2024/solastalgia/" rel="tag">#solastalgia (5)</a>, | |||
<a href="/david/2024/sport/" rel="tag">#sport (6)</a>, | |||
<a href="/david/2024/technique/" rel="tag">#technique (20)</a>, | |||
<a href="/david/2024/technique/" rel="tag">#technique (21)</a>, | |||
<a href="/david/2024/velo/" rel="tag">#vélo (1)</a>, | |||
<a href="/david/2024/web/" rel="tag">#web (13)</a>. | |||
<a href="/david/2024/web/" rel="tag">#web (14)</a>. | |||
</p> | |||
@@ -139,7 +139,7 @@ | |||
</h2> | |||
<p>Dans le cadre de <del><a data-link-domain="en.wikipedia.org" href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">manger ma propre bouffe pour chien</a></del> mon <a data-link-domain="fr.wikipedia.org" href="https://fr.wikipedia.org/wiki/Auto%C3%A9quipement">autoéquipement</a>, je voulais vérifier si l’implémentation de <a data-link-domain="oembed.com" href="https://oembed.com/">oEmbed</a> dans <a data-link-domain="umap-project.org" href="https://umap-project.org/fr/">uMap</a> était utilisable… et il se trouve que ce n’était pas le cas avant aujourd’hui. Il y aurait encore des pistes d’améliorations mais au moins ça affiche une carte (si vous autorisez les requêtes externes en JS) :</p> | |||
<style type="text/css" nonce="oembed-default-style"> | |||
<style type="text/css"> | |||
o-embed { | |||
/* Size of the oembed + paragraph + margins. */ | |||
height: calc(300px + 1rem + 3rem); | |||
@@ -153,7 +153,7 @@ | |||
</p> | |||
</o-embed> | |||
<script type="module" nonce="oembed-web-component"> | |||
<script type="module"> | |||
class OEmbed extends HTMLElement { | |||
static tagName = 'o-embed' | |||
@@ -134,12 +134,192 @@ | |||
</p> | |||
</nav> | |||
<h2> | |||
<a href="/david/2024/03/26/" title="Lien permanent vers cet article">GPX Viewer</a> <time datetime="2024-03-26">26 mars 2024</time> | |||
</h2> | |||
<p><a href="/david/2024/03/25/" title="Inclusion">Suite de mes expérimentations</a> de la veille à la fois en cartographie et en <em>web components</em>. Je me suis demandé s’il était possible de faire un composant qui ne dépende pas de uMap pour afficher une trace GPX. Le fait d’avoir passé une heure à trouver la bonne <a data-link-domain="MDN" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">CSP</a> pour arriver à afficher une carte m’a d’une certaine manière motivé (c’était une journée galère).</p> | |||
<p>Il se trouve qu’il existe <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx">leaflet-gpx</a> qui fait exactement ce que je voulais. Il ne me restait « plus qu’à » en faire un composant web, ce que j’avais déjà <a data-link-domain="gitlab.com" href="https://gitlab.com/umap-project/leaflet-webcomponents/">fait par ailleurs</a> :</p> | |||
<gpx-viewer data-height="500px" data-width="800px"> | |||
<p> | |||
Vous devriez voir s’afficher une carte de mon « Grand Tour de la forêt de Ouareau », | |||
qui contient <a data-gpx href="/static/david/2024/grand_tour_de_la_foret_de_ouareau.gpx">cette trace GPX</a>, | |||
centrée sur <span data-latitude>46.2117</span>, <span data-longitude>-73.9335</span> | |||
avec un zoom de <span data-zoom>12</span>. | |||
Il est probable que cela ne s’exécute pas dans un agrégateur par exemple. | |||
</p> | |||
</gpx-viewer> | |||
<script type="module"> | |||
import * as L from '/static/david/2024/leaflet.1.9.4/leaflet.1.9.4.js' | |||
window.L = L // Hack for leaflet-gpx, youpi. | |||
</script> | |||
<script type="module" nonce="oembed-web-component"> | |||
class GPXViewer extends HTMLElement { | |||
static tagName = 'gpx-viewer' | |||
static register(tagName, registry) { | |||
if(!registry && ('customElements' in globalThis)) { | |||
registry = globalThis.customElements | |||
} | |||
registry?.define(tagName || this.tagName, this) | |||
} | |||
#attachCSS(path) { | |||
const linkElement = document.createElement('link') | |||
linkElement.setAttribute('rel', 'stylesheet') | |||
linkElement.setAttribute('href', path) | |||
this.shadowRoot.appendChild(linkElement) | |||
} | |||
#createMapContainer() { | |||
const mapContainer = document.createElement('div') | |||
mapContainer.style.height = this.dataset.height | |||
mapContainer.style.width = this.dataset.width | |||
this.shadowRoot.appendChild(mapContainer) | |||
return mapContainer | |||
} | |||
#createMap(mapContainer) { | |||
const map = L.map(mapContainer).setView( | |||
[ | |||
this.querySelector('[data-latitude]').textContent, | |||
this.querySelector('[data-longitude]').textContent, | |||
], | |||
this.querySelector('[data-zoom]').textContent | |||
) | |||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { | |||
attribution: | |||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', | |||
}).addTo(map) | |||
return map | |||
} | |||
#attachGPX(map) { | |||
const gpxUrl = this.querySelector('[data-gpx]').href | |||
new GPX(gpxUrl, { | |||
async: true, | |||
marker_options: { | |||
startIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-start.png', | |||
endIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-end.png', | |||
shadowUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-shadow.png' | |||
} | |||
}).on('loaded', (e) => { | |||
map.fitBounds(e.target.getBounds()) | |||
}).addTo(map) | |||
} | |||
constructor() { | |||
super() | |||
this.attachShadow({ mode: 'open' }) | |||
} | |||
async connectedCallback() { | |||
this.#attachCSS('/static/david/2024/leaflet.1.9.4/leaflet.css') | |||
const mapContainer = this.#createMapContainer() | |||
const map = this.#createMap(mapContainer) | |||
this.#attachGPX(map) | |||
} | |||
} | |||
import GPX from '/static/david/2024/leaflet-gpx.1.7.0-custom/gpx.1.7.0-custom.js' | |||
GPXViewer.register() | |||
</script> | |||
<p>Alors en fait, comme rien n’est jamais simple en JS, il a fallu que j’adapte le plugin qui n’était pas compatible avec les modules JS et que je fasse des galipettes pour que ça finisse par tomber en marche 🤸. Cet écosystème est merveilleux (quel métier !). J’ai quand même <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx/issues/153">remonté le problème</a>.</p> | |||
<p>Au passage, inspiré par <a data-link-domain="blog.k-nut.eu" href="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent" hreflang="en" | |||
title="Consultation de l’article (anglais)">le travail de Knut Hühne</a> | |||
<a href="/david/cache/2024/65fba9cd025cd2403f932cb2c928cf14/" hreflang="en" | |||
data-tippy data-description="An example of using schema.org microdata to build a HTML Webcomponent for Leaflet" | |||
data-source="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent" | |||
data-date="2024-03-25" | |||
data-favicon="https://blog.k-nut.eu/favicon-32x32.png" | |||
data-domain="blog.k-nut.eu" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a> sur la sémantique des données d’une carte, je me suis demandé si j’allais définir un <a data-link-domain="schema.org" href="https://schema.org/itinerary">itinerary</a> pour un <a data-link-domain="schema.org" href="https://schema.org/Trip">Trip</a> avec des <a data-link-domain="schema.org" href="https://schema.org/GeoCoordinates">GeoCoordinates</a> mais ça m’a fait tirer un fil beaucoup trop long. Je garde l’idée sous le coude <a data-link-domain="umap-project.gitlab.io" href="https://umap-project.gitlab.io/leaflet-webcomponents/">pour mes propres recherches</a> dans le domaine cela dit.</p> | |||
<p>Un composant de plus, je ne suis pas très satisfait de celui-ci car j’avais l’espoir d’arriver à quelque chose de facilement réutilisable. J’aurais au moins commencé à mettre les doigts dans le XML du format GPX. Il est possible de définir plusieurs segments au sein d’une même trace. Il peut y avoir pas mal de métadonnées, notamment de mise en forme de la trace mais aussi de caractéristiques (biologiques, physiques, etc).</p> | |||
<a href="#hr-138" title="Lien vers cette section de la page"><hr id="hr-138" /></a> | |||
<blockquote lang="en"> | |||
<p>The fact is that we can’t rely on any single website to hold the whole world’s knowledge, because it can be corrupted sooner or later. The only solution is a distributed architecture, with many smaller websites connecting with each other and sharing information. This is where ActivityPub comes in, the protocol used by Mastodon, Lemmy, Peertube and many other federated social media projects.</p> | |||
<p>I have worked on Lemmy for the past four years, bringing it from a prototype to a fully functional Reddit alternative. I wrote the entire federation code and became very familiar with the protocol. <mark>I realized that the same technology easily be used to create a federated encyclopedia.</mark> As no one else took up such a project, I finally decided to do it on my own and create Ibis. Thanks to my previous experience with the tech stack and the ActivityPub library I created, I was able to complete a proof of concept in a relatively short time of four months.</p> | |||
<p><cite><em><a data-link-domain="ibis.wiki" href="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki" hreflang="en" | |||
title="Consultation de l’article (anglais)">Announcing Ibis, the federated Wikipedia Alternative</a> | |||
<a href="/david/cache/2024/691120ebac09f68413501b7f5daa2db7/" hreflang="en" | |||
data-tippy data-description="" | |||
data-source="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki" | |||
data-date="2024-03-26" | |||
data-favicon="" | |||
data-domain="ibis.wiki" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a></em></cite></p> | |||
</blockquote> | |||
<p>Wikipedia décentralisé, je dis oui ! Merci <a data-link-domain="alexsirac.com" href="https://alexsirac.com/activitypub-powered-wikis/" hreflang="fr" | |||
title="Consultation de l’article">Alex</a> | |||
<a href="/david/cache/2024/55c19feff784a41d2527b5f1589d931d/" hreflang="fr" | |||
data-tippy data-description="" | |||
data-source="https://alexsirac.com/activitypub-powered-wikis/" | |||
data-date="2024-03-26" | |||
data-favicon="https://alexsirac.com/wp-content/uploads/2023/01/cropped-cropped-portraitplante-32x32.webp" | |||
data-domain="alexsirac.com" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a>.</p> | |||
<p>C’est important pour <a href="/david/2024/02/29/" title="Wikipédia">diverses</a> <a href="/david/2024/03/07/" title="Sondages">raisons</a>.</p> | |||
<a href="#hr-139" title="Lien vers cette section de la page"><hr id="hr-139" /></a> | |||
<blockquote lang="en"> | |||
<p>I think it’s time to admit that my hobby is buying stuff for my new hobby.</p> | |||
<p><cite><em>Chris Hallbeck</em>, <a data-link-domain="mastodon.social" href="https://mastodon.social/@Chrishallbeck/112153815963114685">sur masto</a></cite></p> | |||
</blockquote> | |||
<p><em>Touché.</em></p> | |||
<nav> | |||
<p> | |||
<a href="/david/2024/apprentissage/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#apprentissage</a> | |||
<a href="/david/2024/technique/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#technique</a> | |||
<a href="/david/2024/web/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#web</a> | |||
<a href="/david/2024/#tags" title="Liste de toutes les étiquettes 2024">tous ?</a> | |||
</p> | |||
</nav> | |||
<h2> | |||
<a href="/david/2024/03/25/" title="Lien permanent vers cet article">Inclusion</a> <time datetime="2024-03-25">25 mars 2024</time> | |||
</h2> | |||
<p>Dans le cadre de <del><a data-link-domain="en.wikipedia.org" href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">manger ma propre bouffe pour chien</a></del> mon <a data-link-domain="fr.wikipedia.org" href="https://fr.wikipedia.org/wiki/Auto%C3%A9quipement">autoéquipement</a>, je voulais vérifier si l’implémentation de <a data-link-domain="oembed.com" href="https://oembed.com/">oEmbed</a> dans <a data-link-domain="umap-project.org" href="https://umap-project.org/fr/">uMap</a> était utilisable… et il se trouve que ce n’était pas le cas avant aujourd’hui. Il y aurait encore des pistes d’améliorations mais au moins ça affiche une carte (si vous autorisez les requêtes externes en JS) :</p> | |||
<style type="text/css" nonce="oembed-default-style"> | |||
<style type="text/css"> | |||
o-embed { | |||
/* Size of the oembed + paragraph + margins. */ | |||
height: calc(300px + 1rem + 3rem); | |||
@@ -153,7 +333,7 @@ | |||
</p> | |||
</o-embed> | |||
<script type="module" nonce="oembed-web-component"> | |||
<script type="module"> | |||
class OEmbed extends HTMLElement { | |||
static tagName = 'o-embed' | |||
@@ -134,6 +134,186 @@ | |||
</p> | |||
</nav> | |||
<h2> | |||
<a href="/david/2024/03/26/" title="Lien permanent vers cet article">GPX Viewer</a> <time datetime="2024-03-26">26 mars 2024</time> | |||
</h2> | |||
<p><a href="/david/2024/03/25/" title="Inclusion">Suite de mes expérimentations</a> de la veille à la fois en cartographie et en <em>web components</em>. Je me suis demandé s’il était possible de faire un composant qui ne dépende pas de uMap pour afficher une trace GPX. Le fait d’avoir passé une heure à trouver la bonne <a data-link-domain="MDN" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">CSP</a> pour arriver à afficher une carte m’a d’une certaine manière motivé (c’était une journée galère).</p> | |||
<p>Il se trouve qu’il existe <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx">leaflet-gpx</a> qui fait exactement ce que je voulais. Il ne me restait « plus qu’à » en faire un composant web, ce que j’avais déjà <a data-link-domain="gitlab.com" href="https://gitlab.com/umap-project/leaflet-webcomponents/">fait par ailleurs</a> :</p> | |||
<gpx-viewer data-height="500px" data-width="800px"> | |||
<p> | |||
Vous devriez voir s’afficher une carte de mon « Grand Tour de la forêt de Ouareau », | |||
qui contient <a data-gpx href="/static/david/2024/grand_tour_de_la_foret_de_ouareau.gpx">cette trace GPX</a>, | |||
centrée sur <span data-latitude>46.2117</span>, <span data-longitude>-73.9335</span> | |||
avec un zoom de <span data-zoom>12</span>. | |||
Il est probable que cela ne s’exécute pas dans un agrégateur par exemple. | |||
</p> | |||
</gpx-viewer> | |||
<script type="module"> | |||
import * as L from '/static/david/2024/leaflet.1.9.4/leaflet.1.9.4.js' | |||
window.L = L // Hack for leaflet-gpx, youpi. | |||
</script> | |||
<script type="module" nonce="oembed-web-component"> | |||
class GPXViewer extends HTMLElement { | |||
static tagName = 'gpx-viewer' | |||
static register(tagName, registry) { | |||
if(!registry && ('customElements' in globalThis)) { | |||
registry = globalThis.customElements | |||
} | |||
registry?.define(tagName || this.tagName, this) | |||
} | |||
#attachCSS(path) { | |||
const linkElement = document.createElement('link') | |||
linkElement.setAttribute('rel', 'stylesheet') | |||
linkElement.setAttribute('href', path) | |||
this.shadowRoot.appendChild(linkElement) | |||
} | |||
#createMapContainer() { | |||
const mapContainer = document.createElement('div') | |||
mapContainer.style.height = this.dataset.height | |||
mapContainer.style.width = this.dataset.width | |||
this.shadowRoot.appendChild(mapContainer) | |||
return mapContainer | |||
} | |||
#createMap(mapContainer) { | |||
const map = L.map(mapContainer).setView( | |||
[ | |||
this.querySelector('[data-latitude]').textContent, | |||
this.querySelector('[data-longitude]').textContent, | |||
], | |||
this.querySelector('[data-zoom]').textContent | |||
) | |||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { | |||
attribution: | |||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', | |||
}).addTo(map) | |||
return map | |||
} | |||
#attachGPX(map) { | |||
const gpxUrl = this.querySelector('[data-gpx]').href | |||
new GPX(gpxUrl, { | |||
async: true, | |||
marker_options: { | |||
startIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-start.png', | |||
endIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-end.png', | |||
shadowUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-shadow.png' | |||
} | |||
}).on('loaded', (e) => { | |||
map.fitBounds(e.target.getBounds()) | |||
}).addTo(map) | |||
} | |||
constructor() { | |||
super() | |||
this.attachShadow({ mode: 'open' }) | |||
} | |||
async connectedCallback() { | |||
this.#attachCSS('/static/david/2024/leaflet.1.9.4/leaflet.css') | |||
const mapContainer = this.#createMapContainer() | |||
const map = this.#createMap(mapContainer) | |||
this.#attachGPX(map) | |||
} | |||
} | |||
import GPX from '/static/david/2024/leaflet-gpx.1.7.0-custom/gpx.1.7.0-custom.js' | |||
GPXViewer.register() | |||
</script> | |||
<p>Alors en fait, comme rien n’est jamais simple en JS, il a fallu que j’adapte le plugin qui n’était pas compatible avec les modules JS et que je fasse des galipettes pour que ça finisse par tomber en marche 🤸. Cet écosystème est merveilleux (quel métier !). J’ai quand même <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx/issues/153">remonté le problème</a>.</p> | |||
<p>Au passage, inspiré par <a data-link-domain="blog.k-nut.eu" href="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent" hreflang="en" | |||
title="Consultation de l’article (anglais)">le travail de Knut Hühne</a> | |||
<a href="/david/cache/2024/65fba9cd025cd2403f932cb2c928cf14/" hreflang="en" | |||
data-tippy data-description="An example of using schema.org microdata to build a HTML Webcomponent for Leaflet" | |||
data-source="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent" | |||
data-date="2024-03-25" | |||
data-favicon="https://blog.k-nut.eu/favicon-32x32.png" | |||
data-domain="blog.k-nut.eu" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a> sur la sémantique des données d’une carte, je me suis demandé si j’allais définir un <a data-link-domain="schema.org" href="https://schema.org/itinerary">itinerary</a> pour un <a data-link-domain="schema.org" href="https://schema.org/Trip">Trip</a> avec des <a data-link-domain="schema.org" href="https://schema.org/GeoCoordinates">GeoCoordinates</a> mais ça m’a fait tirer un fil beaucoup trop long. Je garde l’idée sous le coude <a data-link-domain="umap-project.gitlab.io" href="https://umap-project.gitlab.io/leaflet-webcomponents/">pour mes propres recherches</a> dans le domaine cela dit.</p> | |||
<p>Un composant de plus, je ne suis pas très satisfait de celui-ci car j’avais l’espoir d’arriver à quelque chose de facilement réutilisable. J’aurais au moins commencé à mettre les doigts dans le XML du format GPX. Il est possible de définir plusieurs segments au sein d’une même trace. Il peut y avoir pas mal de métadonnées, notamment de mise en forme de la trace mais aussi de caractéristiques (biologiques, physiques, etc).</p> | |||
<a href="#hr-138" title="Lien vers cette section de la page"><hr id="hr-138" /></a> | |||
<blockquote lang="en"> | |||
<p>The fact is that we can’t rely on any single website to hold the whole world’s knowledge, because it can be corrupted sooner or later. The only solution is a distributed architecture, with many smaller websites connecting with each other and sharing information. This is where ActivityPub comes in, the protocol used by Mastodon, Lemmy, Peertube and many other federated social media projects.</p> | |||
<p>I have worked on Lemmy for the past four years, bringing it from a prototype to a fully functional Reddit alternative. I wrote the entire federation code and became very familiar with the protocol. <mark>I realized that the same technology easily be used to create a federated encyclopedia.</mark> As no one else took up such a project, I finally decided to do it on my own and create Ibis. Thanks to my previous experience with the tech stack and the ActivityPub library I created, I was able to complete a proof of concept in a relatively short time of four months.</p> | |||
<p><cite><em><a data-link-domain="ibis.wiki" href="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki" hreflang="en" | |||
title="Consultation de l’article (anglais)">Announcing Ibis, the federated Wikipedia Alternative</a> | |||
<a href="/david/cache/2024/691120ebac09f68413501b7f5daa2db7/" hreflang="en" | |||
data-tippy data-description="" | |||
data-source="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki" | |||
data-date="2024-03-26" | |||
data-favicon="" | |||
data-domain="ibis.wiki" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a></em></cite></p> | |||
</blockquote> | |||
<p>Wikipedia décentralisé, je dis oui ! Merci <a data-link-domain="alexsirac.com" href="https://alexsirac.com/activitypub-powered-wikis/" hreflang="fr" | |||
title="Consultation de l’article">Alex</a> | |||
<a href="/david/cache/2024/55c19feff784a41d2527b5f1589d931d/" hreflang="fr" | |||
data-tippy data-description="" | |||
data-source="https://alexsirac.com/activitypub-powered-wikis/" | |||
data-date="2024-03-26" | |||
data-favicon="https://alexsirac.com/wp-content/uploads/2023/01/cropped-cropped-portraitplante-32x32.webp" | |||
data-domain="alexsirac.com" | |||
><svg xmlns="http://www.w3.org/2000/svg" | |||
width="24" height="24" viewBox="0 0 24 24" fill="none" | |||
stroke="currentColor" stroke-width="2" stroke-linecap="square" | |||
stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle> | |||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path> | |||
<line x1="12" y1="17" x2="12.01" y2="17"></line> | |||
</svg> | |||
<span class="sr-only">[archive]</span></a>.</p> | |||
<p>C’est important pour <a href="/david/2024/02/29/" title="Wikipédia">diverses</a> <a href="/david/2024/03/07/" title="Sondages">raisons</a>.</p> | |||
<a href="#hr-139" title="Lien vers cette section de la page"><hr id="hr-139" /></a> | |||
<blockquote lang="en"> | |||
<p>I think it’s time to admit that my hobby is buying stuff for my new hobby.</p> | |||
<p><cite><em>Chris Hallbeck</em>, <a data-link-domain="mastodon.social" href="https://mastodon.social/@Chrishallbeck/112153815963114685">sur masto</a></cite></p> | |||
</blockquote> | |||
<p><em>Touché.</em></p> | |||
<nav> | |||
<p> | |||
<a href="/david/2024/apprentissage/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#apprentissage</a> | |||
<a href="/david/2024/technique/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#technique</a> | |||
<a href="/david/2024/web/" | |||
title="Liste de tous les articles 2024 associés à cette étiquette" | |||
rel="tag">#web</a> | |||
<a href="/david/2024/#tags" title="Liste de toutes les étiquettes 2024">tous ?</a> | |||
</p> | |||
</nav> | |||
<h2> | |||
<a href="/david/2024/03/13/" title="Lien permanent vers cet article">Sérendipité</a> <time datetime="2024-03-13">13 mars 2024</time> | |||
</h2> |
@@ -468,6 +468,7 @@ | |||
</style> | |||
<p>Liste des publications récentes en ordre anté-chronologique :</p> | |||
<p> | |||
<a href="/david/2024/03/26/" data-apprentissage data-technique data-web title="Étiquettes : apprentissage, technique, web.">GPX Viewer</a>, | |||
<a href="/david/2024/03/25/" data-dependance data-partage data-technique title="Étiquettes : dépendance, partage, technique.">Inclusion</a>, | |||
<a href="/david/2024/03/24/" data-courage data-lecture data-poesie title="Étiquettes : courage, lecture, poésie.">Cage</a>, | |||
<a href="/david/2024/03/23/" data-dependance data-psychologie data-technique title="Étiquettes : dépendance, psychologie, technique.">Intendant</a>, | |||
@@ -559,7 +560,7 @@ | |||
<a href="/david/2024/accompagnement/" data-tag="accompagnement" rel="tag">#accompagnement (2)</a>, | |||
<a href="/david/2024/adaptation/" data-tag="adaptation" rel="tag">#adaptation (3)</a>, | |||
<a href="/david/2024/addiction/" data-tag="addiction" rel="tag">#addiction (5)</a>, | |||
<a href="/david/2024/apprentissage/" data-tag="apprentissage" rel="tag">#apprentissage (15)</a>, | |||
<a href="/david/2024/apprentissage/" data-tag="apprentissage" rel="tag">#apprentissage (16)</a>, | |||
<a href="/david/2024/aventure/" data-tag="aventure" rel="tag">#aventure (4)</a>, | |||
<a href="/david/2024/cinema/" data-tag="cinema" rel="tag">#cinéma (1)</a>, | |||
<a href="/david/2024/commun/" data-tag="commun" rel="tag">#commun (9)</a>, | |||
@@ -598,9 +599,9 @@ | |||
<a href="/david/2024/psychologie/" data-tag="psychologie" rel="tag">#psychologie (11)</a>, | |||
<a href="/david/2024/solastalgia/" data-tag="solastalgia" rel="tag">#solastalgia (5)</a>, | |||
<a href="/david/2024/sport/" data-tag="sport" rel="tag">#sport (6)</a>, | |||
<a href="/david/2024/technique/" data-tag="technique" rel="tag">#technique (20)</a>, | |||
<a href="/david/2024/technique/" data-tag="technique" rel="tag">#technique (21)</a>, | |||
<a href="/david/2024/velo/" data-tag="velo" rel="tag">#vélo (1)</a>, | |||
<a href="/david/2024/web/" data-tag="web" rel="tag">#web (13)</a>. | |||
<a href="/david/2024/web/" data-tag="web" rel="tag">#web (14)</a>. | |||
</p> |
@@ -13,6 +13,124 @@ | |||
</author> | |||
<rights>Copyright (c) 2004-2024, David Larlet</rights> | |||
<entry xml:lang="fr"> | |||
<title type="html">GPX Viewer</title> | |||
<link href="https://larlet.fr/david/2024/03/26/" rel="alternate" type="text/html" /> | |||
<updated>2024-03-26T12:00:00+01:00</updated> | |||
<id>https://larlet.fr/david/2024/03/26/</id> | |||
<summary type="html"> | |||
<p><a href="https://larlet.fr/david/2024/03/25/">Suite de mes expérimentations</a> de la veille à la fois en cartographie et en <em>web components</em>. Je me suis demandé s’il était possible de faire un composant qui ne dépende pas de uMap pour afficher une trace GPX. Le fait d’avoir passé une heure à trouver la bonne <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">CSP</a> pour arriver à afficher une carte m’a d’une certaine manière motivé (c’était une journée&nbsp;galère).</p> | |||
<p>Il se trouve qu’il existe <a href="https://github.com/mpetazzoni/leaflet-gpx">leaflet-gpx</a> qui fait exactement ce que je voulais. Il ne me restait «&nbsp;plus qu’à&nbsp;» en faire un composant web, ce que j’avais déjà <a href="https://gitlab.com/umap-project/leaflet-webcomponents/">fait par ailleurs</a>&nbsp;:</p> | |||
<gpx-viewer data-height="500px" data-width="800px"> | |||
<p> | |||
Vous devriez voir s’afficher une carte de mon « Grand Tour de la forêt de Ouareau », | |||
qui contient <a data-gpx href="https://larlet.fr/static/david/2024/grand_tour_de_la_foret_de_ouareau.gpx">cette trace GPX</a>, | |||
centrée sur <span data-latitude>46.2117</span>, <span data-longitude>-73.9335</span> | |||
avec un zoom de <span data-zoom>12</span>. | |||
Il est probable que cela ne s’exécute pas dans un agrégateur par exemple. | |||
</p> | |||
</gpx-viewer> | |||
<script type="module"> | |||
import * as L from '/static/david/2024/leaflet.1.9.4/leaflet.1.9.4.js' | |||
window.L = L // Hack for leaflet-gpx, youpi. | |||
</script> | |||
<script type="module" nonce="oembed-web-component"> | |||
class GPXViewer extends HTMLElement { | |||
static tagName = 'gpx-viewer' | |||
static register(tagName, registry) { | |||
if(!registry && ('customElements' in globalThis)) { | |||
registry = globalThis.customElements | |||
} | |||
registry?.define(tagName || this.tagName, this) | |||
} | |||
#attachCSS(path) { | |||
const linkElement = document.createElement('link') | |||
linkElement.setAttribute('rel', 'stylesheet') | |||
linkElement.setAttribute('href', path) | |||
this.shadowRoot.appendChild(linkElement) | |||
} | |||
#createMapContainer() { | |||
const mapContainer = document.createElement('div') | |||
mapContainer.style.height = this.dataset.height | |||
mapContainer.style.width = this.dataset.width | |||
this.shadowRoot.appendChild(mapContainer) | |||
return mapContainer | |||
} | |||
#createMap(mapContainer) { | |||
const map = L.map(mapContainer).setView( | |||
[ | |||
this.querySelector('[data-latitude]').textContent, | |||
this.querySelector('[data-longitude]').textContent, | |||
], | |||
this.querySelector('[data-zoom]').textContent | |||
) | |||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { | |||
attribution: | |||
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', | |||
}).addTo(map) | |||
return map | |||
} | |||
#attachGPX(map) { | |||
const gpxUrl = this.querySelector('[data-gpx]').href | |||
new GPX(gpxUrl, { | |||
async: true, | |||
marker_options: { | |||
startIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-start.png', | |||
endIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-end.png', | |||
shadowUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-shadow.png' | |||
} | |||
}).on('loaded', (e) => { | |||
map.fitBounds(e.target.getBounds()) | |||
}).addTo(map) | |||
} | |||
constructor() { | |||
super() | |||
this.attachShadow({ mode: 'open' }) | |||
} | |||
async connectedCallback() { | |||
this.#attachCSS('/static/david/2024/leaflet.1.9.4/leaflet.css') | |||
const mapContainer = this.#createMapContainer() | |||
const map = this.#createMap(mapContainer) | |||
this.#attachGPX(map) | |||
} | |||
} | |||
import GPX from '/static/david/2024/leaflet-gpx.1.7.0-custom/gpx.1.7.0-custom.js' | |||
GPXViewer.register() | |||
</script> | |||
<p>Alors en fait, comme rien n’est jamais simple en JS, il a fallu que j’adapte le plugin qui n’était pas compatible avec les modules JS et que je fasse des galipettes pour que ça finisse par tomber en marche&nbsp;🤸. Cet écosystème est merveilleux (quel métier&#8239;!). J’ai quand même <a href="https://github.com/mpetazzoni/leaflet-gpx/issues/153">remonté le problème</a>.</p> | |||
<p>Au passage, inspiré par <a href="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent">le travail de Knut Hühne</a> sur la sémantique des données d’une carte, je me suis demandé si j’allais définir un <a href="https://schema.org/itinerary">itinerary</a> pour un <a href="https://schema.org/Trip">Trip</a> avec des <a href="https://schema.org/GeoCoordinates">GeoCoordinates</a> mais ça m’a fait tirer un fil beaucoup trop long. Je garde l’idée sous le coude <a href="https://umap-project.gitlab.io/leaflet-webcomponents/">pour mes propres recherches</a> dans le domaine cela&nbsp;dit.</p> | |||
<p>Un composant de plus, je ne suis pas très satisfait de celui-ci car j’avais l’espoir d’arriver à quelque chose de facilement réutilisable. J’aurais au moins commencé à mettre les doigts dans le XML du format GPX. Il est possible de définir plusieurs segments au sein d’une même trace. Il peut y avoir pas mal de métadonnées, notamment de mise en forme de la trace mais aussi de caractéristiques (biologiques, physiques, etc).</p> | |||
<hr /> | |||
<blockquote lang="en"> | |||
<p>The fact is that we can’t rely on any single website to hold the whole world’s knowledge, because it can be corrupted sooner or later. The only solution is a distributed architecture, with many smaller websites connecting with each other and sharing information. This is where ActivityPub comes in, the protocol used by Mastodon, Lemmy, Peertube and many other federated social media&nbsp;projects.</p> | |||
<p>I have worked on Lemmy for the past four years, bringing it from a prototype to a fully functional Reddit alternative. I wrote the entire federation code and became very familiar with the protocol. <mark>I realized that the same technology easily be used to create a federated encyclopedia.</mark> As no one else took up such a project, I finally decided to do it on my own and create Ibis. Thanks to my previous experience with the tech stack and the ActivityPub library I created, I was able to complete a proof of concept in a relatively short time of four&nbsp;months.</p> | |||
<p><cite><em><a href="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki">Announcing Ibis, the federated Wikipedia&nbsp;Alternative</a></em></cite></p> | |||
</blockquote> | |||
<p>Wikipedia décentralisé, je dis oui&#8239;! Merci <a href="https://alexsirac.com/activitypub-powered-wikis/">Alex</a>.</p> | |||
<p>C’est important pour <a href="https://larlet.fr/david/2024/02/29/">diverses</a> <a href="https://larlet.fr/david/2024/03/07/">raisons</a>.</p> | |||
<hr /> | |||
<blockquote lang="en"> | |||
<p>I think it’s time to admit that my hobby is buying stuff for my new&nbsp;hobby.</p> | |||
<p><cite><em>Chris Hallbeck</em>, <a href="https://mastodon.social/@Chrishallbeck/112153815963114685">sur&nbsp;masto</a></cite></p> | |||
</blockquote> | |||
<p><em>Touché.</em></p> | |||
<nav><p><a href="https://larlet.fr/david/2024/apprentissage/">#apprentissage</a> <a href="https://larlet.fr/david/2024/technique/">#technique</a> <a href="https://larlet.fr/david/2024/web/">#web</a></p></nav><hr/><p><a href="mailto:david@larlet.fr">Réagir ?</a></p></summary> | |||
</entry> | |||
<entry xml:lang="fr"> | |||
<title type="html">Inclusion</title> | |||
<link href="https://larlet.fr/david/2024/03/25/" rel="alternate" type="text/html" /> | |||
@@ -20,7 +138,7 @@ | |||
<id>https://larlet.fr/david/2024/03/25/</id> | |||
<summary type="html"> | |||
<p>Dans le cadre de <del><a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">manger ma propre bouffe pour chien</a></del> mon <a href="https://fr.wikipedia.org/wiki/Auto%C3%A9quipement">autoéquipement</a>, je voulais vérifier si l’implémentation de <a href="https://oembed.com/">oEmbed</a> dans <a href="https://umap-project.org/fr/">uMap</a> était utilisable… et il se trouve que ce n’était pas le cas avant aujourd’hui. Il y aurait encore des pistes d’améliorations mais au moins ça affiche une carte (si vous autorisez les requêtes externes en&nbsp;JS)&nbsp;:</p> | |||
<style type="text/css" nonce="oembed-default-style"> | |||
<style type="text/css"> | |||
o-embed { | |||
/* Size of the oembed + paragraph + margins. */ | |||
height: calc(300px + 1rem + 3rem); | |||
@@ -34,7 +152,7 @@ | |||
</p> | |||
</o-embed> | |||
<script type="module" nonce="oembed-web-component"> | |||
<script type="module"> | |||
class OEmbed extends HTMLElement { | |||
static tagName = 'o-embed' | |||
@@ -1179,30 +1297,4 @@ Et je sais enfin qui je&nbsp;suis…</p> | |||
<nav><p><a href="https://larlet.fr/david/2024/apprentissage/">#apprentissage</a> <a href="https://larlet.fr/david/2024/processus/">#processus</a> <a href="https://larlet.fr/david/2024/web/">#web</a></p></nav><hr/><p><a href="mailto:david@larlet.fr">Réagir ?</a></p></summary> | |||
</entry> | |||
<entry xml:lang="fr"> | |||
<title type="html">Responsabilité</title> | |||
<link href="https://larlet.fr/david/2024/02/22/" rel="alternate" type="text/html" /> | |||
<updated>2024-02-22T12:00:00+01:00</updated> | |||
<id>https://larlet.fr/david/2024/02/22/</id> | |||
<summary type="html"> | |||
<blockquote> | |||
<p>Il n’y a pas vraiment de conclusion à tout ça. Nous avons été témoins, par le petit bout de la lorgnette, du tout début de cette histoire&#8239;; nous avons imaginé un instant <mark>avoir l’opportunité, ou le devoir,</mark> d’y faire quelque chose. Finalement, nous n’avons jamais vraiment donné notre&nbsp;avis.&nbsp;🤷</p> | |||
<p><cite><em><a href="https://www.codeursenliberte.fr/blog/tous_anti_covid/">TousAntiCovid&nbsp;: vu depuis Codeureuses en&nbsp;Liberté</a></em></cite></p> | |||
</blockquote> | |||
<p>Je me souviens vaguement de certaines discussions à ce sujet. D’un côté l’orgueil de penser que l’on pouvait faire mieux, de l’autre la culpabilité de pouvoir être associé à un tel projet. Et au milieu, une épidémie et le sentiment de pouvoir faire des choses avec nos compétences et notre position au sein de l’État français. Un mois plus tard, je m’embarquais dans <a href="https://larlet.fr/david/2020/05/26/">une autre aventure</a>, plus proche de mon&nbsp;style.</p> | |||
<p>Deux années, 23&nbsp;millions de visiteur·euses uniques et 60&nbsp;millions de pages vues plus tard, j’ai l’impression d’avoir été utile à mon échelle. Merci à la D<a href="https://ronan.amicel.net/">R</a><a href="https://www.ut7.fr/">R</a>u<a href="https://www.maiwann.net/">M</a><a href="https://fr.linkedin.com/in/m%C3%A9lodiedahi">M</a> team pour cette&nbsp;période.</p> | |||
<hr /> | |||
<blockquote lang="en"> | |||
<p>A custom element implementation of the Stack is provided for&nbsp;download.</p> | |||
<p><cite><em><a href="https://every-layout.dev/layouts/stack/#the-component">The Stack: Every&nbsp;Layout</a></em></cite></p> | |||
</blockquote> | |||
<p>Utiliser des <em>Web Components</em> pour appliquer un style, je n’y avais pas encore&nbsp;songé.</p> | |||
<hr /> | |||
<p>Hier soir, j’ai regardé <a href="https://en.wikipedia.org/wiki/The_Deepest_Breath">The Deepest Breath</a>. J’ai mal dormi. D’autant que, plus tôt dans la journée, on m’avait transmis <a href="https://www.youtube.com/watch?v=LFLl9q-bEgk">ce retour d’expérience</a>, flippant (et discutable) à plus d’un&nbsp;titre.</p> | |||
<p>La récupération de ma cheville suit son cours, ça va être long, il manque du muscle. Je me sens suffisamment en confiance pour tenter une sortie hivernale en camping cette fin de semaine, on va voir ce que ça donne. Il va faire <q lang="fr_CA">frête</q> et ça laisse peu de place aux&nbsp;incapacités…</p> | |||
<nav><p><a href="https://larlet.fr/david/2024/decision/">#décision</a> <a href="https://larlet.fr/david/2024/experience/">#expérience</a> <a href="https://larlet.fr/david/2024/partage/">#partage</a></p></nav><hr/><p><a href="mailto:david@larlet.fr">Réagir ?</a></p></summary> | |||
</entry> | |||
</feed> |