diff --git a/.easya_info.json b/.easya_info.json
index f4c89a06de7dc98fd195d73f97706b874e27db27..af64a9b8f5afd94d0e107660d0cd2c7f9a4e0be2 100644
--- a/.easya_info.json
+++ b/.easya_info.json
@@ -1,6 +1,6 @@
 {
     "dlb_min_version": 14,
-    "dlb_max_version": 20,
+    "dlb_max_version": 21,
     "php_min_version": 7.0,
     "php_max_version": 8.2
 }
diff --git a/.gitignore b/.gitignore
index 394733a07653e379814ce4a36e2bbdd14b4299c5..79da6a83fddd4bf3c2cf63a768eee503b78d2e48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,23 @@
+# Generated binaries
+/build/*.zip
+/bin/*.zip
+# Doxygen generated documentation
+/build/doxygen/doxygen_warnings.log
+/doc/code/doxygen
+# Composer managed dependencies
+/vendor
+/dev/bin
+# PHPdocumentor generated files
+/build/phpdoc
+/doc/code/phpdoc
+# Sphinx generated files
+/doc/user/build
 /.settings/
+/.buildpath
 /.project
+# PHPStorm generated files
 /.idea/
+# Other
+*.back
+/.editorconfig
+/.gitattributes
diff --git a/ChangeLog.md b/ChangeLog.md
index 667cbe2704548060446f5f58ef607fc9ad5a2d77..a5ca24d6bf5d1a5320d03c0758be5946f0fef402 100755
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -3,6 +3,14 @@ Le format du fichier est basé sur [Tenez un ChangeLog](http://keepachangelog.co
 
 ## [Non Distribué]
 
+## [14.0.19] - 31-03-2025
+- FIX : Compatibilité v21
+
+## [14.0.18] - 25-03-2025
+- Ajout de la librairy proj4php pour la convertion des coordonnées
+- Corrections de codes et deplacement de fonctions dans la classe ProjectingMap
+- Changement de methode pour la prise en compte de coordonnées envoyées par d'autre lors de la creation/modification d'un tiers
+
 ## [14.0.17] - 10-12-2024
 - Correction DROP FOREIGN KEY
 
@@ -96,7 +104,9 @@ Le format du fichier est basé sur [Tenez un ChangeLog](http://keepachangelog.co
 ## [4.0.0] - 10-09-2018
 - Version initial.
 
-[Non Distribué]: http://git.open-dsi.fr/dolibarr-extension/prospectingmap/compare/14.0.17...HEAD
+[Non Distribué]: http://git.open-dsi.fr/dolibarr-extension/prospectingmap/compare/14.0.19...HEAD
+[14.0.19]: http://git.open-dsi.fr/dolibarr-extension/prospectingmap/commits/14.0.19
+[14.0.18]: http://git.open-dsi.fr/dolibarr-extension/prospectingmap/commits/14.0.18
 [14.0.17]: http://git.open-dsi.fr/dolibarr-extension/prospectingmap/commits/14.0.17
 [14.0.16]: http://git.open-dsi.fr/dolibarr-extension/prospectingmap/commits/14.0.16
 [14.0.15]: http://git.open-dsi.fr/dolibarr-extension/prospectingmap/commits/14.0.15
diff --git a/VERSION b/VERSION
index 64cef64fb743f6c58f6ceb6b8dde412c29c3d16b..7db8b94255013e386496f8f35550f433060eae2c 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-14.0.17
\ No newline at end of file
+14.0.19
diff --git a/admin/setup.php b/admin/setup.php
index 3d66de32b8669565e7e7fd26f2e0a03e2299bafd..7f2de399af63798977e566284d4ea90d0b666ae7 100755
--- a/admin/setup.php
+++ b/admin/setup.php
@@ -23,72 +23,103 @@
  */
 
 // Change this following line to use the correct relative path (../, ../../, etc)
-$res=0;
-if (! $res && file_exists("../../main.inc.php")) $res=@include '../../main.inc.php';        // to work if your module directory is into a subdir of root htdocs directory
-if (! $res && file_exists("../../../main.inc.php")) $res=@include '../../../main.inc.php';  // to work if your module directory is into a subdir of root htdocs directory
-if (! $res) die("Include of main fails");
-require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
+$res = 0;
+if (!$res && file_exists("../../main.inc.php")) $res = @include '../../main.inc.php';        // to work if your module directory is into a subdir of root htdocs directory
+if (!$res && file_exists("../../../main.inc.php")) $res = @include '../../../main.inc.php';  // to work if your module directory is into a subdir of root htdocs directory
+if (!$res) die("Include of main fails");
+require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php';
 dol_include_once('/prospectingmap/lib/prospectingmap.lib.php');
+dol_include_once('/prospectingmap/class/prospectingmap.class.php');
 
-$langs->load("admin");
-$langs->load("prospectingmap@prospectingmap");
-$langs->load("opendsi@prospectingmap");
+$langs->loadLangs([ "admin", "prospectingmap@prospectingmap", "opendsi@prospectingmap" ]);
 
 if (!$user->admin) accessforbidden();
 
-$action = GETPOST('action','alpha');
+$action = GETPOST('action', 'alpha');
 
 
-/*
- *  Actions
+/**
+ * Action
  */
+$error = 0;
+$errors = array();
+$prospectingmap = new ProspectingMap($db);
 
 if ($action == 'set_map_settings') {
-    $main_layer_map = GETPOST('PROSPECTINGMAP_MAIN_LAYER_MAP', 'alpha');
+	$value = GETPOST('PROSPECTINGMAP_MAIN_LAYER_MAP', 'alphanohtml');
+	if (empty($value)) $value = 'OSM';
+	$res = dolibarr_set_const($db, 'PROSPECTINGMAP_MAIN_LAYER_MAP', $value, 'chaine', 0, '', $conf->entity);
+	if (!$res > 0) {
+		$errors[] = $db->lasterror();
+		$error++;
+	}
+} elseif ($action == 'set_search_coordinates_settings') {
+	$value = GETPOST('PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS', 'alphanohtml');
+	if (empty($value)) $value = 'https://nominatim.openstreetmap.org/search';
+	$res = dolibarr_set_const($db, 'PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS', $value, 'chaine', 0, '', $conf->entity);
+	if (!$res > 0) {
+		$errors[] = $db->lasterror();
+		$error++;
+	}
 
-    $main_layer_map = empty($main_layer_map) ? 'OSM' : $main_layer_map;
+	$value = GETPOST('PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS', 'alphanohtml');
+	if (empty($value)) $value = 'polygon_geojson=1&format=json&q=__ADDRESS__';
+	$res = dolibarr_set_const($db, 'PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS', $value, 'chaine', 0, '', $conf->entity);
+	if (!$res > 0) {
+		$errors[] = $db->lasterror();
+		$error++;
+	}
+
+	$value = GETPOST('PROSPECTINGMAP_SEARCH_COORDINATES_REQUEST_MODE', 'alphanohtml');
+	if (empty($value)) $value = 'GET';
+	$res = dolibarr_set_const($db, 'PROSPECTINGMAP_SEARCH_COORDINATES_REQUEST_MODE', $value, 'chaine', 0, '', $conf->entity);
+	if (!$res > 0) {
+		$errors[] = $db->lasterror();
+		$error++;
+	}
+
+	$value = GETPOST('PROSPECTINGMAP_COORDINATES_METRICS', 'alphanohtml');
+	if (empty($value)) $value = 'EPSG:3857';
+	$res = 0;
+	if (!empty($conf->global->PROSPECTINGMAP_COORDINATES_METRICS)) $res = $prospectingmap->convertAllThirdPartyCoordinates($conf->global->PROSPECTINGMAP_COORDINATES_METRICS, $value);
+	if ($res > 0) {
+		$res = dolibarr_set_const($db, 'PROSPECTINGMAP_COORDINATES_METRICS', $value, 'chaine', 0, '', $conf->entity);
+		if (!$res > 0) {
+			$errors[] = $db->lasterror();
+			$error++;
+		}
+	} elseif ($res < 0) {
+		$errors[] = $prospectingmap->errorsToString();
+		$error++;
+	}
+} elseif (preg_match('/set_(.*)/',$action,$reg)) {
+	$code = $reg[1];
+	$value = (GETPOST($code) ? GETPOST($code) : 1);
+
+	$res = dolibarr_set_const($db, $code, $value, 'chaine', 0, '', $conf->entity);
+	if (!$res > 0) {
+		$error++;
+		$errors[] = $db->lasterror();
+	}
+} elseif (preg_match('/del_(.*)/',$action,$reg)) {
+	$code = $reg[1];
 
-    dolibarr_set_const($db, 'PROSPECTINGMAP_MAIN_LAYER_MAP', $main_layer_map, 'chaine', 0, '', $conf->entity);
+	$res = dolibarr_del_const($db, $code, $conf->entity);
+	if (!$res > 0) {
+		$error++;
+		$errors[] = $db->lasterror();
+	}
 }
