diff --git a/971.php b/971.php new file mode 100644 index 0000000..ec20ae6 --- /dev/null +++ b/971.php @@ -0,0 +1,26 @@ + + + + JWE - KAT + + + + +

Guadeloupe (971)

+ +
+

Correct ✅ : 0

+

Faux ❌ : 0

+
+ +
+

Trouve la commune

+

...

+
+ +
+ + + + + diff --git a/972.php b/972.php new file mode 100644 index 0000000..11ea0b4 --- /dev/null +++ b/972.php @@ -0,0 +1,26 @@ + + + + JWE - KAT + + + + +

Martinique (972)

+ +
+

Correct ✅ : 0

+

Faux ❌ : 0

+
+ +
+

Trouve la commune

+

...

+
+ +
+ + + + + diff --git a/973.php b/973.php new file mode 100644 index 0000000..d43cebe --- /dev/null +++ b/973.php @@ -0,0 +1,26 @@ + + + + JWE - KAT + + + + +

Guyane (973)

+ +
+

Correct ✅ : 0

+

Faux ❌ : 0

+
+ +
+

Trouve la commune

+

...

+
+ +
+ + + + + diff --git a/974.php b/974.php new file mode 100644 index 0000000..39fe93e --- /dev/null +++ b/974.php @@ -0,0 +1,26 @@ + + + + JWE - KAT + + + + +

La Réunion (974)

+ +
+

Correct ✅ : 0

+

Faux ❌ : 0

+
+ +
+

Trouve la commune

+

...

