/** * Collecteur GéoFoncier * Accès aux données foncières et immobilières via l'API GéoFoncier */ const fetch = require('node-fetch'); const GEOFONCIER_BASE_URL = 'https://api2.geofoncier.fr'; const GEOFONCIER_EXPERT_URL = 'https://site-expert.geofoncier.fr'; const USER_AGENT = '4NK-IA-Front/1.0 (Document Analysis Tool)'; const REQUEST_TIMEOUT_MS = 20000; // 20 secondes timeout const REQUEST_DELAY_MS = 2000; // 2 secondes entre les requêtes /** * Recherche les informations foncières pour une adresse * @param {Object} address - Adresse à rechercher * @returns {Promise} Résultat de la recherche GéoFoncier */ async function searchGeofoncierInfo(address) { const startTime = Date.now(); try { console.log(`[GéoFoncier] Recherche info foncière pour: ${address.street}, ${address.city}`); // Étape 1: Géocodage de l'adresse const geocodeResult = await geocodeAddress(address); if (!geocodeResult.success || !geocodeResult.coordinates) { return { success: false, duration: Date.now() - startTime, error: 'Géocodage échoué', address, timestamp: new Date().toISOString() }; } // Étape 2: Recherche des parcelles cadastrales const parcellesResult = await searchParcelles(geocodeResult.coordinates); // Délai de politesse await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY_MS)); // Étape 3: Recherche des informations foncières const foncierResult = await searchInfoFonciere(geocodeResult.coordinates); // Délai de politesse await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY_MS)); // Étape 4: Recherche des mutations récentes const mutationsResult = await searchMutations(geocodeResult.coordinates); const duration = Date.now() - startTime; console.log(`[GéoFoncier] Recherche terminée en ${duration}ms`); return { success: true, duration, address, geocode: geocodeResult, parcelles: parcellesResult, infoFonciere: foncierResult, mutations: mutationsResult, timestamp: new Date().toISOString(), source: 'geofoncier.fr' }; } catch (error) { const duration = Date.now() - startTime; console.error(`[GéoFoncier] Erreur recherche:`, error.message); return { success: false, duration, address, error: error.message, timestamp: new Date().toISOString() }; } } /** * Géocode une adresse via GéoFoncier * @param {Object} address - Adresse à géocoder * @returns {Promise} Résultat du géocodage */ async function geocodeAddress(address) { try { const query = `${address.street}, ${address.postalCode} ${address.city}`; const geocodeUrl = `${GEOFONCIER_BASE_URL}/geocoding/search?q=${encodeURIComponent(query)}&limit=1`; const response = await fetch(geocodeUrl, { method: 'GET', headers: { 'User-Agent': USER_AGENT, 'Accept': 'application/json' }, timeout: REQUEST_TIMEOUT_MS }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (data.features && data.features.length > 0) { const feature = data.features[0]; return { success: true, coordinates: { lat: feature.geometry.coordinates[1], lon: feature.geometry.coordinates[0] }, label: feature.properties.label, score: feature.properties.score || 0, data: feature.properties }; } return { success: false, error: 'Aucun résultat de géocodage' }; } catch (error) { return { success: false, error: error.message }; } } /** * Recherche les parcelles cadastrales * @param {Object} coordinates - Coordonnées {lat, lon} * @returns {Promise} Résultat de la recherche de parcelles */ async function searchParcelles(coordinates) { try { const parcellesUrl = `${GEOFONCIER_BASE_URL}/cadastre/parcelles?lat=${coordinates.lat}&lon=${coordinates.lon}&radius=100`; const response = await fetch(parcellesUrl, { method: 'GET', headers: { 'User-Agent': USER_AGENT, 'Accept': 'application/json' }, timeout: REQUEST_TIMEOUT_MS }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); const parcelles = []; if (data.parcelles && Array.isArray(data.parcelles)) { for (const parcelle of data.parcelles) { parcelles.push({ numero: parcelle.numero || '', section: parcelle.section || '', commune: parcelle.commune || '', surface: parcelle.surface || 0, nature: parcelle.nature || '', adresse: parcelle.adresse || '', proprietaire: parcelle.proprietaire || '', dateMutation: parcelle.dateMutation || '', valeur: parcelle.valeur || 0 }); } } return { success: true, parcelles, total: parcelles.length }; } catch (error) { return { success: false, error: error.message, parcelles: [] }; } } /** * Recherche les informations foncières * @param {Object} coordinates - Coordonnées {lat, lon} * @returns {Promise} Résultat de la recherche foncière */ async function searchInfoFonciere(coordinates) { try { const foncierUrl = `${GEOFONCIER_EXPERT_URL}/api/foncier/info?lat=${coordinates.lat}&lon=${coordinates.lon}`; const response = await fetch(foncierUrl, { method: 'GET', headers: { 'User-Agent': USER_AGENT, 'Accept': 'application/json' }, timeout: REQUEST_TIMEOUT_MS }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); return { success: true, info: { valeurFonciere: data.valeurFonciere || 0, valeurLocative: data.valeurLocative || 0, surfaceHabitable: data.surfaceHabitable || 0, surfaceTerrain: data.surfaceTerrain || 0, nombrePieces: data.nombrePieces || 0, anneeConstruction: data.anneeConstruction || '', typeHabitation: data.typeHabitation || '', energie: data.energie || '', ges: data.ges || '', diagnostics: data.diagnostics || [] } }; } catch (error) { return { success: false, error: error.message, info: null }; } } /** * Recherche les mutations récentes * @param {Object} coordinates - Coordonnées {lat, lon} * @returns {Promise} Résultat de la recherche de mutations */ async function searchMutations(coordinates) { try { const mutationsUrl = `${GEOFONCIER_BASE_URL}/mutations/recentes?lat=${coordinates.lat}&lon=${coordinates.lon}&radius=200&years=5`; const response = await fetch(mutationsUrl, { method: 'GET', headers: { 'User-Agent': USER_AGENT, 'Accept': 'application/json' }, timeout: REQUEST_TIMEOUT_MS }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); const mutations = []; if (data.mutations && Array.isArray(data.mutations)) { for (const mutation of data.mutations) { mutations.push({ date: mutation.date || '', type: mutation.type || '', valeur: mutation.valeur || 0, surface: mutation.surface || 0, prixM2: mutation.prixM2 || 0, vendeur: mutation.vendeur || '', acheteur: mutation.acheteur || '', adresse: mutation.adresse || '', reference: mutation.reference || '' }); } } return { success: true, mutations, total: mutations.length, periode: '5 dernières années' }; } catch (error) { return { success: false, error: error.message, mutations: [] }; } } /** * Recherche les informations de zonage * @param {Object} coordinates - Coordonnées {lat, lon} * @returns {Promise} Résultat de la recherche de zonage */ async function searchZonage(coordinates) { try { const zonageUrl = `${GEOFONCIER_BASE_URL}/zonage/info?lat=${coordinates.lat}&lon=${coordinates.lon}`; const response = await fetch(zonageUrl, { method: 'GET', headers: { 'User-Agent': USER_AGENT, 'Accept': 'application/json' }, timeout: REQUEST_TIMEOUT_MS }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); return { success: true, zonage: { zone: data.zone || '', sousZone: data.sousZone || '', constructibilite: data.constructibilite || '', hauteurMax: data.hauteurMax || '', densiteMax: data.densiteMax || '', servitudes: data.servitudes || [], restrictions: data.restrictions || [] } }; } catch (error) { return { success: false, error: error.message, zonage: null }; } } /** * Recherche les informations de voirie * @param {Object} coordinates - Coordonnées {lat, lon} * @returns {Promise} Résultat de la recherche de voirie */ async function searchVoirie(coordinates) { try { const voirieUrl = `${GEOFONCIER_BASE_URL}/voirie/info?lat=${coordinates.lat}&lon=${coordinates.lon}`; const response = await fetch(voirieUrl, { method: 'GET', headers: { 'User-Agent': USER_AGENT, 'Accept': 'application/json' }, timeout: REQUEST_TIMEOUT_MS }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); return { success: true, voirie: { type: data.type || '', largeur: data.largeur || 0, revetement: data.revetement || '', eclairage: data.eclairage || false, canalisations: data.canalisations || [], transports: data.transports || [] } }; } catch (error) { return { success: false, error: error.message, voirie: null }; } } module.exports = { searchGeofoncierInfo, geocodeAddress, searchParcelles, searchInfoFonciere, searchMutations, searchZonage, searchVoirie };