Salut à tous ! Aujourd’hui, je voudrais partager avec vous une création que j’ai réalisée dans le cadre d’un projet de collection intitulé « Urbanographie ». Cette collection, qui est encore en cours de développement, explore les différentes façons de représenter l’environnement urbain. Dans ce billet, je vais vous expliquer comment j’ai créé une représentation esquissée de la ville de Rabat, en utilisant des données provenant d’OpenStreetMap et des bibliothèques P5.js.
L’idée de ce projet est venue d’un défi de code créatif intitulé « WHERE I LIVE » lancé par Raphaël de Courville sur openprocessing.org. Pour réaliser cette carte esquissée, j’ai utilisé plusieurs bibliothèques et outils, notamment Hershey, Scribble, et OpenStreetMap (OSM).
Commençons par les données. J’ai utilisé l’API Overpass d’OSM pour récupérer les données des routes et des frontières terrestres de Rabat. Ces données sont ensuite converties en un format GeoJSON, qui est plus facile à manipuler avec p5.js.
Une fois les données chargées, je les dessine à l’aide de la bibliothèque Scribble, qui permet de créer un effet d’esquisse pour les lignes tracées. Cela donne un aspect dessiné à la main à la carte, ce qui ajoute une touche artistique à la représentation de la ville.
Pour le texte de la légende et les autres éléments de la carte, j’ai utilisé la bibliothèque Hershey, qui permet de dessiner du texte avec différentes polices de caractères vectorielles en une ligne. Cela confère un aspect cohérent à l’ensemble du dessin et permet par la suite de dessiner le texte plus facilement avec le plotter.
Enfin, j’ai ajouté une échelle qui indique les distances en kilomètres. Pour cela, j’ai calculé la proportion de la largeur et de la hauteur de la carte représentant 1 km, puis j’ai dessiné des rectangles pour indiquer les distances de 250 m, 500 m et 1000 m.
Code source de l’esquisse
const MIN_LAT = 34.0171;
const MIN_LON = -6.8559;
const MAX_LAT = 34.0454;
const MAX_LON = -6.8228;
let roadsData;
let landBoundariesData;
let scribble;
function preload() {
let overpassAPIUrl = "https://overpass-api.de/api/interpreter?data=";
let overpassRoadsQuery = `[out:json][timeout:25];(way["highway"](${MIN_LAT},${MIN_LON},${MAX_LAT},${MAX_LON}););out body;>;out skel qt;`;
let overpassLandBoundariesQuery = `[out:json][timeout:25];(way["natural"](${MIN_LAT},${MIN_LON},${MAX_LAT},${MAX_LON});way["waterway"="riverbank"](${MIN_LAT},${MIN_LON},${MAX_LAT},${MAX_LON}););out body;>;out skel qt;`;
let encodedRoadsQuery = encodeURIComponent(overpassRoadsQuery);
let encodedLandBoundariesQuery = encodeURIComponent(overpassLandBoundariesQuery);
let roadsRequestUrl = overpassAPIUrl + encodedRoadsQuery;
let landBoundariesRequestUrl = overpassAPIUrl + encodedLandBoundariesQuery;
loadJSON(roadsRequestUrl, parseRoadsData, 'json', function() {
console.log('Loaded roads data');
});
loadJSON(landBoundariesRequestUrl, parseLandBoundariesData, 'json', function() {
console.log('Loaded land boundaries data');
});
}
function setup() {
createCanvas(800, 800, SVG);
stroke(0);
strokeWeight(1);
scribble = new Scribble();
}
function draw() {
if (!roadsData || !landBoundariesData) {
background(255, 0, 0);
return;
}
background(255);
drawLandBoundaries();
stroke(0);
strokeWeight(1);
drawRoads();
drawLegend();
}
function drawRoads() {
for (let feature of roadsData.features) {
const roadCoords = feature.geometry.coordinates;
for (let i = 0; i < roadCoords.length - 1; i++) {
const x1 = map(roadCoords[i][0], MIN_LON, MAX_LON, 0, width);
const y1 = map(roadCoords[i][1], MIN_LAT, MAX_LAT, height, 0);
const x2 = map(roadCoords[i + 1][0], MIN_LON, MAX_LON, 0, width);
const y2 = map(roadCoords[i + 1][1], MIN_LAT, MAX_LAT, height, 0);
scribble.scribbleLine(x1, y1, x2, y2);
}
}
}
function drawLandBoundaries() {
stroke(0, 0, 0);
strokeWeight(3);
for (let feature of landBoundariesData.features) {
const boundaryCoords = feature.geometry.coordinates;
for (let i = 0; i < boundaryCoords.length - 1; i++) {
const x1 = map(boundaryCoords[i][0], MIN_LON, MAX_LON, 0, width);
const y1 = map(boundaryCoords[i][1], MIN_LAT, MAX_LAT, height, 0);
const x2 = map(boundaryCoords[i + 1][0], MIN_LON, MAX_LON, 0, width);
const y2 = map(boundaryCoords[i + 1][1], MIN_LAT, MAX_LAT, height, 0);
scribble.scribbleLine(x1, y1, x2, y2);
}
}
}
function parseRoadsData(osmData) {
roadsData = parseOsmData(osmData);
}
function parseLandBoundariesData(osmData) {
landBoundariesData = parseOsmData(osmData);
}
function parseOsmData(osmData) {
let geoJSONData = {
"type": "FeatureCollection",
"features": []
};
for (let element of osmData.elements) {
if (element.type === "way" && element.nodes) {
let feature = {
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": []
}
};
for (let node of element.nodes) {
let osmNode = osmData.elements.find(e => e.type === "node" && e.id === node);
if (osmNode) {
feature.geometry.coordinates.push([osmNode.lon, osmNode.lat]);
}
}
geoJSONData.features.push(feature);
}
}
return geoJSONData;
}
function drawLegend() {
fill(255, 255, 255, 200);
scribble.scribbleRect(20 + 150, 20 + 70, 300, 140);
const latDist = (MAX_LAT - MIN_LAT) * 111; // 1 degré de latitude est environ égal à 111 km
const lonDist = (MAX_LON - MIN_LON) * Math.cos((MIN_LAT * Math.PI) / 180) * 111;
const kmWidthProportion = width / lonDist;
const kmHeightProportion = height / latDist;
stroke(0);
strokeWeight(1);
noFill();
scribble.scribbleRect(40 + kmWidthProportion / 8, 125 + 5, kmWidthProportion / 4, 10);
scribble.scribbleRect(40 + kmWidthProportion / 4, 125 + 5, kmWidthProportion / 2, 10);
scribble.scribbleRect(40 + kmWidthProportion / 2, 125 + 5, kmWidthProportion, 10);
strokeWeight(1);
noFill()
scale(1);
translate(30, 50);
P5.hershey.putText("Rabat, Morocco", { cmap: FONT_HERSHEY.SIMPLEX });
pop();
scale(0.4) ;
push();
translate(30, 160);
textSize(4);
strokeWeight(1);
noFill()
P5.hershey.putText("0", { cmap: FONT_HERSHEY.SIMPLEX });
pop();
push();
translate(150, 160);
strokeWeight(1);
noFill()
P5.hershey.putText("250m", { cmap: FONT_HERSHEY.SIMPLEX });
pop();
push();
translate(330, 160);
strokeWeight(1);
noFill()
P5.hershey.putText("500m", { cmap: FONT_HERSHEY.SIMPLEX });
pop();
push();
translate(600, 160);
strokeWeight(1);
noFill()
P5.hershey.putText("1000m", { cmap: FONT_HERSHEY.SIMPLEX });
scale(1);
pop();
}
function keyPressed() {
if (key === 's' || key === 'S') {
save("rabat_map.svg");
}
}
Librairies à incorporer
<script language="javascript" type="text/javascript" src="libraries/p5.scribble.js"></script>
<script language="javascript" type="text/javascript" src="libraries/p5.hershey.data.js"></script>
<script language="javascript" type="text/javascript" src="libraries/p5.hershey.js"></script>
<script src="https://unpkg.com/[email protected]/dist/p5.svg.js"></script>
Possibles améliorations
Je vous invite à améliorer ce projet et à pousser plus loin l’exploration artistique. Il existe plusieurs idées intéressantes et pistes d’amélioration que vous pourriez envisager. Tout d’abord, vous pourriez ajouter une interface utilisateur simple pour permettre aux utilisateurs de saisir manuellement les coordonnées géographiques (latitude et longitude) de leur choix, rendant ainsi le projet plus interactif et personnalisable.
Pour enrichir l’expérience visuelle, vous pourriez expérimenter avec différentes techniques de rendu, telles que l’utilisation de couleurs variables en fonction de l’altitude, de la densité de population ou des types de routes, ajoutant ainsi une dimension supplémentaire à la représentation. Vous pourriez également jouer avec la transparence et les modes de fusion pour créer des effets de superposition intrigants.
Une autre idée serait d’ajouter la possibilité de choisir parmi différents styles d’esquisse ou d’adapter le niveau de détail en fonction des préférences de l’utilisateur.
Enfin, n’hésitez pas à partager votre code et à encourager les autres à le modifier et à jouer avec. En ouvrant votre travail à la communauté, vous favoriserez la collaboration et l’innovation, permettant à de nouvelles idées de voir le jour et à votre projet de continuer à évoluer.
Voilà, j’espère que vous avez apprécié cette petite incursion dans le monde de la cartographie artistique ! N’hésitez pas à essayer ces techniques vous-même et à partager vos propres créations. Et n’oubliez pas de suivre ce blog pour rester informé des nouveautés de la collection « Urbanographie » !
Très intéressant ton projet! Tu devras nous faire Casa
Dans le projet Urbanographie, Casablanca aura la part du lion. C’est la ville que j’ai le plus travaillé !
Chapeau chef, très intéressant j’ai vraiment apprécié le rendu final. Je vous remercie pour cette incursion et d’avoir partagé avec nous le code source hâte de voir d’autres créations « Urbanographie ».
Merci Mjad ! J’espère que les prochaines création te plairont aussi !