PageSourceSearch

https://antennes-relais.fr/outils-negociateurs.html

html antennes-relais.fr collected 2026-09-25 09:10:22 UTC 89,691 bytes, 851 lines download raw bytes

1<!doctype html><html lang="fr"><head><meta charset="utf-8"><title>Outil Négociateur Télécom : Étude de Zone de Recherche (ZR)</title><meta name="author" content="immonetic"><meta name="rating" content="General"><meta name="description" content="Accélérez vos déploiements avec notre outil dédié aux négociateurs télécoms. Commandez votre étude de Zone de Recherche (ZR) issue de bases officielles."><link rel="preload" href="_frame/style.css" as="style"><link rel="preload" href="_scripts/bootstrap/css/bootstrap.min.css" as="style"><link rel="preload" href="_scripts/bootstrap/fonts/fontawesome-webfont.woff2?v=4.3.0" as="font" type="font/woff2" crossorigin><link rel="preload" href="_scripts/bootstrap/css/font-awesome.min.css" as="style"><!--PRELOAD--><meta property="og:url" content="https://antennes-relais.fr/outils-negociateurs.html"><meta property="og:type" content="website"><meta property="og:site_name" content="antennes relais d"><meta property="og:title" content="Outil Négociateur Télécom : Étude de Zone de Recherche (ZR)"><meta property="og:description" content="Accélérez vos déploiements avec notre outil dédié aux négociateurs télécoms. Commandez votre étude de Zone de Recherche (ZR) issue de bases officielles."><meta property="og:image" content="https://antennes-relais.fr/_media/img/xlarge/gemini-generated-image-jxp9ltjxp9ltjxp9-2.jpg"><meta property="og:image:width" content="1600"><meta property="og:image:height" content="903"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="Outil Négociateur Télécom : Étude de Zone de Recherche (ZR)"><meta name="twitter:description" content="Accélérez vos déploiements avec notre outil dédié aux négociateurs télécoms. Commandez votre étude de Zone de Recherche (ZR) issue de bases officielles."><meta name="twitter:image" content="https://antennes-relais.fr/_media/img/xlarge/gemini-generated-image-jxp9ltjxp9ltjxp9-2.jpg"><meta name="ai-keywords" content="outil négociateur télécom, étude zone de recherche, audit ZR, antennes-relais, données officielles, déploiement réseau, IMMONETIC, cartographie télécom, télécommunications, PLU"><meta name="ai-description" content="Outil d'audit cartographique et d'analyse de Zones de Recherche (ZR) conçu par IMMONETIC pour les professionnels et négociateurs télécoms. Ce service permet de générer des études basées sur des données officielles pour faciliter et accélérer le déploiement de nouvelles antennes-relais.">google.com, pub-5479908916438170, DIRECT, f08c47fec0942fa0<meta name="generator" content="Lauyan TOWeb 14.1.0.112"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link href="rss.xml" rel="alternate" type="application/rss+xml"><link href="_media/img/medium/logo-formulaire.png" rel="shortcut icon" type="image/png"><link href="_media/img/medium/logo-formulaire.png" rel="apple-touch-icon"><meta name="msapplication-TileColor" content="#5835b8"><meta name="msapplication-TileImage" content="_media/img/medium/logo-formulaire.png"><meta name="theme-color" content="#FF8C18"><meta name="msapplication-navbutton-color" content="#FF8C18"><link href="_scripts/bootstrap/css/bootstrap.min.css" rel="stylesheet"><link href="https://fonts.googleapis.com/css?family=Varela+Round&display=swap" rel="stylesheet"><link href="_frame/style.css" rel="stylesheet"><link rel="canonical" href="https://antennes-relais.fr/outils-negociateurs.html"><link rel="stylesheet" href="_scripts/jquery/ui/tw-dialog.css"><link rel="stylesheet" href="_scripts/bootstrap/css/font-awesome.min.css"><style>.raty-comp{padding:8px 0;font-size:10px;color:#FD9317}.tw-blogbar{display:flex;align-items:center}@media (max-width:767px){.tw-blogbar{flex-direction:column;}}.ta-left{text-align:left}.ta-center{text-align:center}.ta-justify{text-align:justify}.ta-right{text-align:right}.float-l{float:left}.float-r{float:right}.flexobj{flex-grow:0;flex-shrink:0;margin-right:1em;margin-left:1em}.flexrow{display:flex !important;align-items:center}.flexrow-align-start{align-items:start}@media (max-width:767px){.flexrow{flex-direction:column}.flexrow-align-start{align-items:center}}.cookie-consent{width:100%;padding:16px 0;position:relative;z-index:9800;text-align:center;}.ckc-bottom{position:fixed;bottom:0;left:0;right:0;height:auto;}.cookie-consent,.cookie-consent button{font-size:100%}.cookie-consent a{font-weight:bold;text-decoration:none}.cookie-consent a:hover{text-decoration:underline}.cookie-consent .no-deco-hover a:hover{text-decoration:none}#main-carousel>.carousel-inner>.item{transform:scale(1.1);}#main-carousel>.carousel-inner>.active{transform:scale(1);transition:0.6s ease-in-out left,transform 4s;}</style><link href="_frame/print.css" rel="stylesheet" type="text/css" media="print">
1<script src="_scripts/cookie/gdpr-ckie.js"></script>
1<script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,"script","https://www.google-analytics.com/analytics.js","ga");ga("create","G-0Q8Q7KS6FQ","auto");ga("send","pageview");window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag("consent","default",{"ad_storage":"denied","ad_user_data":"denied","ad_personalization":"denied","analytics_storage":"denied","functionality_storage":"denied","personalization_storage":"denied","security_storage":"granted","wait_for_update":500});</script>
vendor: 63 bytes, line 1
1<script async src="https://www.googletagmanager.com/gtag/js?id=
1G-D5E230LT02
vendor: 11 bytes, line 1
1"></script>
1<script>
vendor: 118 bytes, line 1
1window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag("js",new Date());gtag("config","
1G-D5E230LT02
vendor: 3 bytes, line 1
1");
1</script>
1<script>
vendor: 388 bytes, line 1
1!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version="2.0";n.queue=[];t=b.createElement(e);t.async=!0;t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,document,"script","https://connect.facebook.net/en_US/fbevents.js");fbq("init","
1457523984376295
vendor: 27 bytes, line 1
1");fbq("track","PageView");
1</script>
1<noscript><img height="1" width="1" alt="" src="https://www.facebook.com/tr?id=457523984376295&ev=PageView&noscript=1"/></noscript></head><body oncontextmenu="return false;"><div id="site"><div id="page"><header><nav id="toolbar3" class="navbar"><div class="navbar-inner"><div class="container-fluid"><ul id="toolbar3_r" class="nav pull-right"><li><div id="sharebox"><a target="_blank" href="https://www.facebook.com/@fmsimmonetic" rel="noopener"><img style="width:2rem" src="_frame/tw-share-facebook.svg" class="anim-zoom" alt="facebook"></a><a target="_blank" href="https://www.linkedin.com/in/immonetic/" rel="noopener"><img style="width:2rem" src="_frame/tw-share-linkedin.svg" class="anim-zoom" alt="linkedin"></a><a target="_blank" href="https://www.youtube.com/channel/UCyzmC-Z4q7FdXPWSOT_pCjA" rel="noopener"><img style="width:2rem" src="_frame/tw-share-youtube.svg" class="anim-zoom" alt="youtube"></a><a target="_blank" href="https://www.instagram.com/antennes_relais/" rel="noopener"><img style="width:2rem" src="_frame/tw-share-instagram.svg" class="anim-zoom" alt="instagram"></a><a target="_blank" href="rss.xml" type="application/rss+xml" rel="noopener"><img style="width:2rem" src="_frame/tw-share-rss.svg" class="anim-zoom" alt="rss"></a></div></li></ul><button type="button" class="btn btn-navbar collapsed" aria-label="Menu" style="float:left" data-toggle="collapse" data-target=".nav-collapse"><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><div class="nav-collapse collapse"><ul id="toolbar3_l" class="nav"><li><ul id="mainmenu" class="nav"><li><a href="./">Accueil</a></li><ul class="nav"><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Propriétaire&nbsp;<small>&#9660;</small></a><ul class="dropdown-menu"><li><a href="tester-mon-adresse.html">Etat des lieux antennes relais</a></li><li><a href="mesures-de-champs-radioelectriques-protocole-anfr.html">Mesurer l'exposition d'un bien aux ondes électromagnétiques</a></li><li><a href="formulaire-valorisation-terrain-immeuble-pour-accueil-antennes-relais.html">Formulaire valorisation terrain, immeuble</a></li><li><a href="formulaire-negociation-renegociation-bail-antennes-relais.html">Assistance négociation, renégociation, reprise de bail</a></li><li><a href="bail-antennes-relais.html">Comprendre les enjeux d'une négociation, cession de bail</a></li><li><a href="optimiser-revenus-site-telecom-existant.html">Optimiser les revenus d’un site télécom</a></li><li><a href="mise-en-location-de-votre-terrain-immeuble-pour-installer-une-antenne-relais.html">Location de votre terrain pour installer un relai</a></li><li><a href="redevance-antennes-relais-bail-loyer-vente.html">Quelle rentabilité pour votre terrain ?</a></li><li><a href="carte-antennes-relais-louer-votre-terrain-bien-a-un-operateur.html">Station relai : carte opérateurs</a></li></ul></li></ul><ul class="nav"><li class="dropdown active"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Opérateur/Gestionnaire&nbsp;<small>&#9660;</small></a><ul class="dropdown-menu"><li><a href="outils-negociateurs.html">Outils négociateurs</a></li><li><a href="operateur-gestionnaire-infrastructures-antennes-relais-decouvrez-nos-services-pour-votre-deploiement.html">Services</a></li><li><a href="legislation-et-jurisprudence-antennes-relais.html">Législation et jurisprudence antennes-relais</a></li><li><a href="prospection-a-distance-de-zones-de-recherche.html">Prospection à distance</a></li></ul></li></ul><ul class="nav"><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Catalogue&nbsp;<small>&#9660;</small></a><ul class="dropdown-menu"><li><a href="posez-votre-question-antennes-relais.html">questions, assistance, pack négo</a></li></ul></li></ul><li><a href="immonetic-expertise-antennes-relais.html">Qui sommes nous ?</a></li><li><a href="contact-immonetic-demande-information-antennes-relais.html">Contact</a></li><li><a href="blog-antennes-relais.html">BLOG</a></li></ul></li></ul></div></div></div></nav><nav id="toolbar1" class="navbar"><div class="navbar-inner"><div class="container-fluid"><ul id="toolbar1_l" class="nav"><li><a id="logo" href="./" aria-label="logo"><span id="logo-lt"></span><span id="logo-rt"></span><br><span id="logo-sl" class="hidden-phone"></span></a></li></ul><ul id="toolbar1_r" class="nav pull-right"><li><a id="button-cart" data-container="body" href="_checkout.html" title="Panier"><span id="sc_pcount"></span></a></li><li><a id="button-login" href="_accounts.php" title="Mon compte"><i class="fa fa-user fa-2x"></i></a></li><li><form id="searchbox" class="navbar-search" action="_search.html"><input type="text" name="req" id="searchbox-req" class="search-query" placeholder="Rechercher"></form></li></ul></div></div></nav></header>
1<main id="content" class="container-fluid"><div class="row-fluid"><div class="span12"></div></div><div id="topic" class="row-fluid"><div id="topic-inner"><div id="top-content" class="span9" style="background:initial"><h1>Outils négociateurs</h1><div class="row-fluid"><div class="span12 alert" style="overflow:hidden;text-align:center;"><button type="button" class="close" data-dismiss="alert">&times;</button>
1<script>google_ad_client = "pub-5479908916438170";google_ad_width = 468;google_ad_height = 60;google_ad_format = "468x60_as";google_ad_type = "text_image";google_color_border = "C0C0C0";google_color_bg = "C09853";google_color_link = "0000FF";google_color_url = "C0C0C0";google_color_text = "000000";</script>
1<script src="https://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
1</div></div><div class="twpara-row row-fluid"><div id="5XxINOXi" class="span12 tw-para "><h2><span style="font-size:18px;">Optimisez votre prospection : Générez l'étude automatisée&nbsp;de votre zone de recherche en quelques minutes !</span></h2><div class="ptext"><p><span style="font-size:16px;">Fini les heures perdues à croiser manuellement le cadastre, les règlements d'urbanisme, les raccordements et les contraintes environnementales.</span></p><p><br><span style="font-size:16px;">Importez simplement votre fichier .kmz. Notre algorithme analyse votre zone de recherche et en extrait instantanément toutes les données stratégiques pour qualifier vos sites avant même la première visite terrain :</span></p><p><br><span style="font-size:16px;">🌍 <b>Couverture globale :</b> Service pleinement opérationnel en <b>France Métropolitaine</b> et aux <b>Antilles</b>, avec prise en compte des spécificités géographiques locales.</span></p><p><span style="font-size:16px;"><br>🎯 <b>Topographie &amp; Émergences</b></span></p><ul><li><span style="font-size:16px;"><b>Le Top 15 des points hauts :</b> Identification des bâtiments les plus hauts (hauteur et altitudes NGF sur zone).</span></li><li><span style="font-size:16px;"><i><i>Masques végétaux</i> :</i>* Estimation des hauteurs boisées pour anticiper les obstacles.</span></li></ul><span style="font-size:16px;">📜 <b>Urbanisme &amp; Environnement</b></span><ul><li><span style="font-size:16px;"><b>Zonage PLU/PLUi :</b> Affichage de la zone réglementaire exacte pour chaque parcelle.</span></li><li><span style="font-size:16px;"><b>Alertes Environnementales :</b> Détection automatique des communes soumises à la <b>Loi Littoral</b> et au risque d'<b>
1érosion côtière</b>.</span></li><li><span style="font-size:16px;"><b>Patrimoine :</b> Signalement des périmètres ABF (Monuments historiques &lt;500m).</span></li></ul> <span style="font-size:16px;"> ⚡ <b>Infrastructures &amp; Raccordement</b></span><ul><li><span style="font-size:16px;"><b>Réseau Électrique :</b> Détection des postes de transformation Enedis (HTA/BT) à moins de 500m pour anticiper les coûts de raccordement.</span></li><li><span style="font-size:16px;"><b>Fibre Optique :</b> Vérification de l'éligibilité et du statut de déploiement (ARCEP) pour chaque adresse.</span></li><li><span style="font-size:16px;"><b>Antennes existantes :</b> Cartographie des sites ANFR présents dans un rayon de 400m (opérateurs, hauteurs, technologies).</span></li></ul> <span style="font-size:16px;"> 🏢 <b>Ciblage Foncier &amp; OSINT</b></span><ul><li><span style="font-size:16px;"><b>Bailleurs Sociaux :</b> Détection et regroupement visuel du patrimoine des acteurs institutionnels (HLM).</span></li><li><span style="font-size:16px;"><b>Copropriétés &amp; Personnes Morales :</b> Vérification au RNIC et génération automatique de <b>liens d'enquête (Pappers)</b> pour trouver le bon interlocuteur (Syndic, gestionnaire) en un clic.</span></li><li><span style="font-size:16px;"><b>Établissements sensibles :</b> Identification des hôpitaux, écoles ou crèches dans un rayon de 120m pour intégrer une marge de sécurité pour la production des DIM, DIVP.</span></li></ul><p><span style="font-size:16px;"><b>Livrables prêts à l'emploi :</b>Téléchargez une archive contenant un tableau de données exhaustif (.csv) et une cartographie interactive (.kmz) directement exploitable sur Google Earth par vos négociateurs.</span></p><p><span style="font-size:16px;"><br><i>( * ) Les hauteurs végétales sont des estimations basées sur les définitions théoriques de l'IGN, et non des hauteurs réelles mesurées sur site.</i></span></p><p><span style="font-size:16px;">Conçu&nbsp;pour les professionnels des télécoms, ce module vous livre des données issues de bases de données officielles, prêtes à être exploitées directement dans vos outils de travail.</span></p><p><strong style="background-color: initial; color: rgb(28, 28, 28);"><span style="font-size:16px;">Besoin de vous rendre compte d'un résultat concret ? Téléchargez&nbsp;celui de notre ZR fictive sur Paris 10 :</span></strong></p><p><strong><span style="font-size:16px;">- </span></strong><a href="_iserv/dlfiles/dl.php?ddl=zr-fictive-paris-10.kmz" id="lnk3-5XxINOXi" rel="nofollow">Fichier KMZ</a></p><p>- <a href="_iserv/dlfiles/dl.php?ddl=zr-fictive-paris-10.csv" id="lnk2-5XxINOXi" rel="nofollow">Fichier CSV</a></p><p><b style="font-size: 16px; background-color: initial; color: rgb(28, 28, 28);">Prêt à accélérer vos déploiements ? Commandez l'étude de votre zone de recherche dans le formulaire ci-dessous !&nbsp;</b></p><p><b style="font-size: 16px; background-color: initial; color: rgb(28, 28, 28);">Une fois le paiement effectué, vous recevrez par mail un lien url et un code d'accès unique à notre service.</b></p><p><b style="font-size: 16px; background-color: initial; color: rgb(28, 28, 28);">Besoin de faire l'étude de plusieurs ZR, </b><a onclick="javascript:return(decMail2(this));" href="znvygb?cnpxme#nagraarf-erynvf.se" id="lnk1-5XxINOXi" rel="nofollow" style="font-size: 16px; background-color: initial;">contactez-nous</a><b style="font-size: 16px; background-color: initial; color: rgb(28, 28, 28);"> pour l'achat d'un pack.</b></p><p><br></p><p style="text-align: center;">
2    <iframe
3      id="JotFormIFrame-262564954333059"
4      title="Etude ZR"
5      onload="window.parent.scrollTo(0,0)"
6      allowtransparency="true"
7      allow="geolocation; microphone; camera; fullscreen; payment"
8      src="https://pci.jotform.com/form/262564954333059"
9      frameborder="0"
10      style="min-width:100%;max-width:100%;height:539px;border:none;"
11      scrolling="no"
12    >
13    </iframe>
14    
14<script src='https://cdn.jotfor.ms/s/umd/latest/for-form-embed-handler.js'></script>
14
15    
15<script>window.jotformEmbedHandler("iframe[id='JotFormIFrame-262564954333059']", "https://pci.jotform.com/")</script>
15
16    <span style="font-size:16px;">&nbsp;</span></p><p><br></p><p style="text-align: center;"><br></p></div></div></div><div class="twpara-row row-fluid"><div id="8lT1Hy4P" class="span12 tw-para "><h2 style="text-align:center"><span style="font-size:18px;">Actualités judiciaires du 20 juillet au 20 septembre&nbsp;2026</span></h2><div class="ptext"><p style="text-align: justify;"><span style="font-size:16px;">Découvrez notre dernière synthèse jurisprudentielle dédiée au déploiement des infrastructures de radiotéléphonie mobile. Cette édition décrypte les tendances récentes des juridictions administratives face aux refus d'urbanisme : contrôle strict de l'intégration paysagère (art. R. 111-27), échec des oppositions fondées sur le seul principe de précaution sanitaire, et application rigoureuse des PLU locaux face à l'urgence d'améliorer la couverture réseau.&nbsp;</span></p><p style="text-align: center;"><a class="btn btn-warning" href="_iserv/dlfiles/dl.php?ddl=lettre-dinformation-immonetic-20260921.pdf" id="lnkb13859de" title="Lettre d’information juridique ">ACCES SYNTHESE</a><br><br></p></div></div></div><div class="twpara-row row-fluid"><div id="Day8eXVN" class="span12 tw-para "><h2><span style="font-size:18px;">Carte Etablissements Particuliers (écoles, crèches, hôpitaux ...)<br></span><br></h2><div class="pobj float-l"><picture><source type="image/webp" srcset="_media/img/thumb/capture-decran-2026-07-01-194914.
16webp 160w,_media/img/small/capture-decran-2026-07-01-194914.webp 480w,_media/img/medium/capture-decran-2026-07-01-194914.webp 768w,_media/img/large/capture-decran-2026-07-01-194914.webp 1024w,_media/img/xlarge/capture-decran-2026-07-01-194914.webp 1208w"><source type="image/png" srcset="_media/img/thumb/capture-decran-2026-07-01-194914.png 160w,_media/img/small/capture-decran-2026-07-01-194914.png 480w,_media/img/medium/capture-decran-2026-07-01-194914.png 768w,_media/img/large/capture-decran-2026-07-01-194914.png 1024w,_media/img/xlarge/capture-decran-2026-07-01-194914.png 1208w"><img src="_media/img/thumb/capture-decran-2026-07-01-194914.png"  width="160" height="156" style="max-width:100%;width:160px;" alt="" loading="lazy"></picture></div><div class="ptext"><div style="text-align: justify;"><span style="font-size:16px;"><b>Avertissement et limites de responsabilité :&nbsp;</b>Les résultats de proximité et les distances affichés sont fournis à titre purement informatif et indicatif. Le calcul des distances est établi de manière automatisée par rapport aux limites parcellaires ou aux contours de bâtiments répertoriés dans les bases de données publiques (OpenStreetMap, FINESS). Des écarts ou approximations géographiques peuvent exister par rapport aux données cadastrales officielles ou à la réalité physique du terrain. <b>Par ailleurs, une marge de sécurité a été volontairement insérée dans cet outil en appliquant un rayon d'extraction de 150 mètres au lieu des 100 mètres prévus par la réglementation, afin d'inciter expressément l'utilisateur à réaliser un contrôle de vérification ou lui permettre de saisir précisément le rayon.</b> En conséquence, la société décline toute responsabilité quant à l'utilisation, l'interprétation ou l'exactitude de ces résultats. Cet outil constitue un premier niveau d'information qui ne dispense en aucun cas l'utilisateur d'un contrôle rigoureux et d'une vérification technique ou visuelle directement sur site au sein de la zone de recherche.</span></div><div style="text-align: justify;"><br></div><div style="text-align: justify;"><em><strong><span style="font-size:16px;">Afin de garantir la confidentialité de vos données, nous n'enregistrons aucune requête effectuée sur notre site. &nbsp;</span></strong></em></div><div style="text-align: justify;"><br></div><div><br></div><div style="text-align: center;"><link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
17<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
17
18
19<style>
20  .immo-container { font-family: 'Segoe UI', Arial, sans-serif; max-width: 700px; margin: 20px auto; padding: 20px; background: #f8f9fa; border-radius: 10px; border: 1px solid #dadce0; color: #333; box-sizing: border-box; }
21  .immo-title { margin-top: 0; color: #1a73e8; font-size: 20px; }
22  .immo-label { font-weight: 600; display: block; margin-bottom: 6px; font-size: 14px; }
23  .immo-input-text { width: 100%; padding: 11px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 5px; font-size: 14px; }
24  .immo-suggest-box { border: 1px solid #ccc; border-top: none; max-height: 180px; overflow-y: auto; background: white; display: none; position: absolute; width: calc(100% - 40px); z-index: 10000; border-radius: 0 0 5px 5px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
25  .immo-suggest-item { padding: 8px 10px; cursor: pointer; border-bottom: 1px solid #f1f3f4; font-size: 13px; }
26  .immo-suggest-item:hover { background-color: #f1f3f4; }
27  .immo-flex { display: flex; margin-top: 15px; align-items: center; gap: 10px; }
28  .immo-input-num { width: 80px; padding: 7px; border: 1px solid #ccc; border-radius: 5px; text-align: center; font-size: 14px; }
29  .immo-btn { margin-top: 15px; width: 100%; padding: 12px; background-color: #1a73e8; color: white; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; font-size: 15px; }
30  .immo-btn:disabled { background-color: #9aa0a6; cursor: not-allowed; }
31  .immo-results-zone { margin-top: 20px; display: none; }
32  .immo-dl-box { margin: 15px 0; padding: 12px; background-color: #e8f0fe; border-radius: 8px; border: 1px solid #1a73e8; display: none; gap: 10px; flex-wrap: wrap; }
33  .immo-dl-btn { padding: 10px 14px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; color: white; font-size: 13px; display: inline-block; text-decoration: none; }
34  .immo-list { background: white; padding: 12px; border-radius: 6px; border: 1px solid #ddd; max-height: 200px; overflow-y: auto; font-size: 13px; line-height: 1.4; margin-bottom: 15px; }
35  .map-wrapper { position: relative; width: 100%; height: 450px; margin-top: 10px; }
36  #immo_map { width: 100%; height: 100%; border-radius: 8px; border: 1px solid #ccc; z-index: 1; }
37  .map-overlay-title { background: white; padding: 8px 12px; border-radius: 6px; box-shadow: 0 2px 6px rgba(0,0,0,0.15); font-size: 12px; max-width: 260px; margin-top: 100px !important; font-family: Arial, sans-serif; }
38  .map-overlay-footer { background: white; padding: 6px 12px; border-radius: 4px; box-shadow: 0 2px 6px rgba(0,0,0,0.15); font-size: 10px; font-family: Arial, sans-serif; max-width: 320px; line-height: 1.4; }
39  .map-overlay-logo { background: rgba(255,255,255,0.9); padding: 5px 10px; border-radius: 6px; box-shadow: 0 2px 6px rgba(0,0,0,0.15); }
40</style>
41
42<div class="immo-container">
43  <h3 class="immo-title">🔍 Saisie d'Adresse & Analyse de Proximité</h3>
44  <div style="position: relative;">
45    <label class="immo-label">Adresse postale (Saisie prédictive ou libre) :</label>
46    <input type="text" id="immo_addr" autocomplete="new-password" placeholder="Ex: 32 bd de Strasburg, Paris..." class="immo-input-text">
47    <div id="immo_suggest" class="immo-suggest-box"></div>
48  </div>
49
50  <div style="position: relative; margin-top: 15px;">
51    <label class="immo-label">Référence du site (Optionnel - Servira de nom de fichier) :</label>
52    <input type="text" id="immo_ref" autocomplete="new-password" placeholder="Ex: Site_Auteuil_Ref_45..." class="immo-input-text">
53  </div>
54  
55  <div class="immo-flex">
56    <label class="immo-label" style="margin: 0;">Rayon de recherche :</label>
57    <input type="number" id="immo_rayon" value="150" class="immo-input-num">
58    <span style="font-size:14px; color:#666;">mètres</span>
59  </div>
60  
61  <button id="immo_submit" class="immo-btn">Lancer l'extraction</button>
62
63  <div id="immo_results" class="immo-results-zone">
64    <div id="immo_meta_text" style="font-size: 13px; font-weight: bold; margin-bottom: 10px; color:#5f6368;"></div>
65    
66    <div class="immo-dl-box" id="immo_dl_box">
67      <a id="immo_dl_map" download="carte_proximite_immonetic.html" class="immo-dl-btn" style="background-color: #1a73e8;">📥 Télécharger la Carte (HTML)</a>
68      <a id="immo_dl_csv" download="liste_etablissements_150m.csv" class="immo-dl-btn" style="background-color: #34a853;">📥 Télécharger la Liste (CSV)</a>
69      <a id="immo_dl_kml" download="analyse_proximite_immonetic.kml" class="immo-dl-btn" style="background-color: #004d40;">📥 Télécharger pour Google Earth (KML)</a>
70    </div>
71
72    <div style="font-size: 11px; color: #666; line-height: 1.4; margin: 15px 0; padding: 10px; background: #fff; border-left: 3px solid #9aa0a6; border-radius: 4px;">
73      <b>Avertissement :</b>
73 Les distances sont calculées de manière automatisée par rapport aux limites parcellaires ou contours de bâtiments publics. Des écarts peuvent exister avec la réalité du terrain. <b>Par défaut, un rayon de 150 mètres a été volontairement appliquée (au lieu des 100 mètres réglementaires) pour plus de sécurité afin d'inciter l'utilisateur à réaliser un contrôle visuel et technique directement sur site. Vous pouvez réduire ce rayon </b> Ce module reste un outil d'information ; nous déclinons toute responsabilité quant à l'utilisation ou l'exactitude de ces résultats. Conditions complètes sur <a href="https://antennes-relais.fr/outils-negociateurs.php" target="_blank" rel="noopener">outils-negociateurs.php</a>.
74    </div>
75
76    <div class="immo-label">Établissements repérés :</div>
77    <div id="immo_text_list" class="immo-list"></div>
78    
79    <button id="immo_ruler_btn" class="immo-btn" style="margin-top:10px; background-color:#5f6368; width:auto; display:inline-block; padding:8px 14px; font-size:13px;">📐 Activer la règle de mesure</button>
80
81    <div class="map-wrapper">
82      <div id="immo_map"></div>
83    </div>
84  </div>
85</div>
86
87<script>
88(function() {
89  function executerScriptAnalyse() {
90    const input = document.getElementById('immo_addr');
91    const btn = document.getElementById('immo_submit');
92    if (!input || !btn) {
93      setTimeout(executerScriptAnalyse, 250);
94      return;
95    }
96
97    const box = document.getElementById('immo_suggest');
98    const rayonInput = document.getElementById('immo_rayon');
99    const refInput = document.getElementById('immo_ref');
100    const resultsZone = document.getElementById('immo_results');
101    const textList = document.getElementById('immo_text_list');
102    const metaText = document.getElementById('immo_meta_text');
103    const dlBox = document.getElementById('immo_dl_box');
104    const dlCsv = document.getElementById('immo_dl_csv');
105    const dlMap = document.getElementById('immo_dl_map');
106    const dlKml = document.getElementById('immo_dl_kml');
107    const rulerBtn = document.getElementById('immo_ruler_btn');
108
109    const URL_LOGO = "https://antennes-relais.fr/_media/img/medium/logo-formulaire.png";
110
111    let lat = null, lon = null, map = null, circle = null, markerGroup = null;
112    let groupScolaire = null, groupCreche = null, groupSoins = null;
113    let currentData = [];
114    let currentAdresseSaisie = "";
115
116    let modeMesure = false;
117    let premierPointMesure = null;
118    let mesureMarkers = [];
119    let mesureLigne = null;
120
121    rulerBtn.addEventListener('click', () => {
122      if (!map) return;
123      modeMesure = !modeMesure;
124      if (modeMesure) {
125        rulerBtn.innerText = "📐 Règle active (Cliquez 2 points sur la carte)";
126        rulerBtn.style.backgroundColor = "#e91e63";
127        map.getContainer().style.cursor = 'crosshair';
128      } else {
129        rulerBtn.innerText = "📐 Activer la règle de mesure";
130        rulerBtn.style.backgroundColor = "#5f6368";
131        map.getContainer().style.cursor = '';
132        nettoyerMesure();
133      }
134    });
135
136    function nettoyerMesure() {
137      premierPointMesure = null;
138      mesureMarkers.forEach(m => map.removeLayer(m));
139      mesureMarkers = [];
140      if (mesureLigne) { map.removeLayer(mesureLigne); mesureLigne = null; }
141    }
142
143    input.addEventListener('input', async (e) => {
144      lat = null; lon = null;
145      const text = e.target.value;
146      if (text.length < 3) { box.style.display = 'none'; return; }
147      try {
148        const res = await fetch('https://photon.komoot.io/api/?q=' + encodeURIComponent(text) + '&lang=fr&limit=5');
149        const data = await res.json();
150        box.innerHTML = '';
151        if (data.features && data.features.length > 0) {
152          box.style.display = 'block';
153          data.features.forEach(feat => {
154            const p = feat.properties;
155            const label = [p.name, p.street, p.postcode, p.city, p.country].filter(Boolean).join(', ');
156            const item = document.createElement('div');
157            item.className = 'immo-suggest-item';
158            item.innerText = label;
159            item.addEventListener('click', () => {
160              const matchNum = input.value.match(/^(\d+)\s*/);
161              const numTape = matchNum ? matchNum[1] : '';
162              input.value = (numTape && !label.match(/^\d/)) ? numTape + " " + label : label;
163              box.style.display = 'none';
164            });
165            box.appendChild(item);
166          });
167        } else { box.style.display = 'none'; }
168      } catch (err) { console.error(err); }
169    });
170
171    document.addEventListener('click', (e) => { if (e.target !== input) box.style.display = 'none'; });
172
173    function getDistance(lat1, lon1, lat2, lon2) {
174      const R = 6371000;
175      const dLat = (lat2 - lat1) * Math.PI / 180;
176      const dLon = (lon2 - lon1) * Math.PI / 180;
177      const a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon/2) * Math.sin(dLon/2);
178      return R * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)));
179    }
180
181    function generateKMLCircle(cLat, cLon, radiusMeters) {
182      var points = [];
183      var numPoints = 64;
184      var earthRadius = 6371000;
185      var d = radiusMeters / earthRadius;
186      var lat1 = cLat * Math.PI / 180;
187      var lon1 = cLon * Math.PI / 180;
188      for (var i = 0; i <= numPoints; i++) {
189        var tc = (i * 2 * Math.PI) / numPoints;
190        var latRad = Math.asin(Math.sin(lat1) * Math.cos(d) + Math.cos(lat1) * Math.sin(d) * Math.cos(tc));
191        var lonRad = lon1 + Math.atan2(Math.sin(tc) * Math.sin(d) * Math.cos(lat1), Math.cos(d) - Math.sin(lat1) * Math.sin(latRad));
192        points.push((lonRad * 180 / Math.PI) + "," + (latRad * 180 / Math.PI) + ",0");
193      }
194      return points.join(" ");
195    }
196
197    function createCustomPin(color) {
198      var hexColor = "#34a853"; 
199      if (color === "purple") hexColor = "#a142f4";
200      if (color === "orange") hexColor = "#ff9900";
201      if (color === "blue") hexColor = "#1a73e8";
202
203      return L.divIcon({
204        className: 'custom-pin-' + color,
205        html: '<svg width="30" height="42" viewBox="0 0 30 42" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 0C6.71 0 0 6.71 0 15C0 26.25 15 42 15 42C15 42 30 26.25 30 15C30 6.71 23.29 0 15 0ZM15 20C12.24 20 10 17.76 10 15C10 12.24 12.24 10 15 10C17.76 10 20 12.24 20 15C20 17.76 17.76 20 15 20Z" fill="' + hexColor + '"/><circle cx="15" cy="15" r="4" fill="#FFFFFF"/></svg>',
206        iconSize: [30, 42],
207        iconAnchor: [15, 42],
208        popupAnchor: [0, -38]
209      });
210    }
211
212    btn.addEventListener('click', async () => {
213      currentAdresseSaisie = input.value;
214      if (!currentAdresseSaisie) { alert("Veuillez saisir une adresse."); return; }
215      
216      btn.innerText = "Extraction en cours...";
217      btn.disabled = true;
218      dlBox.style.display = "none";
219      resultsZone.style.display = "none";
220      nettoyerMesure();
221
222      try {
223        const response = await fetch('https://photon.komoot.io/api/?q=' + encodeURIComponent(currentAdresseSaisie) + '&lang=fr&limit=1');
224        const data = await response.json();
225        if (data.features && data.features.length > 0) {
226          lon = data.features[0].geometry.coordinates[0];
227          lat = data.features[0].geometry.coordinates[1];
228        } else {
229          alert("Adresse introuvable.");
230          btn.innerText = "Lancer l'extraction"; btn.disabled = false; return;
231        }
232
233        const rayon = parseInt(rayonInput.value);
234        metaText.innerHTML = "📍 Point analysé : " + currentAdresseSaisie + "<br>📏 Rayon d'inclusion étudié : " + rayon + " mètres";
235        currentData = [];
236        textList.innerHTML = "";
237
238        const deltaLat = rayon / 111000;
239        const deltaLon = rayon / (111000 * Math.cos(lat * Math.PI / 180));
240        const bbox = (lon - deltaLon) + "," + (lat - deltaLat) + "," + (lon + deltaLon) + "," + (lat + deltaLat);
241
242        try {
243          const opQ = '[out:json][timeout:15];(node["amenity"~"school|kindergarten|childcare|hospital|clinic|social_facility|nursing_home"](around:' + rayon + ',' + lat + ',' + lon + ');way["amenity"~"school|kindergarten|childcare|hospital|clinic|social_facility|nursing_home"](around:' + rayon + ',' + lat + ',' + lon + ');relation["amenity"~"school|kindergarten|childcare|hospital|clinic|social_facility|nursing_home"](around:' + rayon + ',' + lat + ',' + lon + ');node["social_facility"](around:' + rayon + ',' + lat + ',' + lon + ');way["social_facility"](around:' + rayon + ',' + lat + ',' + lon + ');relation["social_facility"](around:' + rayon + ',' + lat + ',' + lon + '););out center geom;';
244          const overpassURL = "https://overpass-api.de/api/interpreter";
245          const resReal = await fetch(overpassURL, { method: "POST", body: "data=" + encodeURIComponent(opQ) });
246          if(resReal.ok) {
247            const d = await resReal.json();
248            const els = d.elements || [];
249            for (let i = 0; i < els.length; i++) {
250              const el = els[i];
251              const t = el.tags || {};
252              let e_lat = null, e_lon = null;
253              let calc_dist = Infinity;
254              let closest_vertex = null;
255              let polygonPoints = null;
256
257              if (el.lat && el.lon) {
258                e_lat = el.lat;
259                e_lon = el.lon;
260                calc_dist = getDistance(lat, lon, e_lat, e_lon);
261              } else if (el.geometry && el.geometry.length > 0) {
262                polygonPoints = el.geometry.map(function(pt) { return [pt.lat, pt.lon]; });
263                let sum_lat = 0, sum_lon = 0;
264                el.geometry.forEach(pt => {
265                  let d_v = getDistance(lat, lon, pt.lat, pt.lon);
266                  if (d_v < calc_dist) { 
267                    calc_dist = d_v; 
268                    closest_vertex = pt;
269                  }
270                  sum_lat += pt.lat;
271                  sum_lon += pt.lon;
272                });
273                
274                let test_cent_lat = sum_lat / el.geometry.length;
275                let test_cent_lon = sum_lon / el.geometry.length;
276                
277                if (getDistance(lat, lon, test_cent_lat, test_cent_lon) <= rayon) {
278                  e_lat = test_cent_lat;
279                  e_lon = test_cent_lon;
280                } else if (closest_vertex) {
281                  e_lat = closest_vertex.lat;
282                  e_lon = closest_vertex.lon;
283                }
284              } else if (el.center && el.center.lat && el.center.lon) {
285                e_lat = el.center.lat;
286                e_lon = el.center.lon;
287                calc_dist = getDistance(lat, lon, e_lat, e_lon);
288              }
289
290              if (calc_dist <= rayon && e_lat && e_lon) {
291                var typeLabel = "Établissement scolaire";
292                var col = "purple";
293                
294                if (t.amenity === 'childcare' || t.amenity === 'kindergarten') {
295                  typeLabel = "Crèche / Garderie";
296                  col = "green";
297                } else if (t.amenity === 'hospital' || t.amenity === 'clinic' || t.amenity === 'nursing_home' || t.social_facility === 'nursing_home' || t.amenity === 'social_facility' || t.social_facility === 'assisted_living') {
298                  var name_lower = (t.name || "").toLowerCase();
299                  if (name_lower.includes("cabinet") || name_lower.includes("pharmacie") || name_lower.includes("laboratoire") || name_lower.includes("kiné")) {
300                    continue;
301                  }
302                  if (t.social_facility === 'nursing_home' || t.amenity === 'nursing_home' || name_lower.includes("ehpad") || name_lower.includes("retraite") || name_lower.includes("auteuil") || t.social_facility === 'assisted_living') {
303                    typeLabel = "EHPAD / Maison de retraite";
304                  } else {
305                    typeLabel = "Hôpital / Clinique";
306                  }
307                  col = "orange";
308                }
309
310                const nom = t.name || typeLabel;
311                let adr = (t['addr:housenumber'] || '') + " " + (t['addr:street'] || '') + " " + (t['addr:postcode'] || '') + " " + (t['addr:city'] || '');
312                adr = adr.trim();
313                
314                if (!adr) {
315                  try {
316                    const revRes = await fetch('https://photon.komoot.io/reverse?lon=' + e_lon + '&lat=' + e_lat);
317                    if (revRes.ok) {
318                      const revData = await revRes.json();
319                      if (revData.features && revData.features.length > 0) {
320                        const rp = revData.features[0].properties;
321                        adr = ((rp.housenumber || '') + ' ' + (rp.street || '') + ' ' + (rp.postcode || '') + ' ' + (rp.city || '')).trim();
322                      }
323                    }
324                  } catch(errGeo){}
325                }
326                if (!adr) { adr = "Adresse non spécifiée"; }
327
328                currentData.push({ nom: nom, type: typeLabel, dist: calc_dist.toFixed(1), adr: adr, lat: e_lat, lon: e_lon, color: col, geom: polygonPoints });
329              }
330            }
331          }
332        } catch(e){}
333
334        try {
335          const requeteFiltrefiness = '(hopital OR hospitalier OR clinique OR ehpad OR "maison de retraite" OR "soins de suite" OR readaptation OR convalescence OR "personnes agees" OR dependantes OR "hebergement pour personnes") AND NOT (pharmacie OR officine OR laboratoire OR cabinet OR medecin OR fitness OR gym OR domicile OR infirmiere OR kine)';
336          const urlKoumoulFiltre = 'https://opendata.koumoul.com/data-fair/api/v1/datasets/etablissements-finess-france/lines?format=geojson&bbox=' + bbox + '&size=1000&q_mode=simple&qs=' + encodeURIComponent(requeteFiltrefiness);
337          const res = await fetch(urlKoumoulFiltre);
338          if(res.ok) {
339            const d = await res.json();
340            const feats = d.features || [];
341            for (let i = 0; i < feats.length; i++) {
342              const f = feats[i];
343              if (!f.geometry || !f.geometry.coordinates) continue;
344              const c = f.geometry.coordinates;
345              const dist = getDistance(lat, lon, c[1], c[0]);
346              if(dist <= rayon) {
347                const p = f.properties;
348                const nom = p.rs || p.rslongue || p.libcategetab || "Établissement de soins médicalisés";
349                
350                var existeDeja = currentData.some(function(x) { 
351                  return x.nom.toLowerCase().includes(nom.toLowerCase().substring(0, 6)) || getDistance(x.lat, x.lon, c[1], c[0]) < 35; 
352                });
353                
354                if (!existeDeja) {
355                  let adr = p.adresse ? String(p.adresse).trim() : "";
356                  if (!adr) {
357                    adr = ((p.nvoie || '') + ' ' + (p.tvoie || '') + ' ' + (p.lvoie || '')).trim();
358                  }
359                  if (!adr || adr.toLowerCase().includes("non specifiee")) {
360                    try {
361                      const revRes = await fetch('https://photon.komoot.io/reverse?lon=' + c[0] + '&lat=' + c[1]);
362                      if (revRes.ok) {
363                        const revData = await revRes.json();
364                        if (revData.features && revData.features.length > 0) {
365                          const rp = revData.features[0].properties;
366                          adr = ((rp.housenumber || '') + ' ' + (rp.street || '') + ' ' + (rp.postcode || '') + ' ' + (rp.city || '')).trim();
367                        }
368                      }
369                    } catch(errGeo){}
370                  }
371                  if (!adr) adr = "Adresse non répertoriée";
372                  currentData.push({ nom: nom, type: p.libcategetab || "Soins", dist: dist.toFixed(1), adr: adr, lat: c[1], lon: c[0], color: "orange", geom: null });
373                }
374              }
375            }
376          }
377        } catch(e){}
378
379        var coordMap = {};
380        currentData.forEach(function(item) {
381          var key = parseFloat(item.lat).toFixed(5) + "_" + parseFloat(item.lon).toFixed(5);
382          if (!coordMap[key]) { coordMap[key] = 0; }
383          coordMap[key]++;
384          if (coordMap[key] > 1) {
385            var angle = (coordMap[key] * 2 * Math.PI) / 8;
386            item.lat = parseFloat(item.lat) + 0.00014 * Math.sin(angle);
387            item.lon = parseFloat(item.lon) + 0.00014 * Math.cos(angle);
388          }
389        });
390
391        resultsZone.style.display = "block";
392        if (currentData.length === 0) {
393          textList.innerHTML = "<span style='color:red;'>Aucun établissement trouvé dans ce périmètre.</span>";
394        } else {
395          currentData.forEach(item => {
396            const div = document.createElement('div');
397            div.style.marginBottom = "8px";
398            div.innerHTML = "<b>• " + item.nom + "</b> (" + item.type + ") ➔ <b>" + item.dist + " m</b><br><span style='color:#666;'>📍 Adresse : " + item.adr + "</span>";
399            textList.appendChild(div);
400          });
401        }
402
403        if (!map) {
404          map = L.map('immo_map', { zoomControl: true, attributionControl: true });
405          markerGroup = L.layerGroup().addTo(map);
406          groupScolaire = L.layerGroup().addTo(map);
407          groupCreche = L.layerGroup().addTo(map);
408          groupSoins = L.layerGroup().addTo(map);
409
410          map.on('click', function(e) {
411            if (!modeMesure) return;
412            if (!premierPointMesure) {
413              premierPointMesure = e.latlng;
414              let m = L.circleMarker(premierPointMesure, { radius: 6, color: '#e91e63', fillColor: '#e91e63', fillOpacity: 1 }).addTo(map);
415              mesureMarkers.push(m);
416            } else {
417              let secondPoint = e.latlng;
418              let m = L.circleMarker(secondPoint, { radius: 6, color: '#e91e63', fillColor: '#e91e63', fillOpacity: 1 }).addTo(map);
419              mesureMarkers.push(m);
420              let d = getDistance(premierPointMesure.lat, premierPointMesure.lng, secondPoint.lat, secondPoint.lng);
421              
422              if (mesureLigne) map.removeLayer(mesureLigne);
423              mesureLigne = L.polyline([premierPointMesure, secondPoint], { color: '#e91e63', weight: 3, dashArray: '5, 5' }).addTo(map);
424              
425              let milieu = L.latLng((premierPointMesure.lat + secondPoint.lat) / 2, (premierPointMesure.lng + secondPoint.lng) / 2);
426              L.popup().setLatLng(milieu).setContent("<b>Distance mesurée : " + d.toFixed(1) + " m</b>").openOn(map);
427              
428              premierPointMesure = null;
429            }
430          });
431        } else {
432          markerGroup.clearLayers();
433          groupScolaire.clearLayers();
434          groupCreche.clearLayers();
435          groupSoins.clearLayers();
436          if(circle) map.removeLayer(circle);
437        }
438
439        map.setView([lat, lon], 18);
440        const planLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
441        const satLayer = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}');
442        
443        if(!map._layersControl) {
444          var optionsCalques = {
445            "Écoles (Violet)": groupScolaire,
446            "Crèches (Vert)": groupCreche,
447            "Soins médicalisés (Orange)": groupSoins
448          };
449          map._layersControl = L.control.layers({ "Plan standard": planLayer, "Vue Satellite": satLayer }, optionsCalques, { collapsed: false }).addTo(map);
450        }
451
452        L.marker([lat, lon], { icon: createCustomPin("blue") }).addTo(markerGroup).bindPopup("<b>Adresse cible :</b><br>" + currentAdresseSaisie).openPopup();
453        circle = L.circle([lat, lon], { radius: rayon, color: 'blue', weight: 2, fillColor: '#1a73e8', fillOpacity: 0.04 }).addTo(map);
454
455        currentData.forEach(item => {
456          var groupeCible = groupScolaire;
457          if (item.color === "green") groupeCible = groupCreche;
458          if (item.color === "orange") groupeCible = groupSoins;
459
460          L.marker([item.lat, item.lon], { icon: createCustomPin(item.color) })
461           .addTo(groupeCible)
462           .bindPopup("<b>" + item.nom + "</b><br>Type: " + item.type + "<br>Distance: " + item.dist + " m<br>Adresse: " + item.adr);
463
464          if (item.geom) {
465            L.polygon(item.geom, { color: item.color, weight: 2, fillColor: item.color, fillOpacity: 0.15 })
466             .addTo(groupeCible)
467             .bindPopup("<b>Enceinte foncière :</b><br>" + item.nom);
468          }
469        });
470
471        if (window.immoTitleControl) { map.removeControl(window.immoTitleControl); }
472        window.immoTitleControl = L.control({ position: 'topleft' });
473        window.immoTitleControl.onAdd = function() {
474          const d = L.DomUtil.create('div', 'map-overlay-title');
475          d.innerHTML = "<b>Analyse de proximité proposée par <a href='https://antennes-relais.fr' target='_blank'>https://antennes-relais.fr</a> société Immonetic</b>";
476          return d;
477        };
478        window.immoTitleControl.addTo(map);
479
480        if (window.immoFooterControl) { map.removeControl(window.immoFooterControl); }
481        window.immoFooterControl = L.control({ position: 'bottomleft' });
482        window.immoFooterControl.onAdd = function() {
483          const d = L.DomUtil.create('div', 'map-overlay-footer');
484          d.innerHTML = "Retrouvez nos actualités sur <a href='https://antennes-relais.fr/blog-antennes-relais.html' target='_blank'>notre blog</a>.<br><b style='color:#cc0000;'>Avertissement :</b> Données indicatives soumises à vérification visuelle obligatoire sur site (marge de sécurité de 150m appliquée). Voir nos <a href='https://antennes-relais.fr/outils-negociateurs.php' target='_blank'>conditions d'utilisation</a>.";
485          return d;
486        };
487        window.immoFooterControl.addTo(map);
488
489        if (window.immoLogoControl) { map.removeControl(window.immoLogoControl); }
490        window.immoLogoControl = L.control({ position: 'bottomright' });
491        window.immoLogoControl.onAdd = function() {
492          const d = L.DomUtil.create('div', 'map-overlay-logo');
493          d.innerHTML = "<img src='" + URL_LOGO + "' style='max-height:35px;' alt='Immonetic'>";
494          return d;
495        };
496        window.immoLogoControl.addTo(map);
497
498        setTimeout(() => { map.invalidateSize(); }, 300);
499
500        let refValue = refInput ? refInput.value.trim() : "";
501        let cleanRef = refValue ? refValue.replace(/[^a-zA-Z0-9-_ ]/g, '').replace(/\s+/g, '_') : "";
502        
503        let nameHtml = cleanRef ? cleanRef + ".html" : "carte_proximite_immonetic.html";
504        let nameCsv = cleanRef ? cleanRef + ".csv" : "liste_etablissements_150m.csv";
505        let nameKml = cleanRef ? cleanRef + ".kml" : "analyse_proximite_immonetic.kml";
506        
507        dlMap.setAttribute("download", nameHtml);
508        dlCsv.setAttribute("download", nameCsv);
509        dlKml.setAttribute("download", nameKml);
510
511        let csvContent = "\uFEFFNom;Type_Precis;Source;Distance (metres);Adresse\n";
512        currentData.forEach(item => {
513          var nClean = String(item.nom || "").split('"').join('').split("'").join(' ');
514          var tClean = String(item.type || "").split('"').join('').split("'").join(' ');
515          var aClean = String(item.adr || "").split('"').join('').split("'").join(' ');
516          csvContent += '"' + nClean + '";"' + tClean + '";"Immonetic";"' + item.dist + '";"' + aClean + '"\n';
517        });
518        csvContent += '\n"AVERTISSEMENT : Outil d\'information indicatif soumis à contrôle visuel direct sur site . Conditions d\'utilisation disponibles sur : https://antennes-relais.fr/outils-negociateurs.php";;;;\n';
519
520        const blobCsv = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
521        if(dlCsv.href) URL.revokeObjectURL(dlCsv.href);
522        dlCsv.href = URL.createObjectURL(blobCsv);
523
524        var kml = [];
525        kml.push('\x3C?xml version="1.0" encoding="UTF-8"?\x3E');
526        kml.push('\x3Ckml xmlns="http://www.opengis.net/kml/2.2"\x3E\x3CDocument\x3E');
527        kml.push('\x3Cname\x3E' + (refValue ? refValue : 'Analyse Immonetic - ' + currentAdresseSaisie.split('"').join('')) + '\x3C/name\x3E');
528        kml.push('\x3CStyle id="bluePoint"\x3E\x3CIconStyle\x3E\x3CIcon\x3E\x3Chref\x3Ehttp://maps.google.com/mapfiles/kml/paddle/blu-circle.png\x3C/href\x3E\x3C/Icon\x3E\x3C/IconStyle\x3E\x3C/Style\x3E');
529        kml.push('\x3CStyle id="purplePoint"\x3E\x3CIconStyle\x3E\x3CIcon\x3E\x3Chref\x3Ehttp://maps.google.com/mapfiles/kml/paddle/purple-circle.png\x3C/href\x3E\x3C/Icon\x3E\x3C/IconStyle\x3E\x3C/Style\x3E');
530        kml.push('\x3CStyle id="greenPoint"\x3E\x3CIconStyle\x3E\x3CIcon\x3E\x3Chref\x3Ehttp://maps.google.com/mapfiles/kml/paddle/grn-circle.png\x3C/href\x3E\x3C/Icon\x3E\x3C/IconStyle\x3E\x3C/Style\x3E');
531        kml.push('\x3CStyle id="orangePoint"\x3E\x3CIconStyle\x3E\x3CIcon\x3E\x3Chref\x3Ehttp://maps.google.com/mapfiles/kml/paddle/ylw-circle.png\x3C/href\x3E\x3C/Icon\x3E\x3C/IconStyle\x3E\x3C/Style\x3E');
532        kml.push('\x3CStyle id="zoneCircle"\x3E\x3CLineStyle\x3E\x3Ccolor\x3Effe8731a\x3C/color\x3E\x3Cwidth\x3E3\x3C/width\x3E\x3C/LineStyle\x3E\x3CPolyStyle\x3E\x3Ccolor\x3E40e8731a\x3C/color\x3E\x3C/PolyStyle\x3E\x3C/Style\x3E');
533        kml.push('\x3CPlacemark\x3E\x3Cname\x3EPoint Pivot Cible\x3C/name\x3E\x3Cdescription\x3E' + currentAdresseSaisie.split('"').join('') + '\x3C/description\x3E\x3CstyleUrl\x3E#bluePoint\x3C/styleUrl\x3E\x3CPoint\x3E\x3Ccoordinates\x3E' + lon + ',' + lat + ',0\x3C/coordinates\x3E\x3C/Point\x3E\x3C/Placemark\x3E');
534        kml.push('\x3CPlacemark\x3E\x3Cname\x3EPerimetre d\'inclusion (' + rayon + 'm)\x3C/name\x3E\x3CstyleUrl\x3E#zoneCircle\x3C/styleUrl\x3E\x3CPolygon\x3E\x3Ctessellate\x3E1\x3C/tessellate\x3E\x3CouterBoundaryIs\x3E\x3CLinearRing\x3E\x3Ccoordinates\x3E' + generateKMLCircle(lat, lon, rayon) + '\x3C/coordinates\x3E\x3C/LinearRing\x3E\x3C/outerBoundaryIs\x3E\x3C/Polygon\x3E\x3C/Placemark\x3E');
535        
536        var etsCount = 1;
537        currentData.forEach(function(m) {
538          var sId = m.color === "purple" ? "#purplePoint" : (m.color === "green" ? "#greenPoint" : "#orangePoint");
539          var etsName = "ETS" + etsCount;
540          etsCount++;
541          kml.push('\x3CPlacemark\x3E\x3Cname\x3E' + etsName + '\x3C/name\x3E\x3Cdescription\x3ENom complet : ' + String(m.nom).split('"').join('') + ' \nType : ' + m.type + ' \nDistance : ' + m.dist + ' m \nAdresse : ' + String(m.adr).split('"').join('') + '\x3C/description\x3E\x3CstyleUrl\x3E' + sId + '\x3C/styleUrl\x3E\x3CPoint\x3E\x3Ccoordinates\x3E' + m.lon + ',' + m.lat + ',0\x3C/coordinates\x3E\x3C/Point\x3E\x3C/Placemark\x3E');
542        });
543        kml.push('\x3C/Document\x3E\x3C/kml\x3E');
544        
545        const blobKml = new Blob([kml.join('\n')], { type: 'application/vnd.google-earth.kml+xml;charset=utf-8;' });
546        if(dlKml.href) URL.revokeObjectURL(dlKml.href);
547        dlKml.href = URL.createObjectURL(blobKml);
548
549        var h = [];
550        h.push('\x3C!DOCTYPE html\x3E\x3Chtml lang="fr"\x3E\x3Chead\x3E\x3Cmeta charset="UTF-8"\x3E');
551        h.push('\x3Ctitle\x3EAnalyse Cartographique - Immonetic\x3C/title\x3E');
552        h.push('\x3Clink rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" /\x3E');
553        h.push('\x3Cscript src="https://unpkg.com/[email protected]/dist/leaflet.js"\x3E\x3C/script\x3E');
554        h.push('\x3Cstyle\x3Ebody{margin:0;padding:0;} #map{width:100vw;height:100vh;} .o-box{background:white;padding:10px 14px;border-radius:6px;box-shadow:0 2px 6px rgba(0,0,0,0.25);font-size:13px;max-width:350px;font-family:sans-serif;} .o-foot{background:white;padding:8px 12px;border-radius:4px;box-shadow:0 2px 6px rgba(0,0,0,0.2);font-size:10px;font-family:sans-serif;max-width:320px;line-height:1.4;}\x3C/style\x3E');
555        h.push('\x3C/head\x3E\x3Cbody\x3E');
556        h.push('\x3Cbutton id="ruler_btn" style="position:absolute; top:50%; right:10px; transform:translateY(-50%); z-index:2000 !important; background:white; border:2px solid rgba(0,0,0,0.2); border-radius:4px; padding:8px 12px; font-weight:bold; cursor:pointer; font-family:sans-serif; font-size:12px; color:black; box-shadow:0 2px 6px rgba(0,0,0,0.3);"\x3E📐 Règle de mesure\x3C/button\x3E');
557        h.push('\x3Cdiv id="map"\x3E\x3C/div\x3E');
558        h.push('\x3Cscript\x3E');
559        h.push('var map = L.map("map").setView([' + lat + ',' + lon + '], 18);');
560        h.push('var plan = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map);');
561        h.push('var sat = L.tileLayer("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}");');
562        h.push('var gScol = L.layerGroup().addTo(map); var gCrech = L.layerGroup().addTo(map); var gSoin = L.layerGroup().addTo(map);');
563        h.push('L.control.layers({"Plan standard":plan,"Vue Satellite":sat},{"Écoles (Violet)":gScol,"Crèches (Vert)":gCrech,"Soins médicalisés (Orange)":gSoin},{collapsed:false}).addTo(map);');
564        h.push('var title = L.control({position:"topleft"}); title.onAdd = function(){ var d = L.DomUtil.create("div","o-box"); d.innerHTML="<b>Analyse de proximité proposée par \x3Ca href=\'https://antennes-relais.fr\' target=\'_blank\'\x3Ehttps://antennes-relais.fr\x3C/a\x3E société Immonetic</b><br><small>Adresse : ' + currentAdresseSaisie.split('"').join('').split("'").join(' ') + '</small>"; return d; }; title.addTo(map);');
565        h.push('var footer = L.control({position:"bottomleft"}); footer.onAdd = function(){ var d = L.DomUtil.create("div","o-foot"); d.innerHTML="Retrouvez nos actualités sur \x3Ca href=\'https://antennes-relais.fr/blog-antennes-relais.html\' target=\'_blank\'\x3Enotre blog\x3C/a\x3E.<br>\x3Cb style=\'color:#cc0000;\'\x3EAvertissement :\x3C/b\x3E Données indicatives soumises à vérification visuelle obligatoire sur site (marge de sécurité de 150m appliquée). Voir nos \x3Ca href=\'https://antennes-relais.fr/outils-negociateurs.php\' target=\'_blank\'\x3Econditions d\'utilisation\x3C/a\x3E."; return d; }; footer.addTo(map);');
566        h.push('var logo = L.control({position:"bottomright"}); logo.onAdd = function(){ var d = L.DomUtil.create("div"); d.innerHTML="\x3Cdiv style=\'background:rgba(255,255,255,0.9);padding:5px;border-radius:4px;box-shadow:0 1px 5px rgba(0,0,0,0.2);\'\x3E<img src=\'' + URL_LOGO + '\' style=\'max-height:45px;\'\x3E</div>"; return d; }; logo.addTo(map);');
567        h.push('L.marker([' + lat + ',' + lon + '],{icon:L.divIcon({className:"c-p-b",html:\'\x3Csvg width=\"30\" height=\"42\" viewBox=\"0 0 30 42\"\x3E\x3Cpath d=\"M15 0C6.71 0 0 6.71 0 15C0 26.25 15 42 15 42C15 42 30 26.25 30 15C30 6.71 23.29 0 15 0ZM15 20C12.24 20 10 17.76 10 15C10 12.24 12.24 10 15 10C17.76 10 20 12.24 20 15C20 17.76 17.76 20 15 20Z\" fill=\"#1a73e8\"/\x3E\x3Ccircle cx=\"15\" cy=\"15\" r=\"4\" fill=\"#FFFFFF\"/\x3E\x3C/svg\x3E\',iconSize:[30,42],iconAnchor:[15,42],popupAnchor:[0,-38]})}).addTo(map).bindPopup("<b>Point Pivot :</b><br>' + currentAdresseSaisie.split('"').join('').split("'").join(' ') + '").openPopup();');
568        h.push('L.circle([' + lat + ',' + lon + '],{radius:' + rayon + ',color:"blue",weight:2,fillColor:"#1a73e8",fillOpacity:0.04}).addTo(map);');
569        h.push('var markersData = ' + JSON.stringify(currentData) + ';');
570        h.push('markersData.forEach(function(m) {');
571        h.push('  var tG = gScol; if(m.color==="green") tG = gCrech; if(m.color==="orange") tG = gSoin;');
572        h.push('  var hexColor = m.color === "purple" ? "#a142f4" : (m.color === "orange" ? "#ff9900" : "#34a853");');
573        h.push('  L.marker([m.lat, m.lon],{icon:L.divIcon({className:"c-pin",html:\'\x3Csvg width=\"30\" height=\"42\" viewBox=\"0 0 30 42\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\x3E\x3Cpath d=\"M15 0C6.71 0 0 6.71 0 15C0 26.25 15 42 15 42C15 42 30 26.25 30 15C30 6.71 23.29 0 15 0ZM15 20C12.24 20 10 17.76 10 15C10 12.24 12.24 10 15 10C17.76 10 20 12.24 20 15C20 17.76 17.76 20 15 20Z\" fill=\"\' + hexColor + \'\"/\x3E\x3Ccircle cx=\"15\" cy=\"15\" r=\"4\" fill=\"#FFFFFF\"/\x3E\x3C/svg\x3E\',iconSize:[30,42],iconAnchor:[15,42],popupAnchor:[0,-38]})}
573).addTo(tG).bindPopup("\x3Cb\x3E" + m.nom + "\x3C/b\x3E\x3Cbr\x3EType: " + m.type + "\x3Cbr\x3EDistance: " + m.dist + " m\x3Cbr\x3EAdresse: " + m.adr);');
574        h.push('  if(m.geom) { L.polygon(m.geom, {color: m.color, weight: 2, fillColor: m.color, fillOpacity: 0.15}).addTo(tG).bindPopup("\x3Cb\x3EEnceinte foncière :\x3C/b\x3E\x3Cbr\x3E" + m.nom); }');
575        h.push('});');
576        h.push('var modeM = false; var pPt = null; var mMs = []; var mLg = null;');
577        h.push('document.getElementById("ruler_btn").addEventListener("click", function() {');
578        h.push('  modeM = !modeM;');
579        h.push('  if(modeM) { this.innerText = "📐 Règle active (Cliquez 2 points)"; this.style.background = "#e91e63"; this.style.color = "white"; map.getContainer().style.cursor = "crosshair"; }');
580        h.push('  else { this.innerText = "📐 Règle de mesure"; this.style.background = "white"; this.style.color = "black"; map.getContainer().style.cursor = ""; pPt = null; mMs.forEach(function(mk){map.removeLayer(mk);}); mMs=[]; if(mLg){map.removeLayer(mLg);mLg=null;} }');
581        h.push('});');
582        h.push('map.on("click", function(ev) {');
583        h.push('  if(!modeM) return;');
584        h.push('  if(!pPt) { pPt = ev.latlng; var mk = L.circleMarker(pPt, {radius:6, color:"#e91e63", fillColor:"#e91e63", fillOpacity:1}).addTo(map); mMs.push(mk); }');
585        h.push('  else { var sPt = ev.latlng; var mk = L.circleMarker(sPt, {radius:6, color:"#e91e63", fillColor:"#e91e63", fillOpacity:1}).addTo(map); mMs.push(mk); var dst = map.distance(pPt, sPt); if(mLg) map.removeLayer(mLg); mLg = L.polyline([pPt, sPt], {color:"#e91e63", weight:3, dashArray:"5,5"}).addTo(map); var ml = L.latLng((pPt.lat+sPt.lat)/2, (pPt.lng+sPt.lng)/2); L.popup().setLatLng(ml).setContent("\x3Cb\x3EDistance : " + dst.toFixed(1) + " m\x3C/b\x3E").openOn(map); pPt = null; }');
586        h.push('});');
587        h.push('\x3C/script\x3E\x3C/body\x3E\x3C/html\x3E');
588
589        const blobMap = new Blob([h.join('\n')], { type: 'text/html;charset=utf-8;' });
590        if(dlMap.href) URL.revokeObjectURL(dlMap.href);
591        dlMap.href = URL.createObjectURL(blobMap);
592
593        dlBox.style.display = "flex";
594        resultsZone.style.display = "block";
595
596      } catch (err) {
597        console.error(err);
598        alert("Erreur de calcul.");
599      } finally {
600        btn.innerText = "Lancer l'extraction";
601        btn.disabled = false;
602      }
603    });
604  }
605
606  if (document.readyState === "complete" || document.readyState === "interactive") {
607    executerScriptAnalyse();
608  } else {
609    window.addEventListener('DOMContentLoaded', executerScriptAnalyse);
610  }
611})();
612</script>
612</div><p><br></p></div></div></div><div class="twpara-row row-fluid"><div id="0xTLgHjf" class="span12 tw-para "><h2><span style="font-size:18px;">Compresser fichier&nbsp;</span></h2><div class="pobj obj-before" style="text-align:center;"><picture><source type="image/webp" srcset="_media/img/thumb/kling-20260702-image-image-pour-442-1.webp 160w,_media/img/small/kling-20260702-image-image-pour-442-1.webp 480w,_media/img/medium/kling-20260702-image-image-pour-442-1.webp 768w,_media/img/large/kling-20260702-image-image-pour-442-1.webp 1024w,_media/img/xlarge/kling-20260702-image-image-pour-442-1.webp 1205w"><source type="image/png" srcset="_media/img/thumb/kling-20260702-image-image-pour-442-1.png 160w,_media/img/small/kling-20260702-image-image-pour-442-1.png 480w,_media/img/medium/kling-20260702-image-image-pour-442-1.png 768w,_media/img/large/kling-20260702-image-image-pour-442-1.png 1024w,_media/img/xlarge/kling-20260702-image-image-pour-442-1.png 1205w"><img src="_media/img/thumb/kling-20260702-image-image-pour-442-1.png"  width="120" height="160" style="max-width:100%;width:120px;" alt="" loading="lazy"></picture></div><div class="ptext"><p><b>Confidentialité et sécurité de traitement :</b>Pour garantir une confidentialité absolue (100 % sécurisée), la compression est réalisée directement au sein de votre navigateur web. Le document PDF est traité localement par la mémoire vive de votre ordinateur grâce à des algorithmes de re-compression d'images binaires. <b>Le fichier ne quitte jamais votre poste de travail et aucune donnée ne transite sur Internet.</b> Vos baux, pièces d'identité et documents financiers restent strictement privés, vous assurant un respect rigoureux du secret professionnel et une conformité native avec le RGPD.</p><p><i>(Le test : vous pouvez utiliser ce compresseur en coupant complètement votre connexion Internet ou en Mode Avion, il fonctionnera parfaitement).</i></p><p><!-- 1. CHARGEMENT DES BIBLIOTHÈQUES SÉCURISÉES -->
613<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script>
vendor: 2 bytes, line 613
613
614<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
614
615
616<!-- 2. FEUILLE DE STYLE (DESIGN DE L'OUTIL) -->
617<style>
618  .compress-container { font-family: 'Segoe UI', Arial, sans-serif; max-width: 650px; margin: 20px auto; padding: 25px; background: #f8f9fa; border-radius: 12px; border: 1px solid #dadce0; color: #333; box-sizing: border-box; }
619  .compress-zone { border: 2px dashed #1a73e8; padding: 30px 20px; background: #fff; border-radius: 8px; text-align: center; cursor: pointer; transition: background 0.2s; }
620  .compress-zone:hover { background: #f1f3f4; }
621  .compress-icon { font-size: 40px; color: #1a73e8; margin-bottom: 10px; }
622  .compress-label { font-weight: 600; font-size: 14px; color: #3c4043; }
623  .compress-browse-btn { width: 100%; padding: 14px; background: #6c757d; color: white; border: none; border-radius: 6px; font-weight: bold; font-size: 15px; cursor: pointer; margin-top: 10px; }
624  .compress-meta-box { margin-top: 15px; padding: 10px; background: #e8f0fe; border-radius: 6px; font-size: 13px; display: none; text-align: left; border: 1px solid #1a73e8; }
625  .compress-options { margin-top: 20px; display: flex; flex-direction: column; gap: 12px; }
626  .compress-select { width: 100%; padding: 10px; border-radius: 6px; border: 1px solid #ccc; background: white; font-size: 14px; font-family: inherit; }
627  .compress-action-btn { width: 100%; padding: 14px; background: #1a73e8; color: white; border: none; border-radius: 6px; font-weight: bold; font-size: 15px; cursor: pointer; margin-top: 15px; }
628  .compress-action-btn:disabled { background: #9aa0a6; cursor: not-allowed; }
629  .compress-progress-wrapper { margin-top: 20px; display: none; }
630  .compress-bar-bg { width: 100%; height: 8px; background: #dadce0; border-radius: 4px; overflow: hidden; margin-top: 6px; }
631  .compress-bar-fill { width: 0%; height: 100%; background: #1a73e8; transition: width 0.1s; }
632  .compress-status-text { font-size: 12px; color: #5f6368; font-weight: bold; text-align: center; }
633  .compress-success-box { margin-top: 20px; padding: 15px; background: #e6f4ea; border: 1px solid #137333; border-radius: 8px; display: none; text-align: center; }
634  .compress-success-text { color: #137333; font-weight: bold; font-size: 14px; margin-bottom: 12px; }
635  .compress-dl-btn { display: inline-block; padding: 10px 20px; background: #137333; color: white; text-decoration: none; font-weight: bold; border-radius: 4px; font-size: 13px; }
636  .compress-secure-banner { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 10px; background: #e6f4ea; border-left: 4px solid #137333; border-radius: 4px; font-size: 11px; color: #137333; margin-top: 20px; font-weight: 500; }
637</style>
638
639<!-- 3. INTERFACE VISUELLE (HTML) -->
640<div class="compress-container">
641  <div class="compress-zone" id="comp_drop_zone">
642    <div class="compress-icon">📄</div>
643    <div class="compress-label" id="comp_drop_label">Glissez-déposez votre fichier PDF ici</div>
644  </div>
645
646  <button type="button" class="compress-browse-btn" id="comp_btn_parcourir">Ou cliquez ici pour parcourir vos fichiers</button>
647  <input type="file" id="comp_file_input" style="display:none;" accept="application/pdf">
648
649  <div class="compress-meta-box" id="comp_meta_box"></div>
650
651  <div class="compress-options">
652    <div>
653      <label class="compress-label" style="display:block; margin-bottom:5px;">Niveau de compression souhaité :</label>
654      <select id="comp_compress_level" class="compress-select">
655        <option value="recommandee">Haute Qualité Pro (Rendu ultra-net / Compression optimisée)</option>
656        <option value="forte">Compression standard (Qualité écran intermédiaire)</option>
657      </select>
658    </div>
659    <button id="comp_start_compress_btn" class="compress-action-btn" disabled>Compresser le document PDF</button>
660  </div>
661
662  <div class="compress-progress-wrapper" id="comp_progress_wrapper">
663    <div class="compress-status-text" id="comp_status_txt">Initialisation du traitement...</div>
664    <div class="compress-bar-bg">
665      <div class="compress-bar-fill" id="comp_bar_fill"></div>
666    </div>
667  </div>
668
669  <div class="compress-success-box" id="comp_success_box">
670    <div class="compress-success-text" id="comp_success_txt">Fichier compressé avec succès !</div>
671    <a id="comp_download_link" href="#" class="compress-dl-btn">📥 Télécharger le PDF Optimisé</a>
672  </div>
673</div>
674
675<!-- 4. LOGIQUE INTERNE AJUSTÉE (HAUTE NETTETÉ) -->
676<script>
677(function() {
678  function initialiserCompresseur() {
679    const fileInput = document.getElementById('comp_file_input');
680    const startBtn = document.getElementById('comp_start_compress_btn');
681    const btnParcourir = document.getElementById('comp_btn_parcourir');
682    
683    if (!fileInput || !startBtn || !btnParcourir) {
684      setTimeout(initialiserCompresseur, 250);
685      return;
686    }
687
688    const dropZone = document.getElementById('comp_drop_zone');
689    const dropLabel = document.getElementById('comp_drop_label');
690    const metaBox = document.getElementById('comp_meta_box');
691    const levelSelect = document.getElementById('comp_compress_level');
692    const progressWrapper = document.getElementById('comp_progress_wrapper');
693    const statusTxt = document.getElementById('comp_status_txt');
694    const barFill = document.getElementById('comp_bar_fill');
695    const successBox = document.getElementById('comp_success_box');
696    const successTxt = document.getElementById('comp_success_txt');
697    const downloadLink = document.getElementById('comp_download_link');
698
699    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js';
700
701    let fichierSelectionne = null;
702
703    btnParcourir.addEventListener('click', function() { fileInput.click(); });
704    fileInput.addEventListener('change', function(e) { if (e.target.files.length > 0) chargerFichierPDF(e.target.files[0]); });
705    dropZone.addEventListener('dragover', function(e) { e.preventDefault(); dropZone.style.background = '#e8f0fe'; });
706    dropZone.addEventListener('dragleave', function(e) { e.preventDefault(); dropZone.style.background = '#fff'; });
707    dropZone.addEventListener('drop', function(e) {
708      e.preventDefault(); dropZone.style.background = '#fff';
709      if (e.dataTransfer.files.length > 0) chargerFichierPDF(e.dataTransfer.files[0]);
710    });
711
712    function chargerFichierPDF(file) {
713      if (file.type !== 'application/pdf') { alert('Format PDF uniquement.'); return; }
714      fichierSelectionne = file;
715      let sizeMo = (file.size / (1024 * 1024)).toFixed(2);
716      dropLabel.innerText = 'Fichier prêt';
717      metaBox.innerHTML = '📋 <b>Nom :</b> ' + file.name + '<br>⚖️ <b>Poids d\'origine :</b> ' + sizeMo + ' Mo';
718      metaBox.style.display = 'block';
719      startBtn.disabled = false;
720      successBox.style.display = 'none';
721      progressWrapper.style.display = 'none';
722    }
723
724    startBtn.addEventListener('click', async function() {
725      if (!fichierSelectionne) return;
726
727      startBtn.disabled = true;
728      fileInput.disabled = true;
729      successBox.style.display = 'none';
730      progressWrapper.style.display = 'block';
731      barFill.style.width = '0%';
732
733      try {
734        let fileReader = new FileReader();
735        fileReader.onload = async function() {
736          try {
737            let typedarray = new Uint8Array(this.result);
738            let loadingTask = pdfjsLib.getDocument({data: typedarray});
739            let pdf = await loadingTask.promise;
740            let totalPages = pdf.numPages;
741
742            const { jsPDF } = window.jspdf;
743            let docPdf = null;
744
745            // NOUVEAUX RÉGLAGES AGRESSIFS SUR LA NETTETÉ (Cible 200 DPI+)
746            let scaleRatio = 2.2;  // Rendu très pixel-dense pour lisser les polices
747            let imgQuality = 0.82; // Qualité JPEG élevée pour éliminer le "baveux" autour du texte
748
749            if (levelSelect.value === 'forte') {
750              scaleRatio = 1.5;    // Équivalent à l'ancienne compression recommandée
751              imgQuality = 0.65;   
752            }
753
754            for (let i = 1; i <= totalPages; i++) {
755              statusTxt.innerText = 'Rendu Haute Définition de la page ' + i + ' / ' + totalPages + '...';
756              
757              let page = await pdf.getPage(i);
758              let viewport = page.getViewport({ scale: scaleRatio });
759              
760              let canvas = document.createElement('canvas');
761              let ctx = canvas.getContext('2d');
762              
763              // Amélioration de l'anti-aliasing natif du navigateur
764              ctx.imageSmoothingEnabled = true;
765              ctx.imageSmoothingQuality = 'high';
766              
767              canvas.height = viewport.height;
768              canvas.width = viewport.width;
769
770              let renderContext = { canvasContext: ctx, viewport: viewport };
771              await page.render(renderContext).promise;
772
773              let jpegUrl = canvas.toDataURL('image/jpeg', imgQuality);
774              let orientationMode = viewport.width > viewport.height ? 'l' : 'p';
775
776              if (i === 1) {
777                docPdf = new jsPDF({ orientation: orientationMode, unit: 'px', format: [viewport.width, viewport.height], compress: true });
778              } else {
779                docPdf.addPage([viewport.width, viewport.height], orientationMode);
780              }
781
782              docPdf.addImage(jpegUrl, 'JPEG', 0, 0, viewport.width, viewport.height, undefined, 'SLOW');
783
784              let pct = Math.round((i / totalPages) * 100);
785              barFill.style.width = pct + '%';
786              await new Promise(resolve => setTimeout(resolve, 15));
787            }
788
789            statusTxt.innerText = 'Finalisation du rendu Pro...';
790            let blobOutput = docPdf.output('blob');
791            
792            let sizeMoAncien = (fichierSelectionne.size / (1024 * 1024)).toFixed(2);
793            let sizeMoNouveau = (blobOutput.size / (1024 * 1024)).toFixed(2);
794
795            if (downloadLink.href) { URL.revokeObjectURL(downloadLink.href); }
796            downloadLink.href = URL.createObjectURL(blobOutput);
797            
798            let baseName = fichierSelectionne.name.substring(0, fichierSelectionne.name.lastIndexOf('.'));
799            downloadLink.setAttribute('download', baseName + '_compresse.pdf');
800
801            successTxt.innerHTML = '🎯 Opération réussie !<br>⚖️ Avant : ' + sizeMoAncien + ' Mo ➔ <b>Après : ' + sizeMoNouveau + ' Mo</b>';
802            successBox.style.display = 'block';
803            progressWrapper.style.display = 'none';
804
805          } catch (errInner) {
806            console.error(errInner);
807            alert('Erreur lors du traitement binaire.');
808          } finally {
809            startBtn.disabled = false;
810            fileInput.disabled = false;
811          }
812        };
813        fileReader.readAsArrayBuffer(fichierSelectionne);
814      } catch (errOuter) {
815        console.error(errOuter);
816        startBtn.disabled = false;
817        fileInput.disabled = false;
818      }
819    });
820  }
821
822  if (document.readyState === "complete" || document.readyState === "interactive") {
823    initialiserCompresseur();
824  } else {
825    window.addEventListener('DOMContentLoaded', initialiserCompresseur);
826  }
827})();
828</script>
828</p></div></div></div><div style="text-align:right;min-height:28px"><div class="twsharebtnbar-center"><div class="twsharebtn-margin wa-btn"><a href="whatsapp://send?text=Outils+n%C3%A9gociateurs+Etude+ZR+automatis%C3%A9e%20%0Ahttps%3A%2F%2Fantennes-relais.fr%2Foutils-negociateurs.html%235XxINOXi" class="wa_btn"><span>Partager</span></a></div><div class="twsharebtn-margin"><div id="fb-root"></div>
828<script async defer crossorigin="anonymous" src="https://connect.facebook.net/fr_FR/sdk.js#xfbml=1&version=v12.0"></script>
828<div class="fb-share-button" data-href="https://antennes-relais.fr/outils-negociateurs.html#5XxINOXi" data-layout="button" data-size="small"><a target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent("https://antennes-relais.fr/outils-negociateurs.html#5XxINOXi") + '&amp;src=sdkpreparse" class="fb-xfbml-parse-ignore" rel="noopener">Facebook</a></div></div><div class="twsharebtn-margin"><div class="fb-like" data-href="https://antennes-relais.fr/outils-negociateurs.html#5XxINOXi" data-width="100" data-layout="button" data-action="like" data-size="small" data-share="false">Facebook</div></div><div class="twsharebtn-margin"><a href="https://twitter.com/share" class="twitter-share-button" data-url="https://antennes-relais.fr/outils-negociateurs.html#5XxINOXi" data-lang="fr">Tweet</a></div><div class="twsharebtn-margin">
828<script src="https://platform.linkedin.com/in.js" type="text/javascript"> lang: fr_FR</script>
828<script type="IN/Share" data-counter="right"></script>
828</div><div class="twsharebtn pi-btn"><a data-pin-do="buttonPin" data-pin-count="not shown" data-pin-lang="fr" href="https://www.pinterest.com/pin/create/button/?url=https://antennes-relais.fr/outils-negociateurs.html#5XxINOXi&description=Outils+n%C3%A9gociateurs+Etude+ZR+automatis%C3%A9e">Pinterest</a></div></div></div></div><div id="top-sb" class="span3"><div id="SXkEnN8J" class="tw-para "><h2><span style="font-size:18px;">Présentation Vidéo</span></h2><div class="pobj obj-before" style="width:480px;margin-left:auto;margin-right:auto;"><div id="vid_SXkEnN8J" class="wrapper-with-intrinsic-ratio" style="padding-top:30px; padding-bottom:56.25%"><iframe class="element-to-stretch" width="100%" height="100%" src="https://www.youtube.com/embed/W8AfhI9sv1U?fs=1" frameborder="0" allowfullscreen></iframe></div></div><div class="ptext">- <a href="_iserv/dlfiles/dl.php?ddl=documentation-immonetic-zr-automatisee.pdf" id="lnk256d2e73">plaquette présentation</a><div>- <a href="_iserv/dlfiles/dl.php?ddl=engagements-confidentialite-immonetic.pdf" id="lnk056e45d2">engagements de confidentialité</a></div></div></div></div></div></div></main><footer><div id="footersmall" class="row-fluid"><div id="foot-sec1" class="span6 ">© Copyright Antennes Relais. Immonetic - Tous droits réservés.<div><a href="https://antennes-relais.fr/_iserv/twsc/withdrawal.php?lang=fr" title="Exercer votre droit de rétractation en 1 clic" id="lnk952f825c">Exercer votre droit de rétractation en 1 clic</a></div></div><div id="foot-sec2" class="span6 "><div style="text-align: right; "><a href="_tos.html" title="conditions générales de vente et d'utilisation" rel="nofollow">CGUV</a></div></div></div><div id="footerfat" class="row-fluid"><div class="row-fluid"><div  id="footerfat_s1" class="span4 tw-para"><span style="color:#DDDDDD;">Contactez-nous</span><hr><span style="color:#FF9900;">Immonetic<br>32 bd de Strasbourg - 75010 Paris<br><span style="font-size:1.33em;">☎&nbsp;</span>(+33) 07 56 98 59 56&nbsp;</span><br><a onclick="javascript:return(decMail2(this));" href="znvygb?pbagnpg#nagraarf-erynvf.se" title="contact">[email protected]</a>&nbsp;<div><br></div><div>Ils parlent de nous<ul><li><a href="https://www.lepoint.fr/stories/tout-savoir-des-antennes-relais-le-guichet-immonetic-30-07-2024-2566752_3919.php" id="lnkce64e12b">Le Point</a></li><li><a href="https://monimmeuble.com/actualite/installation-dantennes-relais-un-nouveau-service-pour-les-coproprietes" title="mise en avant de notre service optim" rel="nofollow" id="lnka29004e3">Copropriété</a></li><li><a href="https://lavienumerique.com/articles/199639/antennes-relaisfr-by-immonetic-la-nouvelle-plateforme-de-reference-pour-obtenir-des-informations-objectives-sur-les-antennes-relais.html" rel="nofollow" id="lnk16826ad9">Vie numérique</a></li><li><a href="https://www.connaitre-ses-droits.net/bail-dantenne-relais-sur-un-terrain-prix-et-conditions-de-la-location/" rel="nofollow" id="lnk6f180528">Droits</a></li><li><a href="https://www.copropriete-travaux.com/amener-un-revenu-supplementaire-a-la-copropriete/" title="https://www.copropriete-travaux.com/amener-un-revenu-supplementaire-a-la-copropriete/" rel="nofollow" id="lnk3dc1bd48">copropriété travaux</a></li><li><a href="https://www.lebloginfos.fr/les-antennes-relais-pionnieres-de-la-communication-moderne/" rel="nofollow" id="lnk2a8d8543">Blog</a></li></ul></div></div><div  id="footerfat_s2" class="span4 tw-para"><p><font color="#dddddd">Notice Légale</font></p><hr><ul><li><a href="mentions-legales-et-donnees-personnelles.html" title="Notice Légale" rel="nofollow" id="lnkc532bf80">Notice Légale</a></li><li><a href="_tos.html" id="lnk388e3a40" rel="nofollow">CGUV</a></li><li><span style="color:#FF9900;">Crédit Photo AdobeStock_261994683</span><span style="color:#FF9966;">&nbsp;</span></li></ul></div><div  id="footerfat_s3" class="span4 tw-para"><font color="#dddddd">Nos rubriques</font><div><hr><ul><li><a href="guide-complet.html" id="lnk7c904adf">Guide</a></li><li><a href="https://forum.antennes-relais.fr" id="lnk87af49c7">Forum</a></li><li><a href="./" title="acceuil antennes relais">Accueil&nbsp;</a></li><li><a href="veille-antennes-relais.html" id="lnk08016ee7">Veille</a></li><li><a href="posez-votre-question-antennes-relais.html" title="Une question sur les antennes relais" id="lnk141bbcda">Question</a></li><li><a href="#?twscroll" title="services antennes relais pour opérateur et gestionnaire d’infrastru
828ctures" id="lnk48b76e6e">Services opérateurs</a>&nbsp;</li><li><a href="mesures-de-champs-radioelectriques-protocole-anfr.html" title="Services antennes relais pour propriétaire">Services propriétaire</a></li><li><a href="actualites-jurisprudence-antennes-relais-outils-aide-decision.html" title="actualités antennes relais">Actualités</a></li><li><a href="immonetic-expertise-antennes-relais.html" title="Immonetic" rel="nofollow">Qui sommes nous ?</a></li><li><a href="_iserv/dlfiles/dl.php?ddl=cp-immonetic-20230829-hd.pdf" title="communiqué de presse antennes relais" id="lnkf1a05920">Plaquette</a></li><li><a href="contact-immonetic-demande-information-antennes-relais.html" title="Contac" id="lnk177a7f0e">Contact</a><!-- Sendinblue Conversations {literal} -->
829<script>
830    (function(d, w, c) {
831        w.SibConversationsID = '6326c06bb641c1495d1c9a47';
832        w[c] = w[c] || function() {
833            (w[c].q = w[c].q || []).push(arguments);
834        };
835        var s = d.createElement('script');
836        s.async = true;
837        s.src = 'https://conversations-widget.sendinblue.com/sib-conversations.js';
838        if (d.head) d.head.appendChild(s);
839    })(document, window, 'SibConversations');
840</script>
840
841<!-- /Sendinblue Conversations {/literal} -->&nbsp;
841<script type="text/javascript">
842    var _ae = {
843        "pid":"666a76c09cc1f071c996f3f3",
844    };
845    (function() {
846        var element = document.createElement('script'); element.type = 'text/javascript'; element.async = true;
847        element.src = '//static.affilae.com/ae-v3.5.js';
848        var scr = document.getElementsByTagName('script')[0]; scr.parentNode.insertBefore(element, scr);
849    })();
850</script>
850<!-- Entrez le script du champ ici. Ce contenu sera inséré tel quel dans votre site. //--></li></ul></div></div></div></div></footer></div></div>
850<script src="_scripts/jquery/jquery.min.js"></script>
850<script src="_scripts/jquery/ui/tw-dialog.js"></script>
850<script src="_scripts/bootstrap/js/bootstrap.min.js"></script>
850<script async defer src="https://assets.pinterest.com/js/pinit.js"></script>
850<script>jQuery(window).on("load",function(){$(".IN-widget").css("vertical-align","top");$(".twitter-tweet-button").css("vertical-align","top");});!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");function ucc(){var a=sessionStorage.getItem("9d8b-cart")||"";if(!a||a=="")b=0;else{var b=0;var c=0;do{c=a.indexOf(")",c);if(c>0){e=c-1;while(e>=0&&a[e]!="=")e--;b+=parseInt(a.substring(e+1,c));c++}}while(c>0)}$("#sc_pcount").text(b)}function showParag(id){if(id.length>1&&id[0]!="#")id="#"+id;var p=$(id).is("a")?$(id).parent().parent():$(id).parent();$("html,body").animate({scrollTop:p.offset().top},"slow");}$("a[href$='?twscroll']").click(function(){var bk=($(this).attr("href")).split("?")[0];$(this).attr("href",bk);if(bk=="#"){$("html,body").animate({scrollTop:0},"slow");}else{if($(bk+"_container").length)bk+="_container";$("html,body").animate({scrollTop:($(bk).offset()).top},"slow");}});
851function closeTopBanner(){sessionStorage.setItem("12b85caf23689fea8c1bd9713f717893","1");$("#topBanner").remove();moveContent();}function moveContent(){var hh=$("header").outerHeight()+"px";$("#content").css({"position":"relative","top":hh});$("footer").css({"position":"relative","top":hh,"margin-bottom":hh});}function decMail2(e){var s=""+e.href,n=s.lastIndexOf("/"),w;if(s.substr(0,7)=="mailto:")return(true);if(n>0)s=s.substr(n+1);s=s.replace("?",":").replace("#","@").replace(/[a-z]/gi,function(t){return String.fromCharCode(t.charCodeAt(0)+(t.toLowerCase()<"n"?13:-13));});e.href=s;return(true);}function onChangeSiteLang(href){var i=location.href.indexOf("?");if(i>0)href+=location.href.substr(i);document.location.href=href;}</script>
851<script>$(document).ready(function(){ucc();$("#button-cart").popover({placement:"bottom",html:true,content:function(){return sessionStorage.getItem("9d8b-popover")||"<small>VOTRE PANIER EST VIDE</small>"},trigger:"hover"});$("#searchbox>input").click(function(){$(this).select();});$("#searchbox").click(function(e){if(e.offsetX>e.target.width){}else $("#searchbox").submit()});var clickmessage="Le clic droit n'est pas autorisé sur les images de ce site!";function disableclick(e){if(document.all){if(event.button==2||event.button==3){if(event.srcElement.tagName=="IMG"){if(clickmessage!="")alert(clickmessage);return false;}}}else if(document.layers){if(e.which==3){if(clickmessage!= "")alert(clickmessage);return false;}}else if(document.getElementById){if(e.which==3&&e.target.tagName=="IMG" ){if(clickmessage!="")alert(clickmessage);return false;}}}function associateimages(){for(i=0;i<document.images.length;i++)document.images[i].onmousedown=disableclick;}if(document.all){document.onmousedown=disableclick;} else if(document.getElementById){document.onmouseup=disableclick;}else if(document.layers)associateimages();if($(window.location.hash).length)showParag(window.location.hash);if(location.href.indexOf("?")>0&&location.href.indexOf("twtheme=no")>0){$("#toolbar1,#toolbar2,#toolbar3,#footersmall,#footerfat").hide();var idbmk=location.href;idbmk=idbmk.substring(idbmk.lastIndexOf("#")+1,idbmk.lastIndexOf("?"));if(idbmk!=""){idbmk=idbmk.split("?")[0];if($("#"+idbmk+"_container").length)idbmk+="_container";$("html,body").animate({scrollTop:$("#"+idbmk).offset().top},0);}}$(".dropdown-toggle").dropdown();
851$("#site").prepend("<a href='javascript:void(0)' class='toTop' title='Haut de page'><i class='fa fa-arrow-circle-up fa-3x toTopLink'></i></a>");var offset=220;var duration=500;$(window).scroll(function(){if($(this).scrollTop()>offset){$(".toTop").fadeIn(duration);}else{$(".toTop").fadeOut(duration);}});$(".toTop").click(function(event){event.preventDefault();$("html, body").animate({scrollTop:0},duration);return(false);});if(sessionStorage.getItem("12b85caf23689fea8c1bd9713f717893")!="1"){var html='<div id="topBanner" class="alert alert-success shadow fill-available" style="margin:0;border-radius:0;position:relative;z-index:9998;padding-left:180% !important;padding-right:180% !important;margin-left:-180% !important;margin-right:-180% !important;font-size:100%;border-style:solid;border-width:0 0 1px;"><div class="alert-success" style="display:flex;justify-content:center;align-items:center;max-width:940px;"><div style="text-align:center;">Nouveau : Gagnez du temps dans votre prospection en générant de façon automatisée l\'étude de votre ZR ? outils négociateurs dans Opérateur/Gestionnaire</div><div class="closetb" onclick="closeTopBanner()" style="font-size:16px"><i class="fa fa-close"></i></div></div></div>';$("header").prepend(html);}prevScrollpos=window.pageYOffset;var siteMaxWidth=$("#page").css("max-width");if(siteMaxWidth!="none"){for(var i=1;i<=3;i++)$("header").find("#toolbar"+i).css("max-width",siteMaxWidth);$("header").css({"left":"max(0px,50% - "+siteMaxWidth+" / 2)","right":"max(0px,50% - "+siteMaxWidth+" / 2)"});}$("#site").css({"overflow-y":"hidden"});$("header").css({"position":"fixed","top":"0","z-index":"9998","background-color":"hsla(0,0%,0%,0.8)","transition":"top 1s"});$("header").addClass("fill-available");moveContent();$(window).resize(moveContent);$(".btn-navbar").first().on("click",function(){if($(this).hasClass("collapsed")){$("header").css({"position":"relative"});}else{$("header").css({"position":"fixed"});}});$(window).on("scroll",function(){currentScrollPos=window.pageYOffset;if(currentScrollPos<0)return;if(currentScrollPos==0){$("header").css({"zoom":"100%"});}else{$("header").css({"zoom":"75%"});}if(currentScrollPos==0){$("header").css({"box-shadow":"none"});$("header").css({"zoom":"100%"})}else{$("header").css({"box-shadow":"rgba(0, 0, 0, 0.1) 0px 4px 4px 0px"});$("header").css({"zoom":"75%"});}});if(typeof onTOWebPageLoaded=="function")onTOWebPageLoaded();});</script>
851</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.