-elseif ($action == 'set_search_coordinates_settings') {
-    $url_address = GETPOST('PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS', 'alpha');
-    $url_parameters = GETPOST('PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS', 'alpha');
-    $request_mode = GETPOST('PROSPECTINGMAP_SEARCH_COORDINATES_REQUEST_MODE', 'alpha');
-	$coordinates_metrics = GETPOST('PROSPECTINGMAP_COORDINATES_METRICS', 'alpha');
-
-    $url_address = empty($url_address) ? 'https://nominatim.openstreetmap.org/search' : $url_address;
-    $url_parameters = empty($url_parameters) ? 'polygon_geojson=1&format=json&q=__ADDRESS__' : $url_parameters;
-    $request_mode = empty($request_mode) ? 'GET' : $request_mode;
-	$coordinates_metrics = empty($coordinates_metrics) ? 'EPSG:3857' : $coordinates_metrics;
-
-    dolibarr_set_const($db, 'PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS', $url_address, 'chaine', 0, '', $conf->entity);
-    dolibarr_set_const($db, 'PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS', $url_parameters, 'chaine', 0, '', $conf->entity);
-    dolibarr_set_const($db, 'PROSPECTINGMAP_SEARCH_COORDINATES_REQUEST_MODE', $request_mode, 'chaine', 0, '', $conf->entity);
-	if (prospectingmap_convert_all_coordinate($db, $conf->global->PROSPECTINGMAP_COORDINATES_METRICS, $coordinates_metrics, $error_msg) > 0) {
-		dolibarr_set_const($db, 'PROSPECTINGMAP_COORDINATES_METRICS', $coordinates_metrics, 'chaine', 0, '', $conf->entity);
+
+if ($action != '') {
+	if ($error) {
+		setEventMessages('', $errors, 'errors');
 	} else {
-		setEventMessage($error_msg, 'errors');
+		setEventMessage($langs->trans('SetupSaved'));
+		header('Location: ' . $_SERVER["PHP_SELF"]);
+		exit();
 	}
 }
-elseif (preg_match('/set_(.*)/',$action,$reg)) {
-    $code = $reg[1];
-    $value = (GETPOST($code) ? GETPOST($code) : 1);
-    if (dolibarr_set_const($db, $code, $value, 'chaine', 0, '', $conf->entity) > 0) {
-        Header("Location: " . $_SERVER["PHP_SELF"]);
-        exit;
-    } else {
-        dol_print_error($db);
-    }
-}
-elseif (preg_match('/del_(.*)/',$action,$reg)) {
-    $code = $reg[1];
-    if (dolibarr_del_const($db, $code, $conf->entity) > 0) {
-        Header("Location: " . $_SERVER["PHP_SELF"]);
-        exit;
-    } else {
-        dol_print_error($db);
-    }
-}
 
 
 /*
@@ -99,11 +130,11 @@ $form = new Form($db);
 
 llxHeader();
 
-$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php">'.$langs->trans("BackToModuleList").'</a>';
-print load_fiche_titre($langs->trans("ProspectingMapSetup"),$linkback,'title_setup');
+$linkback = '<a href="' . DOL_URL_ROOT . '/admin/modules.php">' . $langs->trans("BackToModuleList") . '</a>';
+print load_fiche_titre($langs->trans("ProspectingMapSetup"), $linkback, 'title_setup');
 print "<br>\n";
 
-$head=prospectingmap_prepare_head();
+$head = prospectingmap_prepare_head();
 
 dol_fiche_head($head, 'settings', $langs->trans("Module163021Name"), 0, 'opendsi@prospectingmap');
 
@@ -113,32 +144,32 @@ print '<br>';
 /**
  * Map settings.
  */
-print load_fiche_titre($langs->trans("ProspectingMapSettings"),'','');
+print load_fiche_titre($langs->trans("ProspectingMapSettings"), '', '');
 /*
 print '<form method="post" action="'.$_SERVER["PHP_SELF"].'">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print '<input type="hidden" name="action" value="set_map_settings">';
 */
-$var=true;
-print '<table class="noborder" width="100%">';
+$var = true;
+print '<table class="noborder centpercent">';
 print '<tr class="liste_titre">';
-print '<td>'.$langs->trans("Parameters").'</td>'."\n";
-print '<td align="right">'.$langs->trans("Value").'</td>'."\n";
+print '<td>' . $langs->trans("Parameters") . '</td>' . "\n";
+print '<td class="right">' . $langs->trans("Value") . '</td>' . "\n";
 print "</tr>\n";
 
 // PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD
 $var = !$var;
-print '<tr ' . $bc[$var] . '>' . "\n";
+print '<tr class="oddeven">' . "\n";
 print '<td>' . $langs->trans("ProspectingMapShowMapIntoCompanyCard") . '</td>' . "\n";
-print '<td align="right">' . "\n";
+print '<td class="right">' . "\n";
 if (!empty($conf->use_javascript_ajax)) {
-    print ajax_constantonoff('PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD');
+	print ajax_constantonoff('PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD');
 } else {
-    if (empty($conf->global->PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD)) {
-        print '<a href="' . $_SERVER['PHP_SELF'] . '?action=set_PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD">' . img_picto($langs->trans("Disabled"), 'switch_off') . '</a>';
-    } else {
-        print '<a href="' . $_SERVER['PHP_SELF'] . '?action=del_PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD">' . img_picto($langs->trans("Enabled"), 'switch_on') . '</a>';
-    }
+	if (empty($conf->global->PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD)) {
+		print '<a href="' . $_SERVER['PHP_SELF'] . '?action=set_PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD">' . img_picto($langs->trans("Disabled"), 'switch_off') . '</a>';
+	} else {
+		print '<a href="' . $_SERVER['PHP_SELF'] . '?action=del_PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD">' . img_picto($langs->trans("Enabled"), 'switch_on') . '</a>';
+	}
 }
 print '</td></tr>' . "\n";
 /*
@@ -147,7 +178,7 @@ print '<tr class="oddeven">'."\n";
 print '<td>'.$langs->trans("ProspectingMapMainLayerMap").'</td>'."\n";
 print '<td class="nowrap">'."\n";
 $map_choices = array(
-    'OSM' => 'OpenStreetMap',
+	'OSM' => 'OpenStreetMap',
 );
 print $form->selectarray("PROSPECTINGMAP_MAIN_LAYER_MAP", $map_choices, $conf->global->PROSPECTINGMAP_MAIN_LAYER_MAP);
 print '</td></tr>'."\n";
@@ -165,52 +196,51 @@ print '</form>';
 /**
  * Search coordinates settings.
  */
-print load_fiche_titre($langs->trans("ProspectingMapSearchCoordinatesSettings"),'','');
+print load_fiche_titre($langs->trans("ProspectingMapSearchCoordinatesSettings"), '', '');
 
-print '<form method="post" action="'.$_SERVER["PHP_SELF"].'">';
-print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+print '<form method="post" action="' . $_SERVER["PHP_SELF"] . '">';
+print '<input type="hidden" name="token" value="' . $_SESSION['newtoken'] . '">';
 print '<input type="hidden" name="action" value="set_search_coordinates_settings">';
 
-$var=true;
-print '<table class="noborder" width="100%">';
+print '<table class="noborder centpercent">';
 print '<tr class="liste_titre">';
-print '<td>'.$langs->trans("Parameters").'</td>'."\n";
-print '<td width="30%">'.$langs->trans("Value").'</td>'."\n";
+print '<td>' . $langs->trans("Parameters") . '</td>' . "\n";
+print '<td width="30%">' . $langs->trans("Value") . '</td>' . "\n";
 print "</tr>\n";
 
 // PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS
-print '<tr class="oddeven">'."\n";
-print '<td>'.$langs->trans("ProspectingMapSearchCoordinatesUrlAddress").'</td>'."\n";
-print '<td class="nowrap">'."\n";
-print '<input type="text" name="PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS" size="50" value="'.dol_escape_htmltag($conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS).'" />'."\n";
-print '</td></tr>'."\n";
+print '<tr class="oddeven">' . "\n";
+print '<td>' . $langs->trans("ProspectingMapSearchCoordinatesUrlAddress") . '</td>' . "\n";
+print '<td class="nowrap">' . "\n";
+print '<input type="text" name="PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS" size="50" value="' . dol_escape_htmltag($conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS) . '" />' . "\n";
+print '</td></tr>' . "\n";
 
 // PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS
-print '<tr class="oddeven">'."\n";
-print '<td>'.$langs->trans("ProspectingMapSearchCoordinatesUrlParameters").'</td>'."\n";
-print '<td class="nowrap">'."\n";
-print '<input type="text" name="PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS" size="50" value="'.dol_escape_htmltag($conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS).'" />'."\n";
-print '</td></tr>'."\n";
+print '<tr class="oddeven">' . "\n";
+print '<td>' . $langs->trans("ProspectingMapSearchCoordinatesUrlParameters") . '</td>' . "\n";
+print '<td class="nowrap">' . "\n";
+print '<input type="text" name="PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS" size="50" value="' . dol_escape_htmltag($conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS) . '" />' . "\n";
+print '</td></tr>' . "\n";
 
 // PROSPECTINGMAP_SEARCH_COORDINATES_REQUEST_MODE
-print '<tr class="oddeven">'."\n";
-print '<td>'.$langs->trans("ProspectingMapSearchCoordinatesRequestMode").'</td>'."\n";
-print '<td class="nowrap">'."\n";
+print '<tr class="oddeven">' . "\n";
+print '<td>' . $langs->trans("ProspectingMapSearchCoordinatesRequestMode") . '</td>' . "\n";
+print '<td class="nowrap">' . "\n";
 print $form->selectarray("PROSPECTINGMAP_SEARCH_COORDINATES_REQUEST_MODE", array('GET' => 'GET', 'POST' => 'POST'), $conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_REQUEST_MODE);
-print '</td></tr>'."\n";
+print '</td></tr>' . "\n";
 
 // PROSPECTINGMAP_COORDINATES_METRICS
-print '<tr class="oddeven">'."\n";
-print '<td>'.$langs->trans("ProspectingMapCoordinatesMetrics").'</td>'."\n";
-print '<td class="nowrap">'."\n";
+print '<tr class="oddeven">' . "\n";
+print '<td>' . $langs->trans("ProspectingMapCoordinatesMetrics") . '</td>' . "\n";
+print '<td class="nowrap">' . "\n";
 print $form->selectarray("PROSPECTINGMAP_COORDINATES_METRICS", array('EPSG:3857' => 'Web Mercator (EPSG:3857)', 'EPSG:4326' => 'WGS 84 (EPSG:4326)'), $conf->global->PROSPECTINGMAP_COORDINATES_METRICS);
-print '</td></tr>'."\n";
+print '</td></tr>' . "\n";
 
 print '</table>';
 
 print '<br>';
-print '<div align="center">';
-print '<input type="submit" class="button" value="'.$langs->trans("Save").'">';
+print '<div class="center">';
+print '<input type="submit" class="button" value="' . $langs->trans("Save") . '">';
 print '</div>';
 
 print '</form>';
diff --git a/class/actions_prospectingmap.class.php b/class/actions_prospectingmap.class.php
index d7633c4c84f7e43a61c5733be097aba127e8104b..4fd18b32e0f5a0ef75e6bc7be24e99b07ffb465b 100644
--- a/class/actions_prospectingmap.class.php
+++ b/class/actions_prospectingmap.class.php
@@ -15,262 +15,272 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
+dol_include_once('/prospectingmap/class/prospectingmap.class.php');
+
 class ActionsProspectingMap
 {
-    /**
-     * @var DoliDB Database handler.
-     */
-    public $db;
-    /**
-     * @var string Error
-     */
-    public $error = '';
-    /**
-     * @var array Errors
-     */
-    public $errors = array();
-
-    /**
-     * @var array Hook results. Propagated to $hookmanager->resArray for later reuse
-     */
-    public $results = array();
-
-    /**
-     * @var string String displayed by executeHook() immediately after return
-     */
-    public $resprints;
-
-    /**
-     * Selected fields
-     *
-     * @var null
-     */
-    private $_selectedFieldList = null;
-
-    /**
-     * At least one coordinate field selected
-     *
-     * @var null
-     */
-    private $_coordinateFieldSelected = null;
-
-    /**
-     * Coordinate field list
-     *
-     * @var string[]
-     */
-    private static $_coordinateFieldList = array(
-        'pm_coordinate.longitude',
-        'pm_coordinate.latitude'
-    );
-
-
-    /**
-     * Get selected field list
-     *
-     * @return array|false|string[]|null
-     */
-    private function _getSelectedFieldList() {
-        global $contextpage, $user;
-
-        if ($this->_selectedFieldList === null) {
-            $this->_selectedFieldList = array();
-
-            $tmpVar = 'MAIN_SELECTEDFIELDS_' . (empty($contextpage) ? $_SERVER['PHP_SELF'] : $contextpage);
-            if (!empty($user->conf->$tmpVar)) {
-                $this->_selectedFieldList = explode(',', $user->conf->$tmpVar);
-            }
-        }
-
-        return $this->_selectedFieldList;
-    }
-
-    /**
-     * Check if at least on coordinate field is selected
-     *
-     * @return bool|null
-     */
-    private function _coordinateFieldListInSelectedFieldList() {
-        global $sortfield;
-
-        if ($this->_coordinateFieldSelected === null) {
-            $this->_coordinateFieldSelected = false;
-            $coordinateFieldList = self::$_coordinateFieldList;
-
-            if (in_array(trim($sortfield), $coordinateFieldList)) {
-                $this->_coordinateFieldSelected = true;
-            } else {
-                $selectedFieldList = $this->_getSelectedFieldList();
-                foreach ($coordinateFieldList as $coordinateField) {
-                    if (in_array($coordinateField, $selectedFieldList)) {
-                        $this->_coordinateFieldSelected = true;
-                        break;
-                    }
-                }
-            }
-        }
-
-        return $this->_coordinateFieldSelected;
-    }
-
-    /**
-     * Enable coordinate field
-     *
-     * @return bool
-     */
-    private function _coordinateFieldEnable() {
-        global $conf;
-
-        $enable = false;
-
-        // /!\ Need "printFieldListFrom" hook in company list (PR 15564)
-        if (!empty($conf->prospectingmap->enabled)) {
-            if (!empty($conf->global->PROSPECTINGMAP_FORCE_COORDINATES_IN_COMPANY_LIST)) {
-                $enable = true;
-            }
-            elseif (!empty($conf->global->EASYA_VERSION) && version_compare($conf->global->EASYA_VERSION, '2020.9', '>=')) {
-                $enable = true;
-            }
+	/**
+	 * @var DoliDB Database handler.
+	 */
+	public $db;
+	/**
+	 * @var string Error
+	 */
+	public $error = '';
+	/**
+	 * @var array Errors
+	 */
+	public $errors = array();
+
+	/**
+	 * @var array Hook results. Propagated to $hookmanager->resArray for later reuse
+	 */
+	public $results = array();
+
+	/**
+	 * @var string String displayed by executeHook() immediately after return
+	 */
+	public $resprints;
+
+	/**
+	 * Selected fields
+	 *
+	 * @var null
+	 */
+	private $_selectedFieldList = null;
+
+	/**
+	 * At least one coordinate field selected
+	 *
+	 * @var null
+	 */
+	private $_coordinateFieldSelected = null;
+
+	/**
+	 * Coordinate field list
+	 *
+	 * @var string[]
+	 */
+	private static $_coordinateFieldList = array(
+		'pm_coordinate.longitude',
+		'pm_coordinate.latitude'
+	);
+
+
+	/**
+	 * Get selected field list
+	 *
+	 * @return array|false|string[]|null
+	 */
+	private function _getSelectedFieldList()
+	{
+		global $contextpage, $user;
+
+		if ($this->_selectedFieldList === null) {
+			$this->_selectedFieldList = array();
+
+			$tmpVar = 'MAIN_SELECTEDFIELDS_' . (empty($contextpage) ? $_SERVER['PHP_SELF'] : $contextpage);
+			if (!empty($user->conf->$tmpVar)) {
+				$this->_selectedFieldList = explode(',', $user->conf->$tmpVar);
+			}
+		}
+
+		return $this->_selectedFieldList;
+	}
+
+	/**
+	 * Check if at least on coordinate field is selected
+	 *
+	 * @return bool|null
+	 */
+	private function _coordinateFieldListInSelectedFieldList()
+	{
+		global $sortfield;
+
+		if ($this->_coordinateFieldSelected === null) {
+			$this->_coordinateFieldSelected = false;
+			$coordinateFieldList = self::$_coordinateFieldList;
+
+			if (in_array(trim($sortfield), $coordinateFieldList)) {
+				$this->_coordinateFieldSelected = true;
+			} else {
+				$selectedFieldList = $this->_getSelectedFieldList();
+				foreach ($coordinateFieldList as $coordinateField) {
+					if (in_array($coordinateField, $selectedFieldList)) {
+						$this->_coordinateFieldSelected = true;
+						break;
+					}
+				}
+			}
+		}
+
+		return $this->_coordinateFieldSelected;
+	}
+
+	/**
+	 * Enable coordinate field
+	 *
+	 * @return bool
+	 */
+	private function _coordinateFieldEnable()
+	{
+		global $conf;
+
+		$enable = false;
+
+		// /!\ Need "printFieldListFrom" hook in company list (PR 15564)
+		if (!empty($conf->prospectingmap->enabled)) {
+			if (!empty($conf->global->PROSPECTINGMAP_FORCE_COORDINATES_IN_COMPANY_LIST)) {
+				$enable = true;
+			} elseif (!empty($conf->global->EASYA_VERSION) && version_compare($conf->global->EASYA_VERSION, '2020.9', '>=')) {
+				$enable = true;
+			}
 //            elseif (version_compare(DOL_VERSION, '14.0.0', '>=')) {
 //                $enable = true;
 //            }
-        }
-
-        return $enable;
-    }
-
-    /**
-     * Constructor
-     *
-     * @param        DoliDB $db Database handler
-     */
-    public function __construct($db)
-    {
-        $this->db = $db;
-    }
-
-    /**
-     * Overloading the addMoreMassActions function : replacing the parent's function with the one below
-     *
-     * @param   array           $parameters     Hook metadatas (context, etc...)
-     * @param   CommonObject    &$object        The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
-     * @param   string          &$action        Current action (if set). Generally create or edit or null
-     * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook
-     * @return  int             < 0 on error, 0 on success, 1 to replace standard code
-     */
-    function addMoreMassActions($parameters, &$object, &$action, $hookmanager)
-    {
-        $context = explode(':', $parameters['context']);
-
-        if (in_array('thirdpartylist', $context)) {
-            global $param;
-
-            // Longitude
-            $searchPMCoordinateLongitude = GETPOST('search_pm_coordinate_longitude', 'alpha');
-            if ($searchPMCoordinateLongitude != '') $param .= '&search_pm_coordinate_longitude=' . urlencode($searchPMCoordinateLongitude);
-            // Latitude
-            $searchPMCoordinateLatitude = GETPOST('search_pm_coordinate_latitude', 'alpha');
-            if ($searchPMCoordinateLatitude != '') $param .= '&search_pm_coordinate_latitude=' . urlencode($searchPMCoordinateLatitude);
-        }
-
-        return 0;
-    }
-
-    /**
-     * Overloading the doActions function : replacing the parent's function with the one below
-     *
-     * @param   array           $parameters     Hook metadatas (context, etc...)
-     * @param   CommonObject    &$object        The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
-     * @param   string          &$action        Current action (if set). Generally create or edit or null
-     * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook
-     * @return  int             < 0 on error, 0 on success, 1 to replace standard code
-     */
-    function doActions($parameters, &$object, &$action, $hookmanager)
-    {
-        global $conf, $langs;
-
-        $context = explode(':', $parameters['context']);
-
-        if (in_array('thirdpartylist', $context)) {
-            global $arrayfields;
-
-            // Longitude
-            $arrayfields['pm_coordinate.longitude'] = array('label' => $langs->trans('ProspectingMapLongitude'), 'checked' => 0, 'position' => 600, 'enabled' => $this->_coordinateFieldEnable());
-            // Latitude
-            $arrayfields['pm_coordinate.latitude']  = array('label' => $langs->trans('ProspectingMapLatitude'),  'checked' => 0, 'position' => 601, 'enabled' => $this->_coordinateFieldEnable());
-
-            // remove filters
-            if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) {
-                $_GET['search_pm_coordinate_longitude'] = '';
-                $_GET['search_pm_coordinate_latitude']  = '';
-            }
-        }
-
-        return 0;
-    }
-
-    /**
-     * Overloading the formObjectOptions function : replacing the parent's function with the one below
-     *
-     * @param   array() $parameters Hook metadatas (context, etc...)
-     * @param   CommonObject &$object The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
-     * @param   string &$action Current action (if set). Generally create or edit or null
-     * @param   HookManager $hookmanager Hook manager propagated to allow calling another hook
-     * @return  int                             < 0 on error, 0 on success, 1 to replace standard code
-     */
-    function formObjectOptions($parameters, &$object, &$action, $hookmanager)
-    {
-        global $conf, $langs;
-
-        if (in_array('thirdpartycard', explode(':', $parameters['context']))) {
-            $langs->load('prospectingmap@prospectingmap');
-
-            $coordinates_metrics = empty($conf->global->PROSPECTINGMAP_COORDINATES_METRICS) ? 'EPSG:3857' : $conf->global->PROSPECTINGMAP_COORDINATES_METRICS;
-            $icon = dol_buildpath('/prospectingmap/img/dot.png', 1);
-            if (($action == 'create' || $action == 'edit') &&
-                ((isset($_GET['map_longitude']) || isset($_POST['map_longitude'])) ||
-                    (isset($_GET['map_latitude']) || isset($_POST['map_latitude'])))
-            ) {
-                $longitude = GETPOST('map_longitude', 'int');
-                $latitude = GETPOST('map_latitude', 'int');
-            } else {
-                dol_include_once('/prospectingmap/lib/prospectingmap.lib.php');
-                $coordonate = prospectingmap_get_map_location($this->db, $object->id);
-                $longitude = $coordonate['lon'];
-                $latitude = $coordonate['lat'];
-            }
-
-            if ($action != 'create' && $action != 'edit' &&
-                (!isset($longitude) || !isset($longitude) || empty($conf->global->PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD))
-            )
-                return 0;
-
-            if ($action == 'create' || $action == 'edit') {
-                $longitude = isset($longitude) && $longitude !== '' ? $longitude : 0;
-                $latitude = isset($latitude) && $latitude !== '' ? $latitude : 0;
-
-                $map_html = str_replace("'", "\\'", '<tr><td>' . $langs->trans('ProspectingMapLocation') . '</td>' .
-                    '<td colspan="3">' .
-                    '<div id="map" class="map" style="width:80%; height: 400px;"></div>' .
-                    '<div id="map_button" style="margin-top: 10px;">' .
-                    '<input type="hidden" name="map_longitude" id="map_longitude" value="' . $longitude . '">' .
-                    '<input type="hidden" name="map_latitude" id="map_latitude" value="' . $latitude . '">' .
-                    '<input type="button" id="edit_map_location" class="button" value="' . $langs->transnoentitiesnoconv('ProspectingMapEditLocation') . '">' .
-                    ' &nbsp; &nbsp; ' .
-                    '<input type="button" id="get_map_location" class="button" value="' . $langs->transnoentitiesnoconv('ProspectingMapGetLocation') . '">' .
-                    '</div>' .
-                    '</td></tr>'
-                );
-
-                $editLocationLabel = str_replace("'", "\\'", $langs->transnoentitiesnoconv('ProspectingMapEditLocation'));
-                $endEditLocationLabel = str_replace("'", "\\'", $langs->transnoentitiesnoconv('ProspectingMapEndEditLocation'));
-            } else {
-                $map_html = '<tr><td colspan="2"><div id="map" class="map" style="width:100%; height: 300px;"></div></td></tr>';
-            }
-
-            $openlayer_path = dol_escape_js(dol_buildpath('/prospectingmap/includes/openlayers.v6.15.1', 1), 2);
-            $openlayerplugin = dol_escape_js(dol_buildpath('/prospectingmap/includes/GpPluginOpenLayers-3.4.4', 1), 2);
-            print <<<HTML
+		}
+
+		return $enable;
+	}
+
+	/**
+	 * Constructor
+	 *
+	 * @param        DoliDB $db Database handler
+	 */
+	public function __construct($db)
+	{
+		$this->db = $db;
+	}
+
+	/**
+	 * Overloading the printFieldListValue function : replacing the parent's function with the one below
+	 *
+	 * @param	array			$parameters		Hook metadatas (context, etc...)
+	 * @param	CommonObject	$object			The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
+	 * @param	string			$action			Current action (if set). Generally create or edit or null
+	 * @param	HookManager		$hookmanager	Hook manager propagated to allow calling another hook
+	 * @return	int								Result < 0 on error, 0 on success, 1 to replace standard code
+	 */
+	public function addMoreMassActions($parameters, &$object, &$action, $hookmanager)
+	{
+		$context = explode(':', $parameters['context']);
+
+		if (in_array('thirdpartylist', $context)) {
+			global $param;
+
+			// Longitude
+			$searchPMCoordinateLongitude = GETPOST('search_pm_coordinate_longitude', 'alpha');
+			if ($searchPMCoordinateLongitude != '') $param .= '&search_pm_coordinate_longitude=' . urlencode($searchPMCoordinateLongitude);
+			// Latitude
+			$searchPMCoordinateLatitude = GETPOST('search_pm_coordinate_latitude', 'alpha');
+			if ($searchPMCoordinateLatitude != '') $param .= '&search_pm_coordinate_latitude=' . urlencode($searchPMCoordinateLatitude);
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Overloading the doActions function : replacing the parent's function with the one below
+	 *
+	 * @param	array			$parameters		Hook metadatas (context, etc...)
+	 * @param	CommonObject	$object			The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
+	 * @param	string			$action			Current action (if set). Generally create or edit or null
+	 * @param	HookManager		$hookmanager	Hook manager propagated to allow calling another hook
+	 * @return	int								Result < 0 on error, 0 on success, 1 to replace standard code
+	 */
+	public function doActions($parameters, &$object, &$action, $hookmanager)
+	{
+		global $conf, $langs;
+
+		$context = explode(':', $parameters['context']);
+
+		if (in_array('thirdpartylist', $context)) {
+			global $arrayfields;
+
+			// Longitude
+			$arrayfields['pm_coordinate.longitude'] = array('label' => $langs->trans('ProspectingMapLongitude'), 'checked' => 0, 'position' => 600, 'enabled' => $this->_coordinateFieldEnable());
+			// Latitude
+			$arrayfields['pm_coordinate.latitude'] = array('label' => $langs->trans('ProspectingMapLatitude'), 'checked' => 0, 'position' => 601, 'enabled' => $this->_coordinateFieldEnable());
+
+			// remove filters
+			if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) {
+				$_GET['search_pm_coordinate_longitude'] = '';
+				$_GET['search_pm_coordinate_latitude'] = '';
+			}
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Overloading the formObjectOptions function : replacing the parent's function with the one below
+	 *
+	 * @param	array			$parameters		Hook metadatas (context, etc...)
+	 * @param	CommonObject	$object			The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
+	 * @param	string			$action			Current action (if set). Generally create or edit or null
+	 * @param	HookManager		$hookmanager	Hook manager propagated to allow calling another hook
+	 * @return	int								Result < 0 on error, 0 on success, 1 to replace standard code
+	 */
+	public function formObjectOptions($parameters, &$object, &$action, $hookmanager)
+	{
+		global $conf, $langs;
+
+		if (in_array('thirdpartycard', explode(':', $parameters['context']))) {
+			$langs->load('prospectingmap@prospectingmap');
+
+			$longitude = 0;
+			$latitude = 0;
+			$coordinates_metrics = empty($conf->global->PROSPECTINGMAP_COORDINATES_METRICS) ? 'EPSG:3857' : $conf->global->PROSPECTINGMAP_COORDINATES_METRICS;
+			$icon = dol_buildpath('/prospectingmap/img/dot.png', 1);
+			if (($action == 'create' || $action == 'edit') &&
+				((isset($_GET['map_longitude']) || isset($_POST['map_longitude'])) ||
+					(isset($_GET['map_latitude']) || isset($_POST['map_latitude'])))
+			) {
+				$longitude = GETPOST('map_longitude', 'int');
+				$latitude = GETPOST('map_latitude', 'int');
+			} else {
+				$prospectingmap = new ProspectingMap($this->db);
+				$coordinates = $prospectingmap->getThirdPartyCoordinates($object->id);
+				if (!isset($coordinates)) {
+					setEventMessages($prospectingmap->error, $prospectingmap->errors, 'errors');
+				} else {
+					$longitude = $coordinates['lon'];
+					$latitude = $coordinates['lat'];
+				}
+			}
+
+			if ($action != 'create' && $action != 'edit' &&
+				(!isset($longitude) || !isset($longitude) || empty($conf->global->PROSPECTINGMAP_SHOW_MAP_INTO_COMPANY_CARD))
+			)
+				return 0;
+
+			if ($action == 'create' || $action == 'edit') {
+				$longitude = isset($longitude) && $longitude !== '' ? $longitude : 0;
+				$latitude = isset($latitude) && $latitude !== '' ? $latitude : 0;
+
+				$map_html = str_replace("'", "\\'", '<tr><td>' . $langs->trans('ProspectingMapLocation') . '</td>' .
+					'<td colspan="3">' .
+					'<div id="map" class="map" style="width:80%; height: 400px;"></div>' .
+					'<div id="map_button" style="margin-top: 10px;">' .
+					'<input type="hidden" name="map_longitude" id="map_longitude" value="' . $longitude . '">' .
+					'<input type="hidden" name="map_latitude" id="map_latitude" value="' . $latitude . '">' .
+					'<input type="button" id="edit_map_location" class="button" value="' . $langs->transnoentitiesnoconv('ProspectingMapEditLocation') . '">' .
+					' &nbsp; &nbsp; ' .
+					'<input type="button" id="get_map_location" class="button" value="' . $langs->transnoentitiesnoconv('ProspectingMapGetLocation') . '">' .
+					'</div>' .
+					'</td></tr>'
+				);
+
+				$editLocationLabel = str_replace("'", "\\'", $langs->transnoentitiesnoconv('ProspectingMapEditLocation'));
+				$endEditLocationLabel = str_replace("'", "\\'", $langs->transnoentitiesnoconv('ProspectingMapEndEditLocation'));
+			} else {
+				$map_html = '<tr><td colspan="2"><div id="map" class="map" style="width:100%; height: 300px;"></div></td></tr>';
+			}
+
+			$openlayer_path = dol_escape_js(dol_buildpath('/prospectingmap/includes/openlayers.v6.15.1', 1), 2);
+			$openlayerplugin = dol_escape_js(dol_buildpath('/prospectingmap/includes/GpPluginOpenLayers-3.4.4', 1), 2);
+			print <<<HTML
                 <link rel="stylesheet" href="{$openlayer_path}/ol.css" type="text/css">
                 <link rel="stylesheet" href="{$openlayerplugin}/GpPluginOpenLayers.css" type="text/css">
                 <script src="{$openlayer_path}/ol.js"></script>
@@ -279,16 +289,16 @@ class ActionsProspectingMap
                 <script type="text/javascript">
                   $(document).ready(function() {
 HTML;
-            if ($action == 'create' || $action == 'edit') {
-                $url = $conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS;
-                parse_str($conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS, $parameters);
-                $datas = json_encode($parameters);
-                $requestMode = $conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_REQUEST_MODE;
-                $useJqueryBlockUi = !empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false";
-                $errorLabelGetPosition = dol_escape_js($langs->trans('ProspectingMapErrorGetPosition'));
-                $errorLabelGetPositionNotFound = dol_escape_js($langs->trans('ProspectingMapErrorGetPositionNotFound'));
+			if ($action == 'create' || $action == 'edit') {
+				$url = $conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_URL_ADDRESS;
+				parse_str($conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_URL_PARAMETERS, $parameters);
+				$datas = json_encode($parameters);
+				$requestMode = $conf->global->PROSPECTINGMAP_SEARCH_COORDINATES_REQUEST_MODE;
+				$useJqueryBlockUi = !empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false";
+				$errorLabelGetPosition = dol_escape_js($langs->trans('ProspectingMapErrorGetPosition'));
+				$errorLabelGetPositionNotFound = dol_escape_js($langs->trans('ProspectingMapErrorGetPositionNotFound'));
 
-                print <<<HTML
+				print <<<HTML
 
                 function search_address(address, full_address) {
                     const correspondance_addresse = [
@@ -473,14 +483,14 @@ HTML;
                     });
                 });
 HTML;
-            } else {
-                $capital_label = str_replace("'", "\\'", $langs->transnoentitiesnoconv('Capital'));
-                print <<<HTML
+			} else {
+				$capital_label = str_replace("'", "\\'", $langs->transnoentitiesnoconv('Capital'));
+				print <<<HTML
                     $("td:contains('$capital_label')").closest('table').append('$map_html');
 HTML;
-            }
+			}
 
-            print <<<HTML
+			print <<<HTML
                     var coordinate_point = [$longitude, $latitude];
                     console.log('coordinate metrics: ', '$coordinates_metrics');
                     console.log('coordinate point: ', coordinate_point);
@@ -585,8 +595,8 @@ HTML;
                     }
 HTML;
 
-            if ($action == 'create' || $action == 'edit') {
-                print <<<HTML
+			if ($action == 'create' || $action == 'edit') {
+				print <<<HTML
                     /**
                      * Add a click handler to the map to render the popup.
                      */
@@ -616,182 +626,182 @@ HTML;
                       point.setGeometry(new ol.geom.Point(coordinate));
                     }
 HTML;
-            }
-            print <<<HTML
+			}
+			print <<<HTML
                   });
                 </script>
 HTML;
-        }
-
-        return 0;
-    }
-
-    /**
-     * Overloading the printFieldListSelect function : replacing the parent's function with the one below
-     *
-     * @param   array           $parameters     Hook metadatas (context, etc...)
-     * @param   CommonObject    &$object        The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
-     * @param   string          &$action        Current action (if set). Generally create or edit or null
-     * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook
-     * @return  int             < 0 on error, 0 on success, 1 to replace standard code
-     */
-    function printFieldListSelect($parameters, &$object, &$action, $hookmanager)
-    {
-        $context = explode(':', $parameters['context']);
-
-        if (in_array('thirdpartylist', $context)) {
-            if ($this->_coordinateFieldListInSelectedFieldList() === true) {
-                $resPrints = ', pm_coordinate.longitude, pm_coordinate.latitude';
-                $this->resprints = $resPrints;
-            }
-        }
-
-        return 0;
-    }
-
-    /**
-     * Overloading the printFieldListFrom function : replacing the parent's function with the one below
-     *
-     * @param   array           $parameters     Hook metadatas (context, etc...)
-     * @param   CommonObject    &$object        The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
-     * @param   string          &$action        Current action (if set). Generally create or edit or null
-     * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook
-     * @return  int             < 0 on error, 0 on success, 1 to replace standard code
-     */
-    function printFieldListFrom($parameters, &$object, &$action, $hookmanager)
-    {
-        $context = explode(':', $parameters['context']);
-
-        if (in_array('thirdpartylist', $context)) {
-            if ($this->_coordinateFieldListInSelectedFieldList() === true) {
-                $resPrints = " LEFT JOIN " . MAIN_DB_PREFIX . "prospectingmap_coordinate as pm_coordinate ON pm_coordinate.fk_soc = s.rowid";
-                $this->resprints = $resPrints;
-            }
-        }
-
-        return 0;
-    }
-
-    /**
-     * Overloading the printFieldListWhere function : replacing the parent's function with the one below
-     *
-     * @param   array           $parameters     Hook metadatas (context, etc...)
-     * @param   CommonObject    &$object        The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
-     * @param   string          &$action        Current action (if set). Generally create or edit or null
-     * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook
-     * @return  int             < 0 on error, 0 on success, 1 to replace standard code
-     */
-    function printFieldListWhere($parameters, &$object, &$action, $hookmanager)
-    {
-        $context = explode(':', $parameters['context']);
-
-        if (in_array('thirdpartylist', $context)) {
-            if ($this->_coordinateFieldListInSelectedFieldList() === true) {
-                $searchPMCoordinateLongitude = GETPOST('search_pm_coordinate_longitude', 'alpha');
-                $searchPMCoordinateLatitude = GETPOST('search_pm_coordinate_latitude', 'alpha');
-
-                $out = '';
-                // Longitude
-                if ($searchPMCoordinateLongitude) $out .= natural_search('pm_coordinate.longitude', $searchPMCoordinateLongitude);
-                // Longitude
-                if ($searchPMCoordinateLatitude) $out .= natural_search('pm_coordinate.longitude', $searchPMCoordinateLatitude);
-
-                $this->resprints = $out;
-            }
-        }
-
-        return 0;
-    }
-
-    /**
-     * Overloading the printFieldListOption function : replacing the parent's function with the one below
-     *
-     * @param   array           $parameters     Hook metadatas (context, etc...)
-     * @param   CommonObject    &$object        The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
-     * @param   string          &$action        Current action (if set). Generally create or edit or null
-     * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook
-     * @return  int             < 0 on error, 0 on success, 1 to replace standard code
-     */
-    function printFieldListOption($parameters, &$object, &$action, $hookmanager)
-    {
-        $context = explode(':', $parameters['context']);
-
-        if (in_array('thirdpartylist', $context)) {
-            global $arrayfields;
-
-            // Longitude
-            if (!empty($arrayfields['pm_coordinate.longitude']['checked'])) {
-                $searchPMCoordinateLongitude = GETPOST('search_pm_coordinate_longitude', 'alpha');
-                print '<td class="liste_titre">';
-                print '<input class="flat" type="text" name="search_pm_coordinate_longitude" size="8" value="' . dol_escape_htmltag($searchPMCoordinateLongitude) . '">';
-                print '</td>';
-            }
-            // Latitude
-            if (!empty($arrayfields['pm_coordinate.latitude']['checked'])) {
-                $searchPMCoordinateLatitude  = GETPOST('search_pm_coordinate_latitude', 'alpha');
-                print '<td class="liste_titre">';
-                print '<input class="flat" type="text" name="search_pm_coordinate_latitude" size="8" value="' . dol_escape_htmltag($searchPMCoordinateLatitude) . '">';
-                print '</td>';
-            }
-        }
-
-        return 0;
-    }
-
-    /**
-     * Overloading the printFieldListTitle function : replacing the parent's function with the one below
-     *
-     * @param   array           $parameters     Hook metadatas (context, etc...)
-     * @param   CommonObject    &$object        The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
-     * @param   string          &$action        Current action (if set). Generally create or edit or null
-     * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook
-     * @return  int             < 0 on error, 0 on success, 1 to replace standard code
-     */
-    function printFieldListTitle($parameters, &$object, &$action, $hookmanager)
-    {
-        $context = explode(':', $parameters['context']);
-        if (in_array('thirdpartylist', $context)) {
-            global $arrayfields, $begin, $param, $sortfield, $sortorder;
-
-            // Longitude
-            if (!empty($arrayfields['pm_coordinate.longitude']['checked'])) print_liste_field_titre($arrayfields['pm_coordinate.longitude']['label'], $_SERVER['PHP_SELF'], 'pm_coordinate.longitude', $begin, $param, 'align="center"', $sortfield, $sortorder);
-            // Latitude
-            if (!empty($arrayfields['pm_coordinate.latitude']['checked']))  print_liste_field_titre($arrayfields['pm_coordinate.latitude']['label'], $_SERVER['PHP_SELF'], 'pm_coordinate.latitude', $begin, $param, 'align="center"', $sortfield, $sortorder);
-        }
-
-        return 0;
-    }
-
-    /**
-     * Overloading the printFieldListValue function : replacing the parent's function with the one below
-     *
-     * @param   array           $parameters     Hook metadatas (context, etc...)
-     * @param   CommonObject    &$object        The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
-     * @param   string          &$action        Current action (if set). Generally create or edit or null
-     * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook
-     * @return  int             < 0 on error, 0 on success, 1 to replace standard code
-     */
-    function printFieldListValue($parameters, &$object, &$action, $hookmanager)
-    {
-        $context = explode(':', $parameters['context']);
-
-        if (in_array('thirdpartylist', $context)) {
-            global $arrayfields, $i, $totalarray, $obj;
-
-            // Longitude
-            if (!empty($arrayfields['pm_coordinate.longitude']['checked'])) {
-                print '<td align="center">' . $obj->longitude . '</td>';
-                if (!$i) $totalarray['nbfield']++;
-            }
-
-            // Latitude
-            if (!empty($arrayfields['pm_coordinate.latitude']['checked'])) {
-                // Cache categories
-                print '<td align="center">' . $obj->latitude . '</td>';
-                if (!$i) $totalarray['nbfield']++;
-            }
-        }
-
-        return 0;
-    }
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Overloading the printFieldListSelect function : replacing the parent's function with the one below
+	 *
+	 * @param	array			$parameters		Hook metadatas (context, etc...)
+	 * @param	CommonObject	$object			The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
+	 * @param	string			$action			Current action (if set). Generally create or edit or null
+	 * @param	HookManager		$hookmanager	Hook manager propagated to allow calling another hook
+	 * @return	int								Result < 0 on error, 0 on success, 1 to replace standard code
+	 */
+	public function printFieldListSelect($parameters, &$object, &$action, $hookmanager)
+	{
+		$context = explode(':', $parameters['context']);
+
+		if (in_array('thirdpartylist', $context)) {
+			if ($this->_coordinateFieldListInSelectedFieldList() === true) {
+				$resPrints = ', pm_coordinate.longitude, pm_coordinate.latitude';
+				$this->resprints = $resPrints;
+			}
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Overloading the printFieldListFrom function : replacing the parent's function with the one below
+	 *
+	 * @param	array			$parameters		Hook metadatas (context, etc...)
+	 * @param	CommonObject	$object			The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
+	 * @param	string			$action			Current action (if set). Generally create or edit or null
+	 * @param	HookManager		$hookmanager	Hook manager propagated to allow calling another hook
+	 * @return	int								Result < 0 on error, 0 on success, 1 to replace standard code
+	 */
+	public function printFieldListFrom($parameters, &$object, &$action, $hookmanager)
+	{
+		$context = explode(':', $parameters['context']);
+
+		if (in_array('thirdpartylist', $context)) {
+			if ($this->_coordinateFieldListInSelectedFieldList() === true) {
+				$resPrints = " LEFT JOIN " . MAIN_DB_PREFIX . "prospectingmap_coordinate as pm_coordinate ON pm_coordinate.fk_soc = s.rowid";
+				$this->resprints = $resPrints;
+			}
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Overloading the printFieldListWhere function : replacing the parent's function with the one below
+	 *
+	 * @param	array			$parameters		Hook metadatas (context, etc...)
+	 * @param	CommonObject	$object			The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
+	 * @param	string			$action			Current action (if set). Generally create or edit or null
+	 * @param	HookManager		$hookmanager	Hook manager propagated to allow calling another hook
+	 * @return	int								Result < 0 on error, 0 on success, 1 to replace standard code
+	 */
+	public function printFieldListWhere($parameters, &$object, &$action, $hookmanager)
+	{
+		$context = explode(':', $parameters['context']);
+
+		if (in_array('thirdpartylist', $context)) {
+			if ($this->_coordinateFieldListInSelectedFieldList() === true) {
+				$searchPMCoordinateLongitude = GETPOST('search_pm_coordinate_longitude', 'alpha');
+				$searchPMCoordinateLatitude = GETPOST('search_pm_coordinate_latitude', 'alpha');
+
+				$out = '';
+				// Longitude
+				if ($searchPMCoordinateLongitude) $out .= natural_search('pm_coordinate.longitude', $searchPMCoordinateLongitude);
+				// Longitude
+				if ($searchPMCoordinateLatitude) $out .= natural_search('pm_coordinate.longitude', $searchPMCoordinateLatitude);
+
+				$this->resprints = $out;
+			}
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Overloading the printFieldListOption function : replacing the parent's function with the one below
+	 *
+	 * @param	array			$parameters		Hook metadatas (context, etc...)
+	 * @param	CommonObject	$object			The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
+	 * @param	string			$action			Current action (if set). Generally create or edit or null
+	 * @param	HookManager		$hookmanager	Hook manager propagated to allow calling another hook
+	 * @return	int								Result < 0 on error, 0 on success, 1 to replace standard code
+	 */
+	public function printFieldListOption($parameters, &$object, &$action, $hookmanager)
+	{
+		$context = explode(':', $parameters['context']);
+
+		if (in_array('thirdpartylist', $context)) {
+			global $arrayfields;
+
+			// Longitude
+			if (!empty($arrayfields['pm_coordinate.longitude']['checked'])) {
+				$searchPMCoordinateLongitude = GETPOST('search_pm_coordinate_longitude', 'alpha');
+				print '<td class="liste_titre">';
+				print '<input class="flat" type="text" name="search_pm_coordinate_longitude" size="8" value="' . dol_escape_htmltag($searchPMCoordinateLongitude) . '">';
+				print '</td>';
+			}
+			// Latitude
+			if (!empty($arrayfields['pm_coordinate.latitude']['checked'])) {
+				$searchPMCoordinateLatitude = GETPOST('search_pm_coordinate_latitude', 'alpha');
+				print '<td class="liste_titre">';
+				print '<input class="flat" type="text" name="search_pm_coordinate_latitude" size="8" value="' . dol_escape_htmltag($searchPMCoordinateLatitude) . '">';
+				print '</td>';
+			}
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Overloading the printFieldListTitle function : replacing the parent's function with the one below
+	 *
+	 * @param	array			$parameters		Hook metadatas (context, etc...)
+	 * @param	CommonObject	$object			The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
+	 * @param	string			$action			Current action (if set). Generally create or edit or null
+	 * @param	HookManager		$hookmanager	Hook manager propagated to allow calling another hook
+	 * @return	int								Result < 0 on error, 0 on success, 1 to replace standard code
+	 */
+	public function printFieldListTitle($parameters, &$object, &$action, $hookmanager)
+	{
+		$context = explode(':', $parameters['context']);
+		if (in_array('thirdpartylist', $context)) {
+			global $arrayfields, $begin, $param, $sortfield, $sortorder;
+
+			// Longitude
+			if (!empty($arrayfields['pm_coordinate.longitude']['checked'])) print_liste_field_titre($arrayfields['pm_coordinate.longitude']['label'], $_SERVER['PHP_SELF'], 'pm_coordinate.longitude', $begin, $param, 'align="center"', $sortfield, $sortorder);
+			// Latitude
+			if (!empty($arrayfields['pm_coordinate.latitude']['checked'])) print_liste_field_titre($arrayfields['pm_coordinate.latitude']['label'], $_SERVER['PHP_SELF'], 'pm_coordinate.latitude', $begin, $param, 'align="center"', $sortfield, $sortorder);
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Overloading the printFieldListValue function : replacing the parent's function with the one below
+	 *
+	 * @param	array			$parameters		Hook metadatas (context, etc...)
+	 * @param	CommonObject	$object			The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
+	 * @param	string			$action			Current action (if set). Generally create or edit or null
+	 * @param	HookManager		$hookmanager	Hook manager propagated to allow calling another hook
+	 * @return	int								Result < 0 on error, 0 on success, 1 to replace standard code
+	 */
+	public function printFieldListValue($parameters, &$object, &$action, $hookmanager)
+	{
+		$context = explode(':', $parameters['context']);
+
+		if (in_array('thirdpartylist', $context)) {
+			global $arrayfields, $i, $totalarray, $obj;
+
+			// Longitude
+			if (!empty($arrayfields['pm_coordinate.longitude']['checked'])) {
+				print '<td class="center">' . $obj->longitude . '</td>';
+				if (!$i) $totalarray['nbfield']++;
+			}
+
+			// Latitude
+			if (!empty($arrayfields['pm_coordinate.latitude']['checked'])) {
+				// Cache categories
+				print '<td class="center">' . $obj->latitude . '</td>';
+				if (!$i) $totalarray['nbfield']++;
+			}
+		}
+
+		return 0;
+	}
 }
\ No newline at end of file
diff --git a/class/coordinateconverter.class.php b/class/coordinateconverter.class.php
deleted file mode 100644
index e8028a1ca8c123642a39195849fa24c9e0573a71..0000000000000000000000000000000000000000
--- a/class/coordinateconverter.class.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-/* Copyright (C) 2022      Open-DSI             <support@open-dsi.fr>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Adapted from code of Armand on https://gis.stackexchange.com/questions/120636/math-formula-for-transforming-from-epsg4326-to-epsg3857
- */
-
-class CoordinateConverter
-{
-	private static $a = 6378137.0;
-
-	/**
-	 * Convert Web Mercator (EPSG:3857) to WGS 84 (EPSG:4326)
-	 *
-	 * @param	array	$epsg3857Coordinate		Coordinate (X, Y)
-	 * @return	array							Converted coordinate (Longitude, Latitude)
-	 */
-    public static function convertEpsg3857To4326($epsg3857Coordinate)
-	{
-        // D = -N / a
-        // φ = π / 2 – 2 atan(e ^ D)
-        // λ = E / a
-
-        $d = -$epsg3857Coordinate[1] / self::$a;
-        $phi = pi() / 2 - 2 * atan(exp($d));
-        $lambda = $epsg3857Coordinate[0] / self::$a;
-        $lat = $phi / pi() * 180;
-        $lon = $lambda / pi() * 180;
-
-        return [$lon, $lat];
-    }
-
-	/**
-	 * Convert WGS 84 (EPSG:4326) to Web Mercator (EPSG:3857)
-	 *
-	 * @param	array	$epsg4326Coordinate		Coordinate (Longitude, Latitude)
-	 * @return	array							Converted coordinate (X, Y)
-	 */
-	public static function convertEpsg4326To3857($epsg4326Coordinate)
-	{
-		// E = x = a * λ
-		// N = y = a * ln[tan(π/4 + φ/2)]
-
-		$lambda = $epsg4326Coordinate[0] / 180 * pi();
-		$phi = $epsg4326Coordinate[1] / 180 * pi();
-		$x = self::$a * $lambda;
-		$y = self::$a * log(tan(pi() / 4 + $phi / 2));
-
-		return [$x, $y];
-	}
-}
diff --git a/class/prospectingmap.class.php b/class/prospectingmap.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..fcb0c8921a872670127b29bfa9fb48a4d91e33fc
--- /dev/null
+++ b/class/prospectingmap.class.php
@@ -0,0 +1,254 @@
+<?php
+/* Copyright (C) 2025      Easya Solutions             <support@easya.solutions>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file    htdocs/prospectingmap/class/prospectingmap.class.php
+ * \ingroup prospectingmap
+ * \brief
+ */
+
+require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
+// Use a PSR-4 autoloader for the `proj4php` root namespace.
+dol_include_once('/prospectingmap/vendor/autoload.php');
+
+use proj4php\Proj4php;
+use proj4php\Proj;
+use proj4php\Point;
+
+
+/**
+ * Class ProspectingMap
+ *
+ * Put here description of your class
+ */
+class ProspectingMap extends CommonObject
+{
+	/**
+	 * @var DoliDB Database handler.
+	 */
+	public $db;
+	/**
+	 * @var string Error
+	 */
+	public $error = '';
+	/**
+	 * @var array Errors
+	 */
+	public $errors = array();
+	/**
+	 * @var array Warnings
+	 */
+	public $warnings = array();
+
+	/**
+	 * @var string[] Supported coordinate format code (used in prospecting map, see '/prospectingmap/vendor/proj4php/proj4php/src/defs' for all supported format)
+	 */
+	public static $supportedCoordinateFormatCode = [ 'EPSG:4326', 'EPSG:3857', 'EPSG:2154' ];
+
+
+	/**
+	 * Constructor
+	 *
+	 * @param        DoliDB $db Database handler
+	 */
+	public function __construct($db)
+	{
+		$this->db = $db;
+	}
+
+	/**
+	 * Convert coordinates
+	 *
+	 * @param	string	$fromFormatCode		Format code of the coordinates provided
+	 * @param	string	$toFormatCode		Format code of the converted coordinates
+	 * @param	double	$longitude			Longitude (Coordinate X)
+	 * @param	double	$latitude			Latitude (Coordinate Y)
+	 * @param	double	$altitude			Altitude (Coordinate Z)
+	 * @return	array						Converted coordinate (Longitude, Latitude, Altitude)
+	 */
+	public function convertCoordinates($fromFormatCode, $toFormatCode, $longitude, $latitude, $altitude = 0)
+	{
+		global $langs;
+		$langs->load('prospectingmap@prospectingmap');
+
+		$this->errors = [];
+		$fromFormatCode = trim((string) $fromFormatCode);
+		$toFormatCode = trim((string) $toFormatCode);
+		$longitude = (double) price2num($longitude);
+		$latitude = (double) price2num($latitude);
+		$altitude = (double) price2num($altitude);
+
+		try {
+			// Initialise Proj4
+			$proj4 = new Proj4php();
+
+			// Create two different projections.
+			$projFrom = new Proj($fromFormatCode, $proj4);
+			$projTo = new Proj($toFormatCode, $proj4);
+
+			// Create a point.
+			$pointSrc = new Point($longitude, $latitude, $altitude, $projFrom);
+
+			// Transform the point between datums.
+			$pointDest = $proj4->transform($projTo, $pointSrc);
+		} catch (Exception $e) {
+			$this->errors[] = $e->getMessage();
+			return null;
+		}
+
+		return $pointDest->toArray();
+	}
+
+	/**
+	 *  Get coordinates for the specified company
+	 *
+	 * @param   int     $socid      Company ID
+	 * @return  array               null if KO otherwise the coordinates of the company: array('lon' => 0, 'lat' => 0)
+	 */
+	public function getThirdPartyCoordinates($socid)
+	{
+		$this->errors = [];
+		$socid = max(0, (int) $socid);
+		$longitude = null;
+		$latitude = null;
+
+		if ($socid > 0) {
+			$sql = "SELECT longitude, latitude FROM " . MAIN_DB_PREFIX . "prospectingmap_coordinate WHERE fk_soc = " . ((int) $socid);
+			$resql = $this->db->query($sql);
+			if (!$resql) {
+				$this->errors[] = $this->db->lasterror();
+				return null;
+			}
+
+			if ($obj = $this->db->fetch_object($resql)) {
+				$longitude = (double) $obj->longitude;
+				$latitude = (double) $obj->latitude;
+			}
+			$this->db->free($resql);
+		}
+
+		return array('lon' => $longitude, 'lat' => $latitude);
+	}
+
+	/**
+	 *  Set coordinates for the specified company
+	 *
+	 * @param   int     $socid          Company ID
+	 * @param   int     $longitude      Longitude of the map location for the company
+	 * @param   int     $latitude       Latitude of the map location for the company
+	 * @return  int                     >0 if ok, <0 if ko, 0 if none
+	 */
+	public function setThirdPartyCoordinates($socid, $longitude, $latitude)
+	{
+		$this->errors = [];
+		$socid = max(0, (int) $socid);
+
+		if ($socid > 0) {
+			$sql = "INSERT INTO " . MAIN_DB_PREFIX . "prospectingmap_coordinate(fk_soc, longitude, latitude)" .
+				" VALUES (" . ((int) $socid) . ", '" . $this->db->escape($longitude) . "', '" . $this->db->escape($latitude) . "')" .
+				" ON DUPLICATE KEY UPDATE longitude = '" . $this->db->escape($longitude) . "', latitude = '" . $this->db->escape($latitude) . "'";
+			$resql = $this->db->query($sql);
+			if (!$resql) {
+				$this->errors[] = $this->db->lasterror();
+				return -1;
+			}
+
+			return 1;
+		}
+
+		return 0;
+	}
+
+	/**
+	 *  Delete coordinates for the specified company
+	 *
+	 * @param   int     $socid          Company ID
+	 * @return  int                     >0 if ok, <0 if ko, 0 if none
+	 */
+	public function delThirdPartyCoordinates($socid)
+	{
+		$this->errors = [];
+		$socid = max(0, (int) $socid);
+
+		if ($socid > 0) {
+			$sql = "DELETE FROM " . MAIN_DB_PREFIX . "prospectingmap_coordinate WHERE fk_soc = " . ((int) $socid);
+			$resql = $this->db->query($sql);
+			if (!$resql) {
+				$this->errors[] = $this->db->lasterror();
+				return -1;
+			}
+
+			return 1;
+		}
+
+		return 0;
+	}
+
+	/**
+	 *  Convert all coordinates of all third parties from a format to another format
+	 *
+	 * @param	string	$fromFormatCode		Old format code of the coordinates
+	 * @param	string	$toFormatCode		New format code of the coordinates
+	 * @return	int							>0 if ok, <0 if ko, 0 if none
+	 */
+	public function convertAllThirdPartyCoordinates($fromFormatCode, $toFormatCode)
+	{
+		global $langs;
+		$langs->load('prospectingmap@prospectingmap');
+
+		$this->errors = [];
+		$fromFormatCode = trim((string) $fromFormatCode);
+		$toFormatCode = trim((string) $toFormatCode);
+
+		if ($fromFormatCode == $toFormatCode) {
+			return 0;
+		}
+
+		$sql = "SELECT fk_soc, longitude, latitude FROM " . MAIN_DB_PREFIX . "prospectingmap_coordinate";
+		$resql = $this->db->query($sql);
+		if (!$resql) {
+			$this->errors[] = $this->db->lasterror();
+			return -1;
+		}
+
+		$error = 0;
+		$this->db->begin();
+
+		while ($obj = $this->db->fetch_object($resql)) {
+			$converted_coordinate = $this->convertCoordinates($fromFormatCode, $toFormatCode, $obj->longitude, $obj->latitude);
+			if (!isset($converted_coordinate)) {
+				$error++;
+				break;
+			}
+
+			$result = $this->setThirdPartyCoordinates($obj->fk_soc, $converted_coordinate[0], $converted_coordinate[1]);
+			if ($result < 0) {
+				$error++;
+				break;
+			}
+		}
+		$this->db->free($resql);
+
+		if ($error) {
+			$this->db->rollback();
+			return -1;
+		} else {
+			$this->db->commit();
+			return 1;
+		}
+	}
+}
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..cb8a3e519e11b2bdd2915ec6d6ec975b8a2995b3
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,5 @@
+{
+    "require": {
+        "proj4php/proj4php": "^2.0"
+    }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000000000000000000000000000000000000..a3c74f5dd49a87c03a0c9be235110e34268f19a7
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,83 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "4e5a88d152387cb7e8fd5665178573a6",
+    "packages": [
+        {
+            "name": "proj4php/proj4php",
+            "version": "2.0.17",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/proj4php/proj4php.git",
+                "reference": "3558c265a86c3243547391c0944f8303dee493b3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/proj4php/proj4php/zipball/3558c265a86c3243547391c0944f8303dee493b3",
+                "reference": "3558c265a86c3243547391c0944f8303dee493b3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "9.*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "proj4php\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL-2.1"
+            ],
+            "authors": [
+                {
+                    "name": "julien2512",
+                    "email": "moquet.julien@gmail.com",
+                    "homepage": "https://github.com/julien2512",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Jason Judge",
+                    "email": "jason.judge@academe.co.uk",
+                    "homepage": "http://academe.co.uk",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Nick Blackwell",
+                    "email": "nickblackwell82@gmail.com",
+                    "homepage": "https://people.ok.ubc.ca/nblackwe",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A PHP-Class for geographic coordinates transformation using proj4 definitions, thanks to a translation from Proj4JS",
+            "homepage": "https://github.com/proj4php/proj4php",
+            "keywords": [
+                "coordinates",
+                "geographic",
+                "proj4",
+                "proj4js"
+            ],
+            "support": {
+                "issues": "https://github.com/proj4php/proj4php/issues",
+                "source": "https://github.com/proj4php/proj4php/tree/2.0.17"
+            },
+            "time": "2024-07-19T19:46:30+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": [],
+    "plugin-api-version": "2.6.0"
+}
diff --git a/core/triggers/interface_99_modProspectingMap_ProspectingMap.class.php b/core/triggers/interface_99_modProspectingMap_ProspectingMap.class.php
index 344d7ab0e8ec55ac4fe2ceb639da208df2dfd5aa..f641b031c87af517cfcf0a3b7772f439f896e112 100755
--- a/core/triggers/interface_99_modProspectingMap_ProspectingMap.class.php
+++ b/core/triggers/interface_99_modProspectingMap_ProspectingMap.class.php
@@ -21,8 +21,8 @@
  *  \brief      File of class of triggers for prospectingmap module
  */
 
-
 require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
+dol_include_once('/prospectingmap/class/prospectingmap.class.php');
 
 
 /**
@@ -47,38 +47,61 @@ class InterfaceProspectingMap extends DolibarrTriggers
 	 * @return int         				<0 if KO, 0 if no triggered ran, >0 if OK
 	 */
 	public function runTrigger($action, $object, User $user, Translate $langs, Conf $conf)
-    {
-        switch ($action) {
-            case 'COMPANY_CREATE':
-            case 'COMPANY_MODIFY':
-                if ((isset($_GET['map_longitude']) || isset($_POST['map_longitude'])) ||
-                    (isset($_GET['map_latitude']) || isset($_POST['map_latitude']))) {
-                    $longitude = GETPOST('map_longitude', 'int');
-                    $latitude = GETPOST('map_latitude', 'int');
+	{
+		if (empty($conf->prospectingmap) || empty($conf->prospectingmap->enabled)) {
+			return 0; // Module not active, we do nothing
+		}
+
+		switch ($action) {
+			case 'COMPANY_CREATE':
+			case 'COMPANY_MODIFY':
+				dol_syslog("Trigger '" . $this->name . "' for action '$action' launched by " . __FILE__ . ". id=" . $object->id);
+				if (((isset($object->coordinate_longitude) || isset($object->coordinate_latitude)) && !empty($object->coordinate_format)) ||
+					isset($_GET['map_longitude']) || isset($_POST['map_longitude']) ||
+					isset($_GET['map_latitude']) || isset($_POST['map_latitude'])
+				) {
+					$prospectingmap = new ProspectingMap($this->db);
+
+					if ((isset($object->coordinate_longitude) || isset($object->coordinate_latitude)) && !empty($object->coordinate_format)) {
+						$longitude = $object->coordinate_longitude;
+						$latitude = $object->coordinate_latitude;
+
+						$converted_coordinate = $prospectingmap->convertCoordinates($object->coordinate_format, $conf->global->PROSPECTINGMAP_COORDINATES_METRICS, $longitude, $latitude);
+						if (!isset($converted_coordinate)) {
+							$this->errors[] = $langs->trans('Module163021Name') . ' : ' . $prospectingmap->errorsToString();
+							return -1;
+						}
+
+						$longitude = $converted_coordinate[0];
+						$latitude = $converted_coordinate[1];
+					} else {
+						$longitude = GETPOST('map_longitude', 'int');
+						$latitude = GETPOST('map_latitude', 'int');
+					}
 
-                    dol_include_once('/prospectingmap/lib/prospectingmap.lib.php');
-                    if ($longitude === '' && $latitude === '') {
-                        $res = prospectingmap_del_map_location($this->db, $object->id, $error_msg);
-                    } else {
-                        $res = prospectingmap_set_map_location($this->db, $object->id, $longitude, $latitude, $error_msg);
-                    }
-                    if ($res < 0) {
-                        $this->errors[] = $langs->trans('Module163021Name') . ' : ' . $error_msg;
-                        return -1;
-                    }
-                }
-                break;
+					if ($longitude === '' && $latitude === '') {
+						$res = $prospectingmap->delThirdPartyCoordinates($object->id);
+					} else {
+						$res = $prospectingmap->setThirdPartyCoordinates($object->id, $longitude, $latitude);
+					}
+					if ($res < 0) {
+						$this->errors[] = $langs->trans('Module163021Name') . ' : ' . $prospectingmap->errorsToString();
+						return -1;
+					}
+				}
+				break;
 
-            case 'COMPANY_DELETE':
-                dol_include_once('/prospectingmap/lib/prospectingmap.lib.php');
-                $res = prospectingmap_del_map_location($this->db, $object->id, $error_msg);
-                if ($res < 0) {
-                    $this->errors[] = $langs->trans('Module163021Name') . ' : ' . $error_msg;
-                    return -1;
-                }
-                break;
-        }
+			case 'COMPANY_DELETE':
+				dol_syslog("Trigger '" . $this->name . "' for action '$action' launched by " . __FILE__ . ". id=" . $object->id);
+				$prospectingmap = new ProspectingMap($this->db);
+				$res = $prospectingmap->delThirdPartyCoordinates($object->id);
+				if ($res < 0) {
+					$this->errors[] = $langs->trans('Module163021Name') . ' : ' . $prospectingmap->errorsToString();
+					return -1;
+				}
+				break;
+		}
 
-        return 0;
-    }
-}
\ No newline at end of file
+		return 0;
+	}
+}
diff --git a/lib/prospectingmap.lib.php b/lib/prospectingmap.lib.php
index e24c90a3bfe44e0fd6a12fd3814d05d6eee0d94d..1d4dd1cbf74a93f2a399cabaf0396cb94a751857 100755
--- a/lib/prospectingmap.lib.php
+++ b/lib/prospectingmap.lib.php
@@ -28,162 +28,35 @@
  */
 function prospectingmap_prepare_head()
 {
-    global $langs, $conf, $user;
-    $h = 0;
-    $head = array();
+	global $langs, $conf, $user;
+	$h = 0;
+	$head = array();
 
-    $head[$h][0] = dol_buildpath("/prospectingmap/admin/setup.php", 1);
-    $head[$h][1] = $langs->trans("Parameters");
-    $head[$h][2] = 'settings';
-    $h++;
+	$head[$h][0] = dol_buildpath("/prospectingmap/admin/setup.php", 1);
+	$head[$h][1] = $langs->trans("Parameters");
+	$head[$h][2] = 'settings';
+	$h++;
 
-    $head[$h][0] = dol_buildpath("/prospectingmap/admin/dictionaries.php", 1);
-    $head[$h][1] = $langs->trans("Dictionary");
-    $head[$h][2] = 'dictionaries';
-    $h++;
+	$head[$h][0] = dol_buildpath("/prospectingmap/admin/dictionaries.php", 1);
+	$head[$h][1] = $langs->trans("Dictionary");
+	$head[$h][2] = 'dictionaries';
+	$h++;
 
-    $head[$h][0] = dol_buildpath("/prospectingmap/admin/about.php", 1);
-    $head[$h][1] = $langs->trans("About") . " / " . $langs->trans("Support");
-    $head[$h][2] = 'about';
-    $h++;
+	$head[$h][0] = dol_buildpath("/prospectingmap/admin/about.php", 1);
+	$head[$h][1] = $langs->trans("About") . " / " . $langs->trans("Support");
+	$head[$h][2] = 'about';
+	$h++;
 
-    $head[$h][0] = dol_buildpath("/prospectingmap/admin/changelog.php", 1);
-    $head[$h][1] = $langs->trans("OpenDsiChangeLog");
-    $head[$h][2] = 'changelog';
-    $h++;
+	$head[$h][0] = dol_buildpath("/prospectingmap/admin/changelog.php", 1);
+	$head[$h][1] = $langs->trans("OpenDsiChangeLog");
+	$head[$h][2] = 'changelog';
+	$h++;
 
-    complete_head_from_modules($conf,$langs,null,$head,$h,'prospectingmap_admin');
+	complete_head_from_modules($conf, $langs, null, $head, $h, 'prospectingmap_admin');
 
-    return $head;
+	return $head;
 }
 
-/**
- *  Get map location for the specified company
- *
- * @param   DoliDb  $db         Database handler
- * @param   int     $socid      Company ID
- * @return  array               Coordinate of the company: array('lon' => 0, 'lat' => 0)
- */
-function prospectingmap_get_map_location($db, $socid)
-{
-    $longitude = null;
-    $latitude = null;
-    if ($socid > 0) {
-        $sql = "SELECT longitude, latitude FROM " . MAIN_DB_PREFIX . "prospectingmap_coordinate WHERE fk_soc = '" . $db->escape($socid) . "'";
-        $resql = $db->query($sql);
-        if ($resql) {
-            if ($obj = $db->fetch_object($resql)) {
-                $longitude = $obj->longitude;
-                $latitude = $obj->latitude;
-            }
-        }
-    }
-
-    return array('lon' => $longitude, 'lat' => $latitude);
-}
-
-/**
- *  Get map location for the specified company
- *
- * @param   DoliDb  $db             Database handler
- * @param   int     $socid          Company ID
- * @param   int     $longitude      Longitude of the map location for the company
- * @param   int     $latitude       Latitude of the map location for the company
- * @param   string  $error_msg      Message of the error
- * @return  int                     >0 if ok, <0 if ko, 0 if none
- */
-function prospectingmap_set_map_location($db, $socid, $longitude, $latitude, &$error_msg)
-{
-    if ($socid > 0) {
-        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prospectingmap_coordinate(fk_soc, longitude, latitude)" .
-            " VALUES ('" . $db->escape($socid) . "', '" . $db->escape($longitude) . "', '" . $db->escape($latitude) . "')" .
-            " ON DUPLICATE KEY UPDATE longitude = '" . $db->escape($longitude) . "', latitude = '" . $db->escape($latitude) . "'";
-        $resql = $db->query($sql);
-        if (!$resql) {
-            $error_msg = $db->lasterror();
-            return -1;
-        }
-
-        return 1;
-    }
-
-    return 0;
-}
-
-/**
- *  Delete map location for the specified company
- *
- * @param   DoliDb  $db             Database handler
- * @param   int     $socid          Company ID
- * @param   string  $error_msg      Message of the error
- * @return  int                     >0 if ok, <0 if ko, 0 if none
- */
-function prospectingmap_del_map_location($db, $socid, &$error_msg)
-{
-    if ($socid > 0) {
-        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "prospectingmap_coordinate WHERE fk_soc = '" . $db->escape($socid) . "'";
-        $resql = $db->query($sql);
-        if (!$resql) {
-            $error_msg = $db->lasterror();
-            return -1;
-        }
-
-        return 1;
-    }
-
-    return 0;
-}
-
-/**
- *  Delete map location for the specified company
- *
- * @param   DoliDb  $db             Database handler
- * @param   string  $old_metrics    Old metrics
- * @param   string  $new_metrics    New metrics
- * @param   string  $error_msg      Message of the error
- * @return  int                     >0 if ok, <0 if ko, 0 if none
- */
-function prospectingmap_convert_all_coordinate($db, $old_metrics, $new_metrics, &$error_msg)
-{
-    if ($old_metrics != $new_metrics) {
-        $error = 0;
-        $db->begin();
-
-        $sql = "SELECT fk_soc, longitude, latitude FROM " . MAIN_DB_PREFIX . "prospectingmap_coordinate";
-        $resql = $db->query($sql);
-        if ($resql) {
-            dol_include_once('/prospectingmap/class/coordinateconverter.class.php', 'CoordinateConverter');
-            while ($obj = $db->fetch_object($resql)) {
-                if ($old_metrics == 'EPSG:3857') {
-                    $converted_coordinate = CoordinateConverter::convertEpsg3857To4326([$obj->longitude, $obj->latitude]);
-                } else {
-                    $converted_coordinate = CoordinateConverter::convertEpsg4326To3857([$obj->longitude, $obj->latitude]);
-                }
-
-                $result = prospectingmap_set_map_location($db, $obj->fk_soc, $converted_coordinate[0], $converted_coordinate[1], $error_msg);
-                if ($result < 0) {
-                    $error++;
-                    break;
-                }
-            }
-            $db->free($resql);
-        } else {
-            $error_msg = $db->lasterror();
-            $error++;
-        }
-
-        if ($error) {
-            $db->rollback();
-            return -1;
-        } else {
-            $db->commit();
-        }
-    }
-
-    return 1;
-}
-
-
 /**
  *  Return multiselect javascript code
  *
@@ -194,46 +67,39 @@ function prospectingmap_convert_all_coordinate($db, $old_metrics, $new_metrics,
  */
 function multiselect_javascript_code($selected, $htmlname, $elemtype='', $width = '100%')
 {
-    global $conf;
+	global $conf;
 
-    $out = '';
+	$out = '';
 
-    // Add code for jquery to use multiselect
-       if (! empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT'))
-       {
-           $tmpplugin=empty($conf->global->MAIN_USE_JQUERY_MULTISELECT)?constant('REQUIRE_JQUERY_MULTISELECT'):$conf->global->MAIN_USE_JQUERY_MULTISELECT;
-              $out.='<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
+	// Add code for jquery to use multiselect
+	if (!empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT')) {
+		$tmpplugin = empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
+		$out .= '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
                <script type="text/javascript">
-                function formatResult(record) {'."\n";
-                    if ($elemtype == 'category')
-                    {
-                        $out.='	//return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> <a href="'.DOL_URL_ROOT.'/categories/viewcat.php?type=0&id=\'+record.id+\'">\'+record.text+\'</a></span>\';
-                                  return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> \'+record.text+\'</span>\';';
-                    }
-                    else
-                    {
-                        $out.='return record.text;';
-                    }
-        $out.= '	};
-                   function formatSelection(record) {'."\n";
-                    if ($elemtype == 'category')
-                    {
-                        $out.='	//return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> <a href="'.DOL_URL_ROOT.'/categories/viewcat.php?type=0&id=\'+record.id+\'">\'+record.text+\'</a></span>\';
-                                  return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> \'+record.text+\'</span>\';';
-                    }
-                    else
-                    {
-                        $out.='return record.text;';
-                    }
-        $out.= '	};
+                function formatResult(record) {' . "\n";
+		if ($elemtype == 'category') {
+			$out .= '	//return \'<span><img src="' . DOL_URL_ROOT . '/theme/eldy/img/object_category.png' . '"> <a href="' . DOL_URL_ROOT . '/categories/viewcat.php?type=0&id=\'+record.id+\'">\'+record.text+\'</a></span>\';
+                                  return \'<span><img src="' . DOL_URL_ROOT . '/theme/eldy/img/object_category.png' . '"> \'+record.text+\'</span>\';';
+		} else {
+			$out .= 'return record.text;';
+		}
+		$out .= '	};
+                   function formatSelection(record) {' . "\n";
+		if ($elemtype == 'category') {
+			$out .= '	//return \'<span><img src="' . DOL_URL_ROOT . '/theme/eldy/img/object_category.png' . '"> <a href="' . DOL_URL_ROOT . '/categories/viewcat.php?type=0&id=\'+record.id+\'">\'+record.text+\'</a></span>\';
+                                  return \'<span><img src="' . DOL_URL_ROOT . '/theme/eldy/img/object_category.png' . '"> \'+record.text+\'</span>\';';
+		} else {
+			$out .= 'return record.text;';
+		}
+		$out .= '	};
                 $(document).ready(function () {
-                    $(\'#'.$htmlname.'\').attr("name", "'.$htmlname.'[]");
-                    $(\'#'.$htmlname.'\').attr("multiple", "multiple");
-                    //$.map('.json_encode($selected).', function(val, i) {
-                        $(\'#'.$htmlname.'\').val('.json_encode($selected).');
+                    $(\'#' . $htmlname . '\').attr("name", "' . $htmlname . '[]");
+                    $(\'#' . $htmlname . '\').attr("multiple", "multiple");
+                    //$.map(' . json_encode($selected) . ', function(val, i) {
+                        $(\'#' . $htmlname . '\').val(' . json_encode($selected) . ');
                     //});
                 
-                       $(\'#'.$htmlname.'\').'.$tmpplugin.'({
+                       $(\'#' . $htmlname . '\').' . $tmpplugin . '({
                            dir: \'ltr\',
                         // Specify format function for dropdown item
                         formatResult: formatResult,
@@ -241,44 +107,44 @@ function multiselect_javascript_code($selected, $htmlname, $elemtype='', $width
                         // Specify format function for selected item
                         formatSelection: formatSelection,
                             templateResult: formatSelection,        /* For 4.0 */
-                            width: \''.$width.'\'
+                            width: \'' . $width . '\'
                        });
                    });
                </script>';
-       }
+	}
 
-       return $out;
+	return $out;
 }
 
 function inject_map_features($features, $chunk_size, $deep = 0)
 {
-    $error = 0;
+	$error = 0;
 
-    if (!empty($features)) {
-        $bulk_features = array_chunk($features, $chunk_size);
-        $idx = 0;
-        foreach ($bulk_features as $bulk) {
+	if (!empty($features)) {
+		$bulk_features = array_chunk($features, $chunk_size);
+		$idx = 0;
+		foreach ($bulk_features as $bulk) {
 //          print "console.log('Prospecting Map: Deep $deep - Idx $idx - Chunk size $chunk_size.');\n";
-            $encoded_bulk = json_encode($bulk);
-            if (!empty($encoded_bulk)) {
+			$encoded_bulk = json_encode($bulk);
+			if (!empty($encoded_bulk)) {
 //              print "console.log('Prospecting Map: Deep $deep - Idx $idx - " . count($bulk) . " map feature processed.');\n";
-                print "geojsonProspectMarkers.features = $.merge(geojsonProspectMarkers.features, $encoded_bulk);\n";
-            } else {
-                if ($chunk_size > 1) {
-                    $result = inject_map_features($bulk, floor($chunk_size / 2), $deep + 1);
-                    if ($result < 0) $error++;
-                } else {
-                    ob_start();
-                    // print_r($bulk);
-                    $content = ob_get_contents();
-                    ob_clean();
-                    print "console.error('Prospecting Map: Error encode json map feature, data:', '" . dol_escape_js($content, 1) ."');\n";
-                    $error++;
-                }
-            }
-            $idx++;
-        }
-    }
-
-    return $error ? -1 : 0;
+				print "geojsonProspectMarkers.features = $.merge(geojsonProspectMarkers.features, $encoded_bulk);\n";
+			} else {
+				if ($chunk_size > 1) {
+					$result = inject_map_features($bulk, floor($chunk_size / 2), $deep + 1);
+					if ($result < 0) $error++;
+				} else {
+					ob_start();
+					// print_r($bulk);
+					$content = ob_get_contents();
+					ob_clean();
+					print "console.error('Prospecting Map: Error encode json map feature, data:', '" . dol_escape_js($content, 1) . "');\n";
+					$error++;
+				}
+			}
+			$idx++;
+		}
+	}
+
+	return $error ? -1 : 0;
 }
\ No newline at end of file