+
+ +
+ + + + + diff --git a/css/map.css b/css/map.css new file mode 100644 index 0000000..d055a20 --- /dev/null +++ b/css/map.css @@ -0,0 +1,42 @@ +body { + margin: 0; + padding: 0; +} + +html, body, #map { + height: 90%; +} + +#title,div { + text-align: center; +} + +.popup-ko .maplibregl-popup-content { + background-color: rgba(255, 0, 0, 0.9); + border-radius: 10px; + box-shadow: 0px 0px 0px 2px rgb(0, 0, 0); +} + +.popup-ok .maplibregl-popup-content { + background-color: rgba(0, 255, 34, 0.9); + border-radius: 10px; + box-shadow: 0px 0px 0px 2px rgb(0, 0, 0); +} + +.maplibregl-popup-tip { + display: none !important; +} + +@media (max-width: 600px) { + body { + font-size: 100%; + } + + h1 { + font-size: 5.5vw; + } + + ul, li { + padding: 3vw; + } +} diff --git a/js/map.js b/js/map.js new file mode 100644 index 0000000..d1b8d5f --- /dev/null +++ b/js/map.js @@ -0,0 +1,383 @@ +/** + * js/map.js + */ + +/** + * Récupérer le nom du fichier actuel (ex: 971.php) et en extraire le code du département (ex: 971). + */ +const currentFileName = window.location.pathname.split('/').pop() +const codeDepartement = currentFileName.split('.')[0] + +/** + * Définir le centre , le niveau de zoom et le style de la carte en fonction du code du département. + */ +let centerCoordinates +let zoomLevel +let styleFileName + +switch (codeDepartement) { + case '971': + centerCoordinates = [-61.4167, 16.25] // Guadeloupe + zoomLevel = 9 + styleFileName = 'ortho.json' + break + case '972': + centerCoordinates = [-61.01635, 14.60285] // Martinique + zoomLevel = 9.5 + styleFileName = 'ortho.json' + break + case '973': + centerCoordinates = [-53.1258, 3.9339] // Guyane française + zoomLevel = 7 + styleFileName = 'ortho-jwe.json' + break + case '974': + centerCoordinates = [55.52905, -21.13014] // La Réunion + styleFileName = 'ortho.json' + zoomLevel = 9.5 + break + default: + centerCoordinates = [-61.4167, 16.25] // Par défaut (Guadeloupe) + styleFileName = 'ortho.json' + zoomLevel = 9.5 +} + +/** + * Initialise et configure la carte + */ +const map = new maplibregl.Map({ + container: 'map', + style: `./kat/styles/${styleFileName}`, + center: centerCoordinates, + zoom: zoomLevel +}) + +if (codeDepartement !== '973') { + /** + * Désactive le zoom, le double-clic, le déplacement de la carte pour les cartes autres que celle la Guyane. + */ + map.scrollZoom.disable() + map.doubleClickZoom.disable() + map.dragPan.disable() +} else { + /** + * Ajoute les contrôles de navigation à la carte, et définit les niveaux de zoom minimum et maximum pour la Guyane. + */ + map.addControl(new maplibregl.NavigationControl({showCompass: false})) + map.setMinZoom(7) + map.setMaxZoom(9.5) +} + +/** + * Définit les décalages des popups en fonction de leur position. + */ +const popupOffsets = { + 'top': [0, 0], + 'top-left': [0, 0], + 'top-right': [0, 0], + 'bottom': [0, -30], + 'bottom-left': [0, -50], + 'bottom-right': [0, -50], + 'left': [25, -25], + 'right': [-25, -25] +} + +/** + * Référence aux éléments HTML pour afficher le score. + */ +const ok = document.getElementById('OK') +const ko = document.getElementById('KO') + +/** + * État actuel de la commune survolée. + */ +let hoveredStateId = null + +/** + * Référence à la commune actuellement sélectionnée pour trouver. + */ +let currentCommune = null + +/** + * Ensemble des codes des communes déjà trouvées correctement. + */ +const correctAnswers = new Set() + +/** + * Récupère les propriétés des communes depuis un fichier GeoJSON en fonction du code du département. + * + * @async + * @param {string} codeDepartement - Numéro du département. + * @returns {Promise} Promesse résolue avec les propriétés des communes. + */ +async function getFile(codeDepartement) { + try { + const response = await fetch(`./kat/data/${codeDepartement}/contours-communes.geojson`) + if (!response.ok) { + throw new Error(`Échec du chargement du fichier JSON : ${response.statusText}`) + } + + data = await response.json() + return data.features.map(({properties}) => properties) + } catch (error) { + console.error('Erreur:', error) + throw new Error(error) + } +} + +/** + * Récupère les coordonnées du centre de la commune depuis le fichier "centres.json" en fonction du code du département. + * + * @param {string} codeDepartement - Numéro du département. + * @param {string} communeCode - Code de la commune. + * @returns {Promise} Promesse résolue avec les coordonnées du centre de la commune. + */ +async function getCommuneCenterCoordinates(codeDepartement, communeCode) { + try { + // Utiliser fetch pour lire le fichier "centres.json" + const response = await fetch(`./kat/data/${codeDepartement}/centres.json`) + if (!response.ok) { + throw new Error(`Échec du chargement du fichier JSON : ${response.statusText}`) + } + + const centerData = await response.json() + + // Vérifier si le code de la commune existe dans les données + if (communeCode in centerData) { + const coordinates = centerData[communeCode] + return coordinates + } else { + throw new Error(`Aucune donnée de centre trouvée pour la commune : ${communeCode}`) + } + } catch (error) { + console.error('Erreur lors de la récupération des coordonnées du centre de la commune :', error) + throw new Error(error) + } +} + + +/** + * Met à jour le score en fonction de la réponse. + * + * @param {boolean} isCorrect - Indique si la réponse est correcte. + */ +function updateScore(isCorrect) { + if (isCorrect) { + ok.innerText = Number.parseInt(ok.innerText, 10) + 1 + } else { + ko.innerText = Number.parseInt(ko.innerText, 10) + 1 + } +} + +/** + * Sélectionne une commune au hasard parmi celles qui n'ont pas encore été trouvées. + * + * @param {Array} communes - Liste de toutes les communes. + * @returns {Object|null} Retourne la commune sélectionnée ou null si toutes les communes ont été trouvées. + */ +function pickRandomCommune(communes) { + const communesRestantes = communes.filter(commune => !correctAnswers.has(commune.code)) + + if (communesRestantes.length === 0) { + return null + } + + const index = Math.floor(Math.random() * communesRestantes.length) + return communesRestantes[index] +} + +/** + * Définit la commune actuelle à trouver et met à jour l'affichage. + * + * @param {Object} commune - La commune à définir comme actuelle. + */ +function setCommune(commune) { + if (currentCommune) { + map.setFeatureState( + {source: 'communes', id: currentCommune.code}, + {current: false} + ) + } + + currentCommune = commune + document.getElementById('commune').innerText = commune.nom + + map.setFeatureState( + {source: 'communes', id: commune.code}, + {current: true} + ) +} + +/** + * Vérifie si le code de la commune cliquée correspond à la commune actuelle à trouver. + * + * @param {string} communeCode - Code de la commune à vérifier. + * @returns {boolean} Résultat de la vérification. + */ +function checkAnswer(communeCode) { + return currentCommune && communeCode === currentCommune.code +} + +/** + * Code pour la gestion des événements de la carte (load, mousemove, mouseleave, click). + */ +map.on('load', async () => { + const communes = await getFile(codeDepartement) + + /** + * Ajouter une source de données pour les communes. + */ + map.addSource('communes', { + 'type': 'geojson', + 'data': `./kat/data/${codeDepartement}/contours-communes.geojson`, + 'promoteId': 'code' + }) + + /** + * Ajouter une couche de remplissage pour les communes. + */ + map.addLayer({ + 'id': 'drom', + 'type': 'fill', + 'source': 'communes', + 'layout': {}, + 'paint': { + 'fill-color': '#1F51FF', + 'fill-opacity': [ + 'case', + ['boolean', ['feature-state', 'hover'], false], + 0.8, + 0 + ] + } + }) + + /** + * Ajouter une couche de remplissage pour les communes (2ème couche), en prenant en compte les réponses correctes et incorrectes. + */ + map.addLayer({ + 'id': 'drom-2', + 'type': 'fill', + 'source': 'communes', + 'layout': {}, + 'paint': { + 'fill-color': [ + 'case', + ['boolean', ['feature-state', 'correct'], false], '#00FF00', + ['boolean', ['feature-state', 'incorrect'], false], '#FF0000', + 'rgba(0,0,0,0)' + ], + 'fill-opacity': [ + 'case', + ['boolean', ['feature-state', 'hover'], false], 0.5, + 0.8 + ] + } + }) + + /** + * Gère l'evenement mousemove (quand la souris survole la commune) + */ + map.on('mousemove', 'drom', e => { + map.getCanvas().style.cursor = 'pointer' + if (e.features.length > 0) { + if (hoveredStateId) { + map.setFeatureState( + {source: 'communes', id: hoveredStateId}, + {hover: false} + ) + } + hoveredStateId = e.features[0].id + map.setFeatureState( + {source: 'communes', id: hoveredStateId}, + {hover: true} + ) + } + }) + + /** + * Gère l'evenement mouseleave (quand la souris quitte la commune) + */ + map.on('mouseleave', 'drom', () => { + map.getCanvas().style.cursor = '' + if (hoveredStateId) { + map.setFeatureState( + {source: 'communes', id: hoveredStateId}, + {hover: false} + ) + } + hoveredStateId = null + }) + + /** + * Gère l'evenement click (quand on clique sur la commune) + */ + map.on('click', 'drom', async e => { + if (e.features.length > 0) { + const clickedCommuneCode = e.features[0].id + const clickedCommuneNom = e.features[0].properties.nom + const isCorrect = checkAnswer(clickedCommuneCode) + + if (isCorrect) { + updateScore(true) + const centerCoordinates = await getCommuneCenterCoordinates(codeDepartement, clickedCommuneCode) + + const popup = new maplibregl.Popup({offset: popupOffsets, closeButton: false, className: 'popup-ok'}) + .setLngLat([centerCoordinates[0], centerCoordinates[1]]) + .setHTML(`

