From 27ab9b28ddc4015e37ee518f8d4eb5ecb87ce477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81my=20Godet?= Date: Wed, 18 Dec 2024 12:03:45 +0100 Subject: [PATCH 1/3] feat(Geolocation): Added search establishment with our dataset --- src/consts/datasets.json | 3 +- src/services/pronote/dataset_geolocation.ts | 35 +++++++++++++++++++ .../login/pronote/PronoteGeolocation.tsx | 3 +- .../login/pronote/PronoteInstanceSelector.tsx | 22 ++++++++++-- 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 src/services/pronote/dataset_geolocation.ts diff --git a/src/consts/datasets.json b/src/consts/datasets.json index 81b722416..e73ba898a 100644 --- a/src/consts/datasets.json +++ b/src/consts/datasets.json @@ -1,5 +1,6 @@ { "kofi-supporters": "https://raw.githubusercontent.com/PapillonApp/datasets/main/kofi-supporters.json", "changelog": "https://raw.githubusercontent.com/PapillonApp/datasets/main/updates/[version].json", - "illustrations": "https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/illustrations/index.json" + "illustrations": "https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/illustrations/index.json", + "establishment": "https://raw.githubusercontent.com/PapillonApp/datasets/main/establishment/[postcode].json" } \ No newline at end of file diff --git a/src/services/pronote/dataset_geolocation.ts b/src/services/pronote/dataset_geolocation.ts new file mode 100644 index 000000000..7a73b9aa1 --- /dev/null +++ b/src/services/pronote/dataset_geolocation.ts @@ -0,0 +1,35 @@ +import pronote from "pawnote"; +import datasets from "../../consts/datasets.json"; + +const getInstancesFromDataset = async (longitude: number, latitude: number):Promise => { + let adress_api_fetch = await fetch(`https://api-adresse.data.gouv.fr/reverse/?lon=${longitude}&lat=${latitude}&limit=1`); + try { + let adress_api = await adress_api_fetch.json(); + if (adress_api.features.length === 0) { + return []; + } + const postcode = adress_api.features[0].properties.postcode; + let instances_fetch = await fetch(datasets.establishment.replace("[postcode]", postcode)); + try { + let instances = await instances_fetch.json(); + return instances.map((instance: any) => { + let distance = Math.sqrt( + Math.pow(instance.lat - latitude, 2) + Math.pow(instance.long - longitude, 2) + ); + return { + name: instance.name.toUpperCase(), + url: instance.url, + distance: distance, + longitude: instance.long, + latitude: instance.lat, + }; + }); + } catch (error) { + return []; + } + } catch (error) { + console.error(error); + } +}; + +export default getInstancesFromDataset; \ No newline at end of file diff --git a/src/views/login/pronote/PronoteGeolocation.tsx b/src/views/login/pronote/PronoteGeolocation.tsx index c61bacaed..8276c6c76 100644 --- a/src/views/login/pronote/PronoteGeolocation.tsx +++ b/src/views/login/pronote/PronoteGeolocation.tsx @@ -70,7 +70,8 @@ const PronoteGeolocation: Screen<"PronoteGeolocation"> = ({ navigation }) => { style={[styles.terms_text, { color: colors.text + "59" }]} > Votre position est nécessaire pour trouver les instances PRONOTE à proximité. - Elle n'est pas stockée et ne sera pas partagée. + Elle sera envoyée à Pronote et à l'api adresse du gouvernement pour trouver les établissements. + Elle n'est pas stockée. ); diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index b73d6cf6a..ac0d26092 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -14,6 +14,7 @@ import { useTheme } from "@react-navigation/native"; import { Search, X, GraduationCap, } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { Audio } from "expo-av"; +import getInstancesFromDataset from "@/services/pronote/dataset_geolocation"; const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ route: { params }, @@ -74,15 +75,30 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ }; }, []); - const playSound = () => sound?.replayAsync(); - useEffect(() => { if (params) { void async function () { - const instances = await pronote.geolocation(params); + const dataset_instances = await getInstancesFromDataset(params.longitude, params.latitude); + const pronote_instances = await pronote.geolocation(params); + + // On calcule la distance entre les instances et l'utilisateur. + let instances = pronote_instances.map((instance) => ({ + ...instance, + distance: Math.sqrt( + Math.pow(instance.latitude - params.latitude, 2) + Math.pow(instance.longitude - params.longitude, 2) + ), + })); + // On limite à 20 instances. instances.splice(20); + // On ajoute les instances trouvées par l'API adresse. + instances.push(...dataset_instances); + + // On trie par distance. + instances.sort((a, b) => a.distance - b.distance); + + // On met à jour les instances. setInstances(instances); setOriginalInstances(instances); }(); From b0058e362cf0d50c1693d3ca49badfa80f55e897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81my=20Godet?= Date: Wed, 18 Dec 2024 13:13:08 +0100 Subject: [PATCH 2/3] feat(Geolocation): Added distance in kilometers --- src/consts/datasets.json | 2 +- src/services/pronote/dataset_geolocation.ts | 37 ++++++++-- .../login/pronote/PronoteInstanceSelector.tsx | 71 ++++++++++++++++--- 3 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/consts/datasets.json b/src/consts/datasets.json index e73ba898a..ec7ec570a 100644 --- a/src/consts/datasets.json +++ b/src/consts/datasets.json @@ -2,5 +2,5 @@ "kofi-supporters": "https://raw.githubusercontent.com/PapillonApp/datasets/main/kofi-supporters.json", "changelog": "https://raw.githubusercontent.com/PapillonApp/datasets/main/updates/[version].json", "illustrations": "https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/illustrations/index.json", - "establishment": "https://raw.githubusercontent.com/PapillonApp/datasets/main/establishment/[postcode].json" + "establishment": "https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/establishment/[postcode].json" } \ No newline at end of file diff --git a/src/services/pronote/dataset_geolocation.ts b/src/services/pronote/dataset_geolocation.ts index 7a73b9aa1..b5d33e760 100644 --- a/src/services/pronote/dataset_geolocation.ts +++ b/src/services/pronote/dataset_geolocation.ts @@ -1,7 +1,7 @@ import pronote from "pawnote"; import datasets from "../../consts/datasets.json"; -const getInstancesFromDataset = async (longitude: number, latitude: number):Promise => { +const getInstancesFromDataset = async (longitude: number, latitude: number): Promise => { let adress_api_fetch = await fetch(`https://api-adresse.data.gouv.fr/reverse/?lon=${longitude}&lat=${latitude}&limit=1`); try { let adress_api = await adress_api_fetch.json(); @@ -9,26 +9,53 @@ const getInstancesFromDataset = async (longitude: number, latitude: number):Prom return []; } const postcode = adress_api.features[0].properties.postcode; + let instances_fetch = await fetch(datasets.establishment.replace("[postcode]", postcode)); try { let instances = await instances_fetch.json(); + + const calculateHaversineDistance = (lat1: number, lon1: number, lat2: number, lon2: number): number => { + const toRadians = (degrees: number) => degrees * (Math.PI / 180); + const R = 6371; // Earth's radius in kilometers + + const dLat = toRadians(lat2 - lat1); + const dLon = toRadians(lon2 - lon1); + + const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * + Math.sin(dLon / 2) * Math.sin(dLon / 2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + return R * c; + }; + return instances.map((instance: any) => { - let distance = Math.sqrt( - Math.pow(instance.lat - latitude, 2) + Math.pow(instance.long - longitude, 2) + const distance = calculateHaversineDistance( + latitude, + longitude, + instance.lat, + instance.long ); + + console.log("User location:", { latitude, longitude }); + console.log("Instance location:", { latitude: instance.lat, longitude: instance.long }); + console.log("Calculated distance:", distance); + return { name: instance.name.toUpperCase(), url: instance.url, - distance: distance, + distance, longitude: instance.long, latitude: instance.lat, }; }); } catch (error) { + console.error("Error fetching instances:", error); return []; } } catch (error) { - console.error(error); + console.error("Error fetching address:", error); + return []; } }; diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index ac0d26092..866a964b2 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -1,6 +1,15 @@ import React, { useEffect, useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { TextInput, TouchableOpacity, View, StyleSheet, ActivityIndicator, Keyboard, KeyboardEvent } from "react-native"; +import { + TextInput, + TouchableOpacity, + View, + StyleSheet, + ActivityIndicator, + Keyboard, + KeyboardEvent, + Text +} from "react-native"; import pronote from "pawnote"; import Reanimated, { LinearTransition, FlipInXDown, FadeInUp, FadeOutUp, ZoomIn, ZoomOut, Easing, ZoomInEasyDown } from "react-native-reanimated"; import determinateAuthenticationView from "@/services/pronote/determinate-authentication-view"; @@ -75,19 +84,37 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ }; }, []); - useEffect(() => { + + useEffect(() => { if (params) { void async function () { const dataset_instances = await getInstancesFromDataset(params.longitude, params.latitude); const pronote_instances = await pronote.geolocation(params); // On calcule la distance entre les instances et l'utilisateur. - let instances = pronote_instances.map((instance) => ({ - ...instance, - distance: Math.sqrt( - Math.pow(instance.latitude - params.latitude, 2) + Math.pow(instance.longitude - params.longitude, 2) - ), - })); + let instances = pronote_instances.map((instance) => { + const toRadians = (degrees: number) => degrees * (Math.PI / 180); + const R = 6371; // Earth's radius in kilometers + + const lat1 = toRadians(params.latitude); + const lon1 = toRadians(params.longitude); + const lat2 = toRadians(instance.latitude); + const lon2 = toRadians(instance.longitude); + + const dLat = lat2 - lat1; + const dLon = lon2 - lon1; + + const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(lat1) * Math.cos(lat2) * + Math.sin(dLon / 2) * Math.sin(dLon / 2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + const distance = R * c; + + return { + ...instance, + distance, // Distance in kilometers + }; + }); // On limite à 20 instances. instances.splice(20); @@ -264,7 +291,6 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ color={colors.text + "88"} /> } - text={instance.name} onPress={async () => { determinateAuthenticationView( instance.url, @@ -272,7 +298,32 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ showAlert ); }} - /> + > + + {instance.name} + + + {`à ${instance.distance.toFixed(2)}km de toi`} + + ))} From dbb2fde23ff90d740240e8d94d48545db87bb75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81my=20Godet?= Date: Wed, 18 Dec 2024 13:16:53 +0100 Subject: [PATCH 3/3] fix(Geolocation): Support of not precise city --- src/services/pronote/dataset_geolocation.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/pronote/dataset_geolocation.ts b/src/services/pronote/dataset_geolocation.ts index b5d33e760..28af52024 100644 --- a/src/services/pronote/dataset_geolocation.ts +++ b/src/services/pronote/dataset_geolocation.ts @@ -8,7 +8,8 @@ const getInstancesFromDataset = async (longitude: number, latitude: number): Pro if (adress_api.features.length === 0) { return []; } - const postcode = adress_api.features[0].properties.postcode; + let postcode = adress_api.features[0].properties.postcode; + postcode = postcode[0] + postcode[1] + "000"; let instances_fetch = await fetch(datasets.establishment.replace("[postcode]", postcode)); try {