Bravo, c'est ${clickedCommuneNom}

`) + .setMaxWidth('200px') + .addTo(map) + + setTimeout(() => { + popup.remove() + }, 1500) + + correctAnswers.add(clickedCommuneCode) + communes.forEach(commune => { + map.setFeatureState( + {source: 'communes', id: commune.code}, + {incorrect: false} + ) + }) + map.setFeatureState( + {source: 'communes', id: clickedCommuneCode}, + {correct: true, current: false} + ) + + const newCommune = pickRandomCommune(communes) + if (newCommune) { + setCommune(newCommune) + } else { + const totalAnswers = Number.parseInt(ok.innerText, 10) + Number.parseInt(ko.innerText, 10) + const percentageCorrectAnswers = (Number.parseInt(ok.innerText, 10) / totalAnswers) * 100 + + alert(`Fin du jeu, ${Math.round(percentageCorrectAnswers)} % de bonnes réponses !`, ) + setTimeout(() => { + location.reload() + }, 1500) + } + } else { + updateScore(false) + const centerCoordinates = await getCommuneCenterCoordinates(codeDepartement, clickedCommuneCode) + + const popup = new maplibregl.Popup({offset: popupOffsets, closeButton: false, className: 'popup-ko'}) + .setLngLat([centerCoordinates[0], centerCoordinates[1]]) + .setHTML(`

Faux, c'est ${clickedCommuneNom}

`) + .setMaxWidth('200px') + .addTo(map) + + setTimeout(() => { + popup.remove() + }, 1500) + + map.setFeatureState( + {source: 'communes', id: clickedCommuneCode}, + {incorrect: true} + ) + } + } + }) + + const commune = pickRandomCommune(communes) + setCommune(commune) +})