Accueil > > > GÉNÉRATEUR HTML DE MAP POUR IMAGE CLIQUABLE
GÉNÉRATEUR HTML DE MAP POUR IMAGE CLIQUABLE
Information sur la source
Description
J'eus une grosse frayeur dans le cadre de mon projet V.I.P.E.R.E. concernant les maps de mes planches anatomiques. En effet, je dus envisager un moment de changer toutes les images de mes ces dernières, ce qui sous entendait de refaire toutes les zones cliquables pour pouvoir naviguer dans mon corps humain. Trouvant les logiciels de génération de map oh combien fastidieux, laborieux, longs et peu précis, comparés à l'ergonomie des logiciels de retouche d'images, je décidai de créer un générateur de map. Je voulais : - Par les calques de mon logiciel d'infographie préféré dessiner les zones cliquables - Aller chercher cette image - Modifier éventuellement le ou les liens ainsi que la ou les infobulles dynamiquement - Faire un copier / coller du HTML de MAP généré dans ma page web Le résultat se trouve dans le fichier Map.php. Vous pouvez l'essayer en vous rendant sur http://www.vipere.lekod.com/Francais/Map.php Pré- requis : Installer la bibliothèque php_gd2 A faire : Renseigner les constantes du script Alerte : Ce script consomme pas mal de ressource processeur. De plus, plus l'image est grande et fournie, plus long il met à se terminer. C'est pour cela que j'ai bridé ma version de démo afin de ne pas saturer mon serveur.
Source
- <html>
- <html dir="LTR">
- <head>
- <meta http-equiv="content-type" content="text/html; charset=utf-8">
- <meta name="Title" lang="fr" content="V.I.P.E.R.E.">
- <meta name="Author" lang="fr" content="Florent Benetiere">
- <meta name="Reply-to" content="tnerolf@lekod.com">
- <meta name="Copyright" content="Florent Benetiere">
- <meta name="revisit-after" content="8">
- <meta name="Robot" content="index,follow,all">
- <meta http-equiv="pragma" content="no-cache">
- <TITLE>Générateur de MAP pour image cliquable</TITLE>
- </head>
- <body>
- <?php
- /*Auteur : Florent Bénetière tnerolf@lekod.com
-
- L'extraction des contours étant longue, on désactive la limite de temps d'exécution pour ce script*/
- set_time_limit(3600);
-
- //Renseignez la constante Chemin_copie_img avec un chemin local où copier le fichier image téléchargé
- define("Chemin_copie_img","[Chemin local du stockage de l'image téléchargée]",true);
- /*Renseignez la constante Chemin_URL_img avec un chemin http pour indiquer le src de l'image
- S'il n'y en a pas, saisissez la même valeur que Chemin_copie_img*/
- define("URL_img","http://[URL vers le répertoire contenant l'image en ligne]",true);
- //Renseignez la constante Taille_fichier_max avec la taille maximale en octets que vous accepter pour un fichier.
- define("Taille_fichier_max",[Taille en octets],true);
- //Renseignez la constante DimX_max avec la largeur maximale de l'image en pixel que vous acceptez
- define("DimX_max",[Taille en pixels],true);
- //Renseignez la constante DimY_max avec la hauteur maximale de l'image en pixel que vous acceptez
- define("DimY_max",[Taille en pixels],true);
- //Les variables globales
- $Image=0;$Tol_Min;$Tol_Max;$Dim;$x=0;$y=0;$Save_x=0;$Save_y=0;$Compteur=0;$Coord_pixels;
-
- $Pixels;$Zones;$En_trop;
- /*
- $Pixels permet de récupérer toutes les coordonnées des contours des formes opaques
- $Zones permet de générer l'HTML des coordonnées des Map
- */
- function Utilise($x,$y)
- {
- //Cette fonction regarde dans tous le code HTML des Maps si les coordonnées d'un pixel sont déja présentes
- global $Zones,$Pixels;
- if(!$Zones) return;
- //On regarde d'abord si c'est un contour
- if(in_array("~".$x.",".$y,$Pixels))
- {
- foreach($Zones as $HTML)
- {
- if(preg_match("&~".$x.",".$y."~&",$HTML))return true;
- }
- return false;
-
- }
- else return true;//Pour que les pixels qui ne sont pas des contours ne soient pas considérés comme inutilisés.
- }
-
- function Transp($Palette,$x,$y)
- {
- global $Tol_Min,$Tol_Max,$Dim;
- /* Si les coordonnées débordent de l'image, les valeurs de $Palette sont à 0
- ce qui correspond à un Noir opaque.
- Or, si le pixel en 0,0 n'est pas de cette couleur, les pixels de dépassement d'image (-1 et > $Dim["x"] et $Dim["y"].
- sont obligatoirement considérés comme opaques au lieu de transparents.
- On force donc la transparence.
- */
- if($x>=$Dim["x"]||$y>=$Dim["y"]||$x<0||$y<0)
- {
- return true;
- }
- elseif
- (
- $Palette["red"]>=$Tol_Min["Rouge"] && $Palette["red"]<=$Tol_Max["Rouge"] &&
- $Palette["green"]>=$Tol_Min["Vert"] && $Palette["green"]<=$Tol_Max["Vert"] &&
- $Palette["blue"]>=$Tol_Min["Bleu"] && $Palette["blue"]<=$Tol_Max["Bleu"]
- )
- {
- return true;//=>Pixel transparent
- }
- else return false;
- }
-
- function Est_contour($x,$y)
- {
- /*
- Cette fonction regarde dans les 8 directions par rapport à la portée passée en argument.
- Si tous les pixels adjacents sont opaques alors on ne prend pas le pixel actif
- */
- global $Image,$Dim,$Pixels,$Coord_pixels,$Tol_Min,$Tol_Max,$En_trop;
- $Palette=imagecolorsforindex($Image, imagecolorat($Image,$x,$y));
-
- if(!Transp($Palette,$x,$y))
- {
- //On regarde l'opacité des pixels adjacents
- //N
- $Abs=$x;$Ordo=$y-1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
- if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='N|';
- //NE
- $Abs=$x+1;$Ordo=$y-1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
- if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='NE|';
- //E
- $Abs=$x+1;$Ordo=$y;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
- if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='E|';
- //SE
- $Abs=$x+1;$Ordo=$y+1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
- if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='SE|';
- //S
- $Abs=$x;$Ordo=$y+1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
- if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='S|';
- //SO
- $Abs=$x-1;$Ordo=$y+1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
- if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='SO|';
- //O
- $Abs=$x-1;$Ordo=$y;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
- if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='O|';
- //NO
- $Abs=$x-1;$Ordo=$y-1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
- if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='NO';
-
- if($Cardinal!='N|NE|E|SE|S|SO|O|NO')
- {
- /*Il s'agit donc d'un contour.
- On "double" l'épaisseur du trait correspondant au contour des formes
- car autrement on a un plantage lorsque deux pixels de contour ne sont séparés que par un pixel transparent.
- En effet, les pixels précédents sont déja pris et il n'est plus possible de déterminer la suite du contour.
- Exemple:
- Légende : Chiffres : Parcours du contour Ok, E pour Erreur
-
- 12345
- XXXX6
- 7
- 89
- E0
- On renseigne $Pixels en insérant dans $Pixels les pixels adjacents au pixel de contour trouvé
- et $En_trop qui servira plus tard à supprimer les pixels superflus.
- Le pixel de contour réel n'est pas inséré car il sera forcément entourés de pixels adjacents, donc il sera opaque*/
-
- for($a=0;$a<8;$a++)
- {
- switch($a)
- {
- case 0:
- $Testx=$x;$Testy=$y-1;//N
- break;
- case 1:
- $Testx=$x+1;$Testy=$y-1;//NE
- break;
- case 2:
- $Testx=$x+1;$Testy=$y;//E
- break;
- case 3:
- $Testx=$x+1;$Testy=$y+1;//SE
- break;
- case 4:
- $Testx=$x;$Testy=$y+1;//S
- break;
- case 5:
- $Testx=$x-1;$Testy=$y+1;//SO
- break;
- case 6:
- $Testx=$x-1;$Testy=$y;//O
- break;
- case 7:
- $Testx=$x-1;$Testy=$y-1;//NO
- break;
- }
- $Pixels[]='~'.$Testx.','.$Testy;
- $Taille=count($Pixels)-1;
- $Coord_pixels[$Taille]["x"]=$Testx;$Coord_pixels[$Taille]["y"]=$Testy;
- $En_trop[]=array("Cle_Pixels"=>$Taille);
- }
- return true;
- }
- }
- }
-
- function Next_contour()
- {
- global $Zones,$x,$y,$Save_x,$Save_y,$Compteur,$Taille,$Dim;
- ++$Compteur;
- static $Prov;
-
- if(!$Prov["x"])
- {
- $Prov["x"]=$x;$Prov["y"]=$y;
- }
- /*On regarde si un pixel adjacent au pixel en cours correspond au premier pixel du contour
- N = $y-1
- NE = $x+1 et $y-1
- E = $x+1
- SE = $x+1 et $y+1
- S = $y+1
- SO = $x-1 et $y+1
- O = $x-1
- NO = $x-1 et $y-1
-
- $Compteur permet de ne pas finaliser la MAP tant que l'on n'est pas positionné sur le quatrième pixel.
-
- Exemple :
-
- 1|2
- x|3
- x|4
-
- Premier pixel détecté : 1 => $Compteur=1
- Second pixel détecté : 2 => $Compteur=2 mais le pixel à l'Ouest est celui du démarrage du contour donc, si on ne bride pas, le contour sera finalisé.
- Troisième pixel détecté : 3 => $Compteur=3 mais le pixel au Nord-Ouest est celui du démarrage du contour donc, si on ne bride pas, le contour sera finalisé.
- Au delà du troisième pixel, si les coordonnées d'un pixel adjacent correspondent à celle du premier pixel de contour cela signifie que l'on a fait le tour du contour.
-
- On détermine quelle est la direction en cours et en fonction de celle-ci, on détermine à partir de quel cardinalité on commence
- et quel sens on prend (aiguille d'une montre ou l'inverse)
- pour tourner autours du pixel
-
- $x==$Prov["x"] && $y==$Prov["y"] => Initialisation
- $x==$Prov["x"] && $y <$Prov["y"] => N
- $x >$Prov["x"] && $y <$Prov["y"] => NE
- $x >$Prov["x"] && $y==$Prov["y"] => E
- $x >$Prov["x"] && $y >$Prov["y"] => SE
- $x==$Prov["x"] && $y >$Prov["y"] => S
- $x <$Prov["x"] && $y >$Prov["y"] => SO
- $x <$Prov["x"] && $y==$Prov["y"] => O
- $x <$Prov["x"] && $y <$Prov["y"] => NO
-
- "0|-1" => N
- "1|-1" => NE
- "1|0" => E
- "1|1" => SE
- "0|1" => S
- "-1|1" => SO
- "-1|0" => O
- "-1|-1" => NO*/
-
- if(($x==$Prov["x"] && $y==$Prov["y"])
- ||($x==$Prov["x"] && $y <=$Prov["y"])
- ||($x >=$Prov["x"] && $y <=$Prov["y"])
- ||($x >=$Prov["x"] && $y==$Prov["y"])
- ||($x >=$Prov["x"] && $y >=$Prov["y"]))
- {
- $Next=array("0|-1","1|-1","1|0","1|1","0|1","-1|1","-1|0","-1|-1" );
- }
- elseif(($x==$Prov["x"] && $y >$Prov["y"])
- ||($x <$Prov["x"] && $y >$Prov["y"])
- ||($x <$Prov["x"] && $y==$Prov["y"])
- ||($x <$Prov["x"] && $y <$Prov["y"]))
- {
- $Next=array("0|1","-1|1","-1|0","-1|-1","0|-1","1|-1","1|0","1|1");
- }
-
- /* Et pourquoi tout cela me direz-vous ?
- C'est très simple : Lorsqu'un prochain pixel est une diagonale, cela laisse les pixels "perpendiculaires" en statut inutilisé.
- Or, pour les diagonales allant vers l'Ouest, en partant de Nord, un mauvais pixel sera inclu dans le contour de la MAP et ensuite
- il n'y aura plus de prochain pixel pour poursuivre la génération de ce contour, ce qui plantera le script.
-
- Exemple avec Sud-Ouest :
- Légende : [X] => Pixels de contour trouvé
- [Y] => Pixel considéré comme faisant partie du contour
- [Z] => Prochain pixel à intégrer
- [P] => Pixel considéré comme plein (ne faisant pas partie du contour
- [o] => Pixel transparent
-
- [X][X][X][o][o][o]
- [P][P][X][X][X][o]
- [P][P][P][Y][X][o]
- [P][P][P][X][o][o]
- [P][P][Z][o][o][o]
-
- En arrivant au [X] le plus bas, si on suit le sens des aiguilles d'une montre, le pixel [Y] sera intégré dans les coordonnées de la MAP car il sera considéré comme inutilisé.
- Ensuite, lorsque l'on fera le tour de ce pixel pour trouver le pixel suivant, on n'en trouvera pas car tous les pixels adjacents à celui-ci seront utilisés.
- */
- foreach($Next as $Val)
- {
- $Test=explode("|", $Val);
-
- if(!Utilise(($x+$Test[0]),($y+$Test[1])))
- {
- $Prov["x"]=$x;$Prov["y"]=$y;//On garde le pixel trouvé pour voir la provenance lors du traitement du pixel suivant.
- //Pour les diagonales, on ajoute dans les pixels utilisés celui qui n'a pas été pris par la diagonale, afin qu'il ne puisse faire dévier le tracé du contour ultérieurement.
- switch($Test[0].'|'.$Test[1])
- {
- case "1|-1": //NE
- $Zones[$Taille].='~'.($x+1).','.$y;
- break;
- case "1|1": //SE
- $Zones[$Taille].='~'.$x.','.($y+1);
- break;
- case "-1|1": //SO
- $Zones[$Taille].='~'.($x-1).','.$y;
- break;
- case "-1|-1": //NO
- $Zones[$Taille].='~'.$x.','.($y-1);
- break;
- }
- $x+=$Test[0];$y+=$Test[1];
- $Zones[$Taille].='~'.$x.','.$y;
- return 1;
- }
- }
-
- if((($x==$Save_x && $y-1==$Save_y) ||//N
- ($x+1==$Save_x && $y-1==$Save_y) ||//NE
- ($x+1==$Save_x && $y==$Save_y) || //E
- ($x+1==$Save_x && $y+1==$Save_y) ||//SE
- ($x==$Save_x && $y+1==$Save_y) || //S
- ($x-1==$Save_x && $y+1==$Save_y) ||//SO
- ($x-1==$Save_x && $y==$Save_y) ||//O
- ($x-1==$Save_x) && $y-1==$Save_y)&& $Compteur>3)//NO
- {
- $Zones[$Taille]='<AREA SHAPE="poly" COORDS="'.$Zones[$Taille].'~'.$x.','.$y.'" HREF="'.$_POST["Lien"].'" title="'.$_POST["Infobulle"].'">';//On ferme la balise Map
- $Save_x=NULL;$Save_y=NULL;$Compteur=0;
- unset($Prov);
- return 2;
- }
-
- //Aucun pixel de prochain contour => Le contours est ouvert => on le ferme pour ne pas planter le script
- $Zones[$Taille]='<AREA SHAPE="poly" COORDS="'.$Zones[$Taille].'" HREF="'.$_POST["Lien"].'" title="'.$_POST["Infobulle"].'">';//On ferme la balise Map
- $Save_x=NULL;$Save_y=NULL;$Compteur=0;
- unset($Prov);
-
- return 0;
- }
-
- function Trouve_bornes($Map)
- {
- $Bornes=array("Min_x"=>0,"Max_x"=>0,"Min_y"=>0,"Max_y"=>0);
-
- preg_match_all("&~([[:digit:]]+),([[:digit:]]+)~&",$Map,$Result);
- /*$Result[1]=Abscisses, $Result[2]=Ordonnées
- On détermine les bornes minimales et maximales des abscisses et des ordonnées*/
-
- array_multisort($Result[1],SORT_ASC,SORT_NUMERIC);
- array_multisort($Result[2],SORT_ASC,SORT_NUMERIC);
-
- $Bornes["Min_x"]=$Result[1][0];$Bornes["Max_x"]=$Result[1][count($Result[1])-1];
- $Bornes["Min_y"]=$Result[2][0];$Bornes["Max_y"]=$Result[2][count($Result[2])-1];
- return $Bornes;
- }
-
- function Suppr_superflus($Map)
- {
- global $Zones;
-
- /* Lorsque l'on a épaissi les contours, on a créé deux zones de map, l'une extérieure au contour et l'autre intérieure.
-
- Exemple :
-
- 111111111 Légende :
- 1XXXXXXX1 X : Contour original supprimé car considéré comme opaque par l'épaississement.
- 1X22222X1 1 : Contour extérieur.
- 1X2 2X1 2 : Contour intérieur.
- 1X2 2X1
- 1X22222X1
- 1XXXXXXX1
- 111111111
-
- Automatiquement, la zone extérieure est prise en premier pour générer le contour car ses pixels sont détectés avant ceux de la zone intérieure.
- On suprimme donc la zone intérieure.
-
- - On classe les coordonnées par l'abscisse et l'ordonnée
- - On récupère l'abscisse et l'ordonnée minimale et maximale de la zone
- - Pour chaque zone antérieurement générée on regarde si l'absisse et l'ordonnée minimales sont à l'intérieure de la zone
- - Si c'est le cas => Zone interne => on la supprimme.
- - Pour chaque zone précédemment générée :
- - On récupère les bornes
- - On regarde si la surface délimitée par les bornes de $Map sont comprises dans la zone délimitée par les bornes de la zone
- - Si oui :
- - On supprime la zone en cours de génération.
- - On supprime de $Pixels tous les pixels qui ont servi à générer la zone
- */
- $Bornes_Map=Trouve_bornes($Map);
-
- foreach($Zones as $Val)
- {
- if($Map!=$Val)
- {
- $Bornes_zone=Trouve_bornes($Val);
-
- if($Bornes_Map["Min_x"]>=$Bornes_zone["Min_x"] && $Bornes_Map["Max_x"]<=$Bornes_zone["Max_x"]
- && $Bornes_Map["Min_y"]>=$Bornes_zone["Min_y"] && $Bornes_Map["Max_y"]<=$Bornes_zone["Max_y"])
- {
- $Zones[array_search($Map,$Zones)].="A ne pas prendre";
- return;
- }
- }
- }
- }
-
- if(!$_FILES['Fichier']['tmp_name'])
- {
- echo '
- <script language="javascript" type="text/javascript">
- function Valid()
- {
- with(document.Form1)
- {
- if(Fichier.value.length==0)
- {
- alert("Sans fichier à charger je n\'ai que mes yeux pour pleurer");
- Fichier.focus();
- }
- else if(Tolerance.value<0||Tolerance.value>100 || isNaN(Tolerance.value))
- {
- alert("La tolérance doit être un nombre compris entre 0 et 100");
- Tolerance.focus();
- }
- else if(Map.value.length==0)
- {
- alert("Sans nom de Map, pas de prise de cap !");
- Map.focus();
- }
- else if(Lien.value.length==0)
- {
- alert("Sans lien je ne peux rien.");
- Lien.focus();
- }
- else
- {
- submit();
- }
- }
- }
- </script>
- <form Enctype="multipart/form-data" Name="Form1" Method="post" action="'.htmlentities($_SERVER['PHP_SELF']).'">
- <input type="hidden" name="MAX_FILE_SIZE" value="'.Taille_fichier_max.'">
- <h1>Générateur de Map pour images cliquables</h1>
- <p>Image à mapper sachant que le premier pixel en haut à gauche
- sera celui définissant <b>"la transparence"</b>.<br>La taille du fichier
- ne doit pas dépasser <b>'.number_format(Taille_fichier_max/1024000,2,',',' ').' Mo</b>.
- <br>Ses dimensions maximales sont de <b>'.DimX_max.'</b> pixels de large et de <b>'.DimY_max.'</b> pixels de haut.<br>Les formats acceptés sont <b>GIF et PNG</b></p>
- <p>Image à traiter : <input type="file" name="Fichier"></p>
- <p>Le premier pixel en haut à gauche servira à définir
- la "transparence". Indiquez la tolérance <input type="text" name="Tolerance" value="0" maxlength="3" size="3" style="text-align:right;">
- %</p>
- <p>Indiquez le nom de la MAP <input type="text" name="Map" maxlength="100" size="50" value="MAP_Lekod"></p>
- <p> Indiquez le lien par défaut <input type="text" name="Lien" maxlength="100" size="50" value="http://Mon_site.com"></p>
- <p>Indiquez l\'infobulle par défaut <input type="text" name="Infobulle" maxlength="100" size="50" value="Mon infobulle"></p>
- <p><input type="button" name="B_Valid" value="Générer la Map" onclick="Valid()"></p>
- <H2 align="center">Ce processus étant <u>assez long</u>,<br> il vous faudra faire montre de <u>patience</u>.</H2>
- </div>
- ';
- }
- else
- {
- //On vérifie que les dimensions et la taille sont dans les limites
- if($_FILES['Fichier']['size']>Taille_fichier_max)
- {
- unlink($_FILES['Fichier']['tmp_name']);
- die('La taile du fichier excède les <b>'.number_format(Taille_fichier_max/1024000,2,',',' ').'</b> Mo<br>
- Abandon de la génération');
- }
- switch(exif_imagetype($_FILES['Fichier']['tmp_name']))
- {
- case IMAGETYPE_GIF:
- $Image=imagecreatefromgif($_FILES['Fichier']['tmp_name']);$Ext='.gif';
- break;
-
- case IMAGETYPE_PNG:
- $Image=imagecreatefrompng($_FILES['Fichier']['tmp_name']);$Ext='.png';
- break;
- default:
- echo '<script language="javascript">alert("Le format du fichier selectionne n\'est pas reconnu !");
- window.location=\''.$_SERVER['PHP_SELF'].'\';
- </script>';
- unlink($_FILES['Fichier']['tmp_name']);die();
- break;
- }
- $Dim=array("x"=>imagesx($Image),"y"=>imagesy($Image));
- if($Dim["x"]>DimX_max||$Dim["y"]>DimY_max)
- {
- imagedestroy($Image);
- die('Les dimensions de l\'image sont supérieures à <b>'.DimX_max.'</b> pixels de large et à <b>'.DimY_max.'</b> pixels de haut.');
- }
- // La couleur de transparence est le pixel en (0,0)
- $Transparence=imagecolorsforindex($Image, imagecolorat($Image,0,0));
-
- //On détermine les couleurs minimales et maximales de tolérance
- $Tol_Min["Rouge"]=intval($Transparence["red"]-($Transparence["red"]*$_POST["Tolerance"]/100));
- $Tol_Max["Rouge"]=intval($Transparence["red"]+($Transparence["red"]*$_POST["Tolerance"]/100));
- $Tol_Min["Vert"]=intval($Transparence["green"]-($Transparence["green"]*$_POST["Tolerance"]/100));
- $Tol_Max["Vert"]=intval($Transparence["green"]+($Transparence["green"]*$_POST["Tolerance"]/100));
- $Tol_Min["Bleu"]=intval($Transparence["blue"]-($Transparence["blue"]*$_POST["Tolerance"]/100));
- $Tol_Max["Bleu"]=intval($Transparence["blue"]+($Transparence["blue"]*$_POST["Tolerance"]/100));
-
- /*On parcourt le tableau
- - Si le pixel est compris dans la plage de tolérance, on n'insère pas ses coordonnées dans $Zones
- */
-
- for($a=0;$a<=$Dim["y"];$a++)
- {
- for($b=0;$b<=$Dim["x"];$b++)
- {
- Est_contour($b,$a);
- }
- }
- imagedestroy($Image);
- if($Pixels[0]=='')die('Il n\'y a rien à mapper.<br>Réduisez la tolérance ou bien changez les zones transparentes');
- /*En épaississant les contours précédemment, on a virtualisé des pixels "opaques"
- On les supprime de $Pixels*/
-
- foreach($Pixels as $Cle => $Val)
- {
- $Poid=0;
- $x=$Coord_pixels[$Cle]["x"];$y=$Coord_pixels[$Cle]["y"];
- if(in_array('~'.$x.','.($y-1),$Pixels)) ++$Poid;//N
- if(in_array('~'.($x+1).','.($y-1),$Pixels)) ++$Poid;//NE
- if(in_array('~'.($x+1).','.$y,$Pixels)) ++$Poid;//E
- if(in_array('~'.($x+1).','.($y+1),$Pixels)) ++$Poid;//SE
- if(in_array('~'.$x.','.($y+1),$Pixels)) ++$Poid;//S
- if(in_array('~'.($x-1).','.($y+1),$Pixels)) ++$Poid;//SO
- if(in_array('~'.($x-1).','.$y,$Pixels)) ++$Poid;//O
- if(in_array('~'.($x-1).','.($y-1),$Pixels)) ++$Poid;//NO
- if($Poid==8) $En_trop[$Cle]["Opaque"]=true;
- }
-
- //Maintenant qu'on a marqué tous les pixels "opaques", on les enlève de $Pixels
- foreach($En_trop as $Cle=>$Val)
- {
- if($Val["Opaque"])
- {
- unset($Pixels[$Val["Cle_Pixels"]]);
- unset($Coord_pixels[$Val["Cle_Pixels"]]);
- unset($En_trop[$Cle]);
- }
- }
- /*
- Maintenant qu'on a les contours, pour chaque point, on détermine le suivant
- à la manière du Petit Poucet, ce qui permet de générer les Maps
- */
- foreach($Pixels as $Cle=>$Valeur)
- {
- //On extrait les coordonnées du code HTML
- $x=$Coord_pixels[$Cle]["x"];$y=$Coord_pixels[$Cle]["y"];
- if(!Utilise($x,$y))
- {
- $Save_x=$x;$Save_y=$y;
- //On instancie une nouvelle zone
- $Zones[].='~'.$x.','.$y;
- $Taille=count($Zones)-1;
- while(!preg_match('~^<AREA SHAPE="poly"~',$Zones[$Taille]))
- {
- while(1==1)
- {
- $Result=Next_contour();
- if($Result==0 || $Result==2)
- {/*$Result==0)=>Aucun pixel de prochain contour => On traite le pixel suivant en finalisant la Map
- $Result==2)=>On a finit de faire le tour de la forme => Finalisation du HTML de la Map*/
- Suppr_superflus($Zones[$Taille]);//On nettoie des zones intérieures ou batardes
- break 2;
- }
- }
- }
- }
- }
- $Taille=count($Zones);
- for($a=0;$a<$Taille;$a++)
- {
- if(!strpos($Zones[$a],"A ne pas prendre"))
- {
- /*Malgré toute ma bonne volonté pour ne pas mettre de zones superflues et de supprimmer tous les pixels inutiles à l'élaboration des maps,
- il arrive qu'il soit possible de se retrouver avec des zones d'un pixel. On ne les prend pas en compte.
- */
- $Nbre_coord=explode(",",$Zones[$a]);
-
- if(count($Nbre_coord)==2)//Cela veut dire qu'il n'y a qu'un pixels de contour
- {
- unset($Zones[$a]);
- }
- else
- {
- $Map.=str_replace(array("\"~","~",-1),array("\"",",",0),$Zones[$a]);
- }
-
- }//-1 est présent lorsqu'en épaississant les contours on déborde vers les bords haut et/ou gauche de l'image.
- else//Par soucis d'esthétisme, on remet à la place un 0. On pourrait faire pareil pour les bords supérieurs lorsque les contours touchent à droite et/ou en bas
- {
- unset($Zones[$a]);
- }
- }
- //On déplace le fichier téléchargé pour affichage de l'image
- $Fichier=Chemin_copie_img.'Map_'.time().$Ext;
- copy($_FILES['Fichier']['tmp_name'],$Fichier);
- echo '
- <script language="javascript" type="text/javascript">
- function Changt()
- {
- var Areas=document.getElementsByTagName("AREA");
- var Textes=document.getElementsByTagName("input");
- var Map=document.getElementsByTagName("MAP");
- var Compteur=0
-
- Map=Map[0];
- Map.innerHTML=Map.innerHTML.replace(/title/gi,"alt");
-
- for(a=0;a<Textes.length;a++)
- {
- if(Textes[a].name.substring(0,4)=="Lien")
- {
- Areas[Compteur].href=Textes[a].value;
- }
- else if(Textes[a].name.substring(0,9)=="Infobulle")
- {
- Areas[Compteur].alt=Textes[a].value;
- Compteur++;
- }
- }
- //Firefox ne reconnait pas les alt. On change cette propriété en title
- Map.innerHTML=Map.innerHTML.replace(/alt/gi,"title");
- document.Form1.Result.value="<MAP NAME=\"'.$_POST["Map"].'\">"+Map.innerHTML+"</MAP>";
- }
- </script>
- <MAP NAME="'.$_POST["Map"].'">'.
- $Map.'</MAP>
- <P name="P_Image" align="center">
- <img src="'.str_replace(Chemin_copie_img,URL_img,$Fichier).'" name="Image" usemap="#'.$_POST["Map"].'">
- </p>
- <p><textarea name="Result" rows="10" cols="100" wrap="soft"><MAP NAME="'.$_POST["Map"].'">'.
- $Map.'</MAP></textarea></p>
- <p>Pour changer le lien d\'une zone de l\'image modifiez la valeur dans la zone <b>"Changer le lien"</b>.
- <p>Pour changer l\'infobulle d\'une zone de l\'image modifiez la valeur dans la zone <b>"Changer l\'infobulle"</b></p>
- <table width="100%">
- <tr>
- <td>
- <p align="center">Changer le lien</p>
- </td>
- <td >
- <p align="center">Changer l\'infobulle</p>
- </td>
- </tr>';
- //On met de quoi changer la valeur des liens et des infobulles
- foreach($Zones as $Cle=>$Val)
- {
- echo '<tr>
- <td align="center">
- <input type="text" name="Lien'.$Cle.'" value="'.$_POST["Lien"].'" size="50%">
- </td>
- <td align="center">
- <input type="text" name="Infobulle'.$Cle.'" value="'.$_POST["Infobulle"].'" size="50%">
- </td>
- </tr>';
- }
-
- echo '</table>
- <p align="center">
- <input type="button" name="B_Valid" value="Valider les changements" onclick="Changt()">
- <input type="button" name="B_Reinit" value="Play it again Sam" onclick="javascript:window.location=\''.htmlentities($_SERVER['PHP_SELF']).'\'"></p>
- ';
- }
- ?>
- </form>
- </body>
-
- </html>
<html>
<html dir="LTR">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="Title" lang="fr" content="V.I.P.E.R.E.">
<meta name="Author" lang="fr" content="Florent Benetiere">
<meta name="Reply-to" content="tnerolf@lekod.com">
<meta name="Copyright" content="Florent Benetiere">
<meta name="revisit-after" content="8">
<meta name="Robot" content="index,follow,all">
<meta http-equiv="pragma" content="no-cache">
<TITLE>Générateur de MAP pour image cliquable</TITLE>
</head>
<body>
<?php
/*Auteur : Florent Bénetière tnerolf@lekod.com
L'extraction des contours étant longue, on désactive la limite de temps d'exécution pour ce script*/
set_time_limit(3600);
//Renseignez la constante Chemin_copie_img avec un chemin local où copier le fichier image téléchargé
define("Chemin_copie_img","[Chemin local du stockage de l'image téléchargée]",true);
/*Renseignez la constante Chemin_URL_img avec un chemin http pour indiquer le src de l'image
S'il n'y en a pas, saisissez la même valeur que Chemin_copie_img*/
define("URL_img","http://[URL vers le répertoire contenant l'image en ligne]",true);
//Renseignez la constante Taille_fichier_max avec la taille maximale en octets que vous accepter pour un fichier.
define("Taille_fichier_max",[Taille en octets],true);
//Renseignez la constante DimX_max avec la largeur maximale de l'image en pixel que vous acceptez
define("DimX_max",[Taille en pixels],true);
//Renseignez la constante DimY_max avec la hauteur maximale de l'image en pixel que vous acceptez
define("DimY_max",[Taille en pixels],true);
//Les variables globales
$Image=0;$Tol_Min;$Tol_Max;$Dim;$x=0;$y=0;$Save_x=0;$Save_y=0;$Compteur=0;$Coord_pixels;
$Pixels;$Zones;$En_trop;
/*
$Pixels permet de récupérer toutes les coordonnées des contours des formes opaques
$Zones permet de générer l'HTML des coordonnées des Map
*/
function Utilise($x,$y)
{
//Cette fonction regarde dans tous le code HTML des Maps si les coordonnées d'un pixel sont déja présentes
global $Zones,$Pixels;
if(!$Zones) return;
//On regarde d'abord si c'est un contour
if(in_array("~".$x.",".$y,$Pixels))
{
foreach($Zones as $HTML)
{
if(preg_match("&~".$x.",".$y."~&",$HTML))return true;
}
return false;
}
else return true;//Pour que les pixels qui ne sont pas des contours ne soient pas considérés comme inutilisés.
}
function Transp($Palette,$x,$y)
{
global $Tol_Min,$Tol_Max,$Dim;
/* Si les coordonnées débordent de l'image, les valeurs de $Palette sont à 0
ce qui correspond à un Noir opaque.
Or, si le pixel en 0,0 n'est pas de cette couleur, les pixels de dépassement d'image (-1 et > $Dim["x"] et $Dim["y"].
sont obligatoirement considérés comme opaques au lieu de transparents.
On force donc la transparence.
*/
if($x>=$Dim["x"]||$y>=$Dim["y"]||$x<0||$y<0)
{
return true;
}
elseif
(
$Palette["red"]>=$Tol_Min["Rouge"] && $Palette["red"]<=$Tol_Max["Rouge"] &&
$Palette["green"]>=$Tol_Min["Vert"] && $Palette["green"]<=$Tol_Max["Vert"] &&
$Palette["blue"]>=$Tol_Min["Bleu"] && $Palette["blue"]<=$Tol_Max["Bleu"]
)
{
return true;//=>Pixel transparent
}
else return false;
}
function Est_contour($x,$y)
{
/*
Cette fonction regarde dans les 8 directions par rapport à la portée passée en argument.
Si tous les pixels adjacents sont opaques alors on ne prend pas le pixel actif
*/
global $Image,$Dim,$Pixels,$Coord_pixels,$Tol_Min,$Tol_Max,$En_trop;
$Palette=imagecolorsforindex($Image, imagecolorat($Image,$x,$y));
if(!Transp($Palette,$x,$y))
{
//On regarde l'opacité des pixels adjacents
//N
$Abs=$x;$Ordo=$y-1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='N|';
//NE
$Abs=$x+1;$Ordo=$y-1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='NE|';
//E
$Abs=$x+1;$Ordo=$y;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='E|';
//SE
$Abs=$x+1;$Ordo=$y+1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='SE|';
//S
$Abs=$x;$Ordo=$y+1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='S|';
//SO
$Abs=$x-1;$Ordo=$y+1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='SO|';
//O
$Abs=$x-1;$Ordo=$y;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='O|';
//NO
$Abs=$x-1;$Ordo=$y-1;$Palette=imagecolorsforindex($Image, imagecolorat($Image,$Abs,$Ordo));
if(!Transp($Palette,$Abs,$Ordo)) $Cardinal.='NO';
if($Cardinal!='N|NE|E|SE|S|SO|O|NO')
{
/*Il s'agit donc d'un contour.
On "double" l'épaisseur du trait correspondant au contour des formes
car autrement on a un plantage lorsque deux pixels de contour ne sont séparés que par un pixel transparent.
En effet, les pixels précédents sont déja pris et il n'est plus possible de déterminer la suite du contour.
Exemple:
Légende : Chiffres : Parcours du contour Ok, E pour Erreur
12345
XXXX6
7
89
E0
On renseigne $Pixels en insérant dans $Pixels les pixels adjacents au pixel de contour trouvé
et $En_trop qui servira plus tard à supprimer les pixels superflus.
Le pixel de contour réel n'est pas inséré car il sera forcément entourés de pixels adjacents, donc il sera opaque*/
for($a=0;$a<8;$a++)
{
switch($a)
{
case 0:
$Testx=$x;$Testy=$y-1;//N
break;
case 1:
$Testx=$x+1;$Testy=$y-1;//NE
break;
case 2:
$Testx=$x+1;$Testy=$y;//E
break;
case 3:
$Testx=$x+1;$Testy=$y+1;//SE
break;
case 4:
$Testx=$x;$Testy=$y+1;//S
break;
case 5:
$Testx=$x-1;$Testy=$y+1;//SO
break;
case 6:
$Testx=$x-1;$Testy=$y;//O
break;
case 7:
$Testx=$x-1;$Testy=$y-1;//NO
break;
}
$Pixels[]='~'.$Testx.','.$Testy;
$Taille=count($Pixels)-1;
$Coord_pixels[$Taille]["x"]=$Testx;$Coord_pixels[$Taille]["y"]=$Testy;
$En_trop[]=array("Cle_Pixels"=>$Taille);
}
return true;
}
}
}
function Next_contour()
{
global $Zones,$x,$y,$Save_x,$Save_y,$Compteur,$Taille,$Dim;
++$Compteur;
static $Prov;
if(!$Prov["x"])
{
$Prov["x"]=$x;$Prov["y"]=$y;
}
/*On regarde si un pixel adjacent au pixel en cours correspond au premier pixel du contour
N = $y-1
NE = $x+1 et $y-1
E = $x+1
SE = $x+1 et $y+1
S = $y+1
SO = $x-1 et $y+1
O = $x-1
NO = $x-1 et $y-1
$Compteur permet de ne pas finaliser la MAP tant que l'on n'est pas positionné sur le quatrième pixel.
Exemple :
1|2
x|3
x|4
Premier pixel détecté : 1 => $Compteur=1
Second pixel détecté : 2 => $Compteur=2 mais le pixel à l'Ouest est celui du démarrage du contour donc, si on ne bride pas, le contour sera finalisé.
Troisième pixel détecté : 3 => $Compteur=3 mais le pixel au Nord-Ouest est celui du démarrage du contour donc, si on ne bride pas, le contour sera finalisé.
Au delà du troisième pixel, si les coordonnées d'un pixel adjacent correspondent à celle du premier pixel de contour cela signifie que l'on a fait le tour du contour.
On détermine quelle est la direction en cours et en fonction de celle-ci, on détermine à partir de quel cardinalité on commence
et quel sens on prend (aiguille d'une montre ou l'inverse)
pour tourner autours du pixel
$x==$Prov["x"] && $y==$Prov["y"] => Initialisation
$x==$Prov["x"] && $y <$Prov["y"] => N
$x >$Prov["x"] && $y <$Prov["y"] => NE
$x >$Prov["x"] && $y==$Prov["y"] => E
$x >$Prov["x"] && $y >$Prov["y"] => SE
$x==$Prov["x"] && $y >$Prov["y"] => S
$x <$Prov["x"] && $y >$Prov["y"] => SO
$x <$Prov["x"] && $y==$Prov["y"] => O
$x <$Prov["x"] && $y <$Prov["y"] => NO
"0|-1" => N
"1|-1" => NE
"1|0" => E
"1|1" => SE
"0|1" => S
"-1|1" => SO
"-1|0" => O
"-1|-1" => NO*/
if(($x==$Prov["x"] && $y==$Prov["y"])
||($x==$Prov["x"] && $y <=$Prov["y"])
||($x >=$Prov["x"] && $y <=$Prov["y"])
||($x >=$Prov["x"] && $y==$Prov["y"])
||($x >=$Prov["x"] && $y >=$Prov["y"]))
{
$Next=array("0|-1","1|-1","1|0","1|1","0|1","-1|1","-1|0","-1|-1" );
}
elseif(($x==$Prov["x"] && $y >$Prov["y"])
||($x <$Prov["x"] && $y >$Prov["y"])
||($x <$Prov["x"] && $y==$Prov["y"])
||($x <$Prov["x"] && $y <$Prov["y"]))
{
$Next=array("0|1","-1|1","-1|0","-1|-1","0|-1","1|-1","1|0","1|1");
}
/* Et pourquoi tout cela me direz-vous ?
C'est très simple : Lorsqu'un prochain pixel est une diagonale, cela laisse les pixels "perpendiculaires" en statut inutilisé.
Or, pour les diagonales allant vers l'Ouest, en partant de Nord, un mauvais pixel sera inclu dans le contour de la MAP et ensuite
il n'y aura plus de prochain pixel pour poursuivre la génération de ce contour, ce qui plantera le script.
Exemple avec Sud-Ouest :
Légende : [X] => Pixels de contour trouvé
[Y] => Pixel considéré comme faisant partie du contour
[Z] => Prochain pixel à intégrer
[P] => Pixel considéré comme plein (ne faisant pas partie du contour
[o] => Pixel transparent
[X][X][X][o][o][o]
[P][P][X][X][X][o]
[P][P][P][Y][X][o]
[P][P][P][X][o][o]
[P][P][Z][o][o][o]
En arrivant au [X] le plus bas, si on suit le sens des aiguilles d'une montre, le pixel [Y] sera intégré dans les coordonnées de la MAP car il sera considéré comme inutilisé.
Ensuite, lorsque l'on fera le tour de ce pixel pour trouver le pixel suivant, on n'en trouvera pas car tous les pixels adjacents à celui-ci seront utilisés.
*/
foreach($Next as $Val)
{
$Test=explode("|", $Val);
if(!Utilise(($x+$Test[0]),($y+$Test[1])))
{
$Prov["x"]=$x;$Prov["y"]=$y;//On garde le pixel trouvé pour voir la provenance lors du traitement du pixel suivant.
//Pour les diagonales, on ajoute dans les pixels utilisés celui qui n'a pas été pris par la diagonale, afin qu'il ne puisse faire dévier le tracé du contour ultérieurement.
switch($Test[0].'|'.$Test[1])
{
case "1|-1": //NE
$Zones[$Taille].='~'.($x+1).','.$y;
break;
case "1|1": //SE
$Zones[$Taille].='~'.$x.','.($y+1);
break;
case "-1|1": //SO
$Zones[$Taille].='~'.($x-1).','.$y;
break;
case "-1|-1": //NO
$Zones[$Taille].='~'.$x.','.($y-1);
break;
}
$x+=$Test[0];$y+=$Test[1];
$Zones[$Taille].='~'.$x.','.$y;
return 1;
}
}
if((($x==$Save_x && $y-1==$Save_y) ||//N
($x+1==$Save_x && $y-1==$Save_y) ||//NE
($x+1==$Save_x && $y==$Save_y) || //E
($x+1==$Save_x && $y+1==$Save_y) ||//SE
($x==$Save_x && $y+1==$Save_y) || //S
($x-1==$Save_x && $y+1==$Save_y) ||//SO
($x-1==$Save_x && $y==$Save_y) ||//O
($x-1==$Save_x) && $y-1==$Save_y)&& $Compteur>3)//NO
{
$Zones[$Taille]='<AREA SHAPE="poly" COORDS="'.$Zones[$Taille].'~'.$x.','.$y.'" HREF="'.$_POST["Lien"].'" title="'.$_POST["Infobulle"].'">';//On ferme la balise Map
$Save_x=NULL;$Save_y=NULL;$Compteur=0;
unset($Prov);
return 2;
}
//Aucun pixel de prochain contour => Le contours est ouvert => on le ferme pour ne pas planter le script
$Zones[$Taille]='<AREA SHAPE="poly" COORDS="'.$Zones[$Taille].'" HREF="'.$_POST["Lien"].'" title="'.$_POST["Infobulle"].'">';//On ferme la balise Map
$Save_x=NULL;$Save_y=NULL;$Compteur=0;
unset($Prov);
return 0;
}
function Trouve_bornes($Map)
{
$Bornes=array("Min_x"=>0,"Max_x"=>0,"Min_y"=>0,"Max_y"=>0);
preg_match_all("&~([[:digit:]]+),([[:digit:]]+)~&",$Map,$Result);
/*$Result[1]=Abscisses, $Result[2]=Ordonnées
On détermine les bornes minimales et maximales des abscisses et des ordonnées*/
array_multisort($Result[1],SORT_ASC,SORT_NUMERIC);
array_multisort($Result[2],SORT_ASC,SORT_NUMERIC);
$Bornes["Min_x"]=$Result[1][0];$Bornes["Max_x"]=$Result[1][count($Result[1])-1];
$Bornes["Min_y"]=$Result[2][0];$Bornes["Max_y"]=$Result[2][count($Result[2])-1];
return $Bornes;
}
function Suppr_superflus($Map)
{
global $Zones;
/* Lorsque l'on a épaissi les contours, on a créé deux zones de map, l'une extérieure au contour et l'autre intérieure.
Exemple :
111111111 Légende :
1XXXXXXX1 X : Contour original supprimé car considéré comme opaque par l'épaississement.
1X22222X1 1 : Contour extérieur.
1X2 2X1 2 : Contour intérieur.
1X2 2X1
1X22222X1
1XXXXXXX1
111111111
Automatiquement, la zone extérieure est prise en premier pour générer le contour car ses pixels sont détectés avant ceux de la zone intérieure.
On suprimme donc la zone intérieure.
- On classe les coordonnées par l'abscisse et l'ordonnée
- On récupère l'abscisse et l'ordonnée minimale et maximale de la zone
- Pour chaque zone antérieurement générée on regarde si l'absisse et l'ordonnée minimales sont à l'intérieure de la zone
- Si c'est le cas => Zone interne => on la supprimme.
- Pour chaque zone précédemment générée :
- On récupère les bornes
- On regarde si la surface délimitée par les bornes de $Map sont comprises dans la zone délimitée par les bornes de la zone
- Si oui :
- On supprime la zone en cours de génération.
- On supprime de $Pixels tous les pixels qui ont servi à générer la zone
*/
$Bornes_Map=Trouve_bornes($Map);
foreach($Zones as $Val)
{
if($Map!=$Val)
{
$Bornes_zone=Trouve_bornes($Val);
if($Bornes_Map["Min_x"]>=$Bornes_zone["Min_x"] && $Bornes_Map["Max_x"]<=$Bornes_zone["Max_x"]
&& $Bornes_Map["Min_y"]>=$Bornes_zone["Min_y"] && $Bornes_Map["Max_y"]<=$Bornes_zone["Max_y"])
{
$Zones[array_search($Map,$Zones)].="A ne pas prendre";
return;
}
}
}
}
if(!$_FILES['Fichier']['tmp_name'])
{
echo '
<script language="javascript" type="text/javascript">
function Valid()
{
with(document.Form1)
{
if(Fichier.value.length==0)
{
alert("Sans fichier à charger je n\'ai que mes yeux pour pleurer");
Fichier.focus();
}
else if(Tolerance.value<0||Tolerance.value>100 || isNaN(Tolerance.value))
{
alert("La tolérance doit être un nombre compris entre 0 et 100");
Tolerance.focus();
}
else if(Map.value.length==0)
{
alert("Sans nom de Map, pas de prise de cap !");
Map.focus();
}
else if(Lien.value.length==0)
{
alert("Sans lien je ne peux rien.");
Lien.focus();
}
else
{
submit();
}
}
}
</script>
<form Enctype="multipart/form-data" Name="Form1" Method="post" action="'.htmlentities($_SERVER['PHP_SELF']).'">
<input type="hidden" name="MAX_FILE_SIZE" value="'.Taille_fichier_max.'">
<h1>Générateur de Map pour images cliquables</h1>
<p>Image à mapper sachant que le premier pixel en haut à gauche
sera celui définissant <b>"la transparence"</b>.<br>La taille du fichier
ne doit pas dépasser <b>'.number_format(Taille_fichier_max/1024000,2,',',' ').' Mo</b>.
<br>Ses dimensions maximales sont de <b>'.DimX_max.'</b> pixels de large et de <b>'.DimY_max.'</b> pixels de haut.<br>Les formats acceptés sont <b>GIF et PNG</b></p>
<p>Image à traiter : <input type="file" name="Fichier"></p>
<p>Le premier pixel en haut à gauche servira à définir
la "transparence". Indiquez la tolérance <input type="text" name="Tolerance" value="0" maxlength="3" size="3" style="text-align:right;">
%</p>
<p>Indiquez le nom de la MAP <input type="text" name="Map" maxlength="100" size="50" value="MAP_Lekod"></p>
<p> Indiquez le lien par défaut <input type="text" name="Lien" maxlength="100" size="50" value="http://Mon_site.com"></p>
<p>Indiquez l\'infobulle par défaut <input type="text" name="Infobulle" maxlength="100" size="50" value="Mon infobulle"></p>
<p><input type="button" name="B_Valid" value="Générer la Map" onclick="Valid()"></p>
<H2 align="center">Ce processus étant <u>assez long</u>,<br> il vous faudra faire montre de <u>patience</u>.</H2>
</div>
';
}
else
{
//On vérifie que les dimensions et la taille sont dans les limites
if($_FILES['Fichier']['size']>Taille_fichier_max)
{
unlink($_FILES['Fichier']['tmp_name']);
die('La taile du fichier excède les <b>'.number_format(Taille_fichier_max/1024000,2,',',' ').'</b> Mo<br>
Abandon de la génération');
}
switch(exif_imagetype($_FILES['Fichier']['tmp_name']))
{
case IMAGETYPE_GIF:
$Image=imagecreatefromgif($_FILES['Fichier']['tmp_name']);$Ext='.gif';
break;
case IMAGETYPE_PNG:
$Image=imagecreatefrompng($_FILES['Fichier']['tmp_name']);$Ext='.png';
break;
default:
echo '<script language="javascript">alert("Le format du fichier selectionne n\'est pas reconnu !");
window.location=\''.$_SERVER['PHP_SELF'].'\';
</script>';
unlink($_FILES['Fichier']['tmp_name']);die();
break;
}
$Dim=array("x"=>imagesx($Image),"y"=>imagesy($Image));
if($Dim["x"]>DimX_max||$Dim["y"]>DimY_max)
{
imagedestroy($Image);
die('Les dimensions de l\'image sont supérieures à <b>'.DimX_max.'</b> pixels de large et à <b>'.DimY_max.'</b> pixels de haut.');
}
// La couleur de transparence est le pixel en (0,0)
$Transparence=imagecolorsforindex($Image, imagecolorat($Image,0,0));
//On détermine les couleurs minimales et maximales de tolérance
$Tol_Min["Rouge"]=intval($Transparence["red"]-($Transparence["red"]*$_POST["Tolerance"]/100));
$Tol_Max["Rouge"]=intval($Transparence["red"]+($Transparence["red"]*$_POST["Tolerance"]/100));
$Tol_Min["Vert"]=intval($Transparence["green"]-($Transparence["green"]*$_POST["Tolerance"]/100));
$Tol_Max["Vert"]=intval($Transparence["green"]+($Transparence["green"]*$_POST["Tolerance"]/100));
$Tol_Min["Bleu"]=intval($Transparence["blue"]-($Transparence["blue"]*$_POST["Tolerance"]/100));
$Tol_Max["Bleu"]=intval($Transparence["blue"]+($Transparence["blue"]*$_POST["Tolerance"]/100));
/*On parcourt le tableau
- Si le pixel est compris dans la plage de tolérance, on n'insère pas ses coordonnées dans $Zones
*/
for($a=0;$a<=$Dim["y"];$a++)
{
for($b=0;$b<=$Dim["x"];$b++)
{
Est_contour($b,$a);
}
}
imagedestroy($Image);
if($Pixels[0]=='')die('Il n\'y a rien à mapper.<br>Réduisez la tolérance ou bien changez les zones transparentes');
/*En épaississant les contours précédemment, on a virtualisé des pixels "opaques"
On les supprime de $Pixels*/
foreach($Pixels as $Cle => $Val)
{
$Poid=0;
$x=$Coord_pixels[$Cle]["x"];$y=$Coord_pixels[$Cle]["y"];
if(in_array('~'.$x.','.($y-1),$Pixels)) ++$Poid;//N
if(in_array('~'.($x+1).','.($y-1),$Pixels)) ++$Poid;//NE
if(in_array('~'.($x+1).','.$y,$Pixels)) ++$Poid;//E
if(in_array('~'.($x+1).','.($y+1),$Pixels)) ++$Poid;//SE
if(in_array('~'.$x.','.($y+1),$Pixels)) ++$Poid;//S
if(in_array('~'.($x-1).','.($y+1),$Pixels)) ++$Poid;//SO
if(in_array('~'.($x-1).','.$y,$Pixels)) ++$Poid;//O
if(in_array('~'.($x-1).','.($y-1),$Pixels)) ++$Poid;//NO
if($Poid==8) $En_trop[$Cle]["Opaque"]=true;
}
//Maintenant qu'on a marqué tous les pixels "opaques", on les enlève de $Pixels
foreach($En_trop as $Cle=>$Val)
{
if($Val["Opaque"])
{
unset($Pixels[$Val["Cle_Pixels"]]);
unset($Coord_pixels[$Val["Cle_Pixels"]]);
unset($En_trop[$Cle]);
}
}
/*
Maintenant qu'on a les contours, pour chaque point, on détermine le suivant
à la manière du Petit Poucet, ce qui permet de générer les Maps
*/
foreach($Pixels as $Cle=>$Valeur)
{
//On extrait les coordonnées du code HTML
$x=$Coord_pixels[$Cle]["x"];$y=$Coord_pixels[$Cle]["y"];
if(!Utilise($x,$y))
{
$Save_x=$x;$Save_y=$y;
//On instancie une nouvelle zone
$Zones[].='~'.$x.','.$y;
$Taille=count($Zones)-1;
while(!preg_match('~^<AREA SHAPE="poly"~',$Zones[$Taille]))
{
while(1==1)
{
$Result=Next_contour();
if($Result==0 || $Result==2)
{/*$Result==0)=>Aucun pixel de prochain contour => On traite le pixel suivant en finalisant la Map
$Result==2)=>On a finit de faire le tour de la forme => Finalisation du HTML de la Map*/
Suppr_superflus($Zones[$Taille]);//On nettoie des zones intérieures ou batardes
break 2;
}
}
}
}
}
$Taille=count($Zones);
for($a=0;$a<$Taille;$a++)
{
if(!strpos($Zones[$a],"A ne pas prendre"))
{
/*Malgré toute ma bonne volonté pour ne pas mettre de zones superflues et de supprimmer tous les pixels inutiles à l'élaboration des maps,
il arrive qu'il soit possible de se retrouver avec des zones d'un pixel. On ne les prend pas en compte.
*/
$Nbre_coord=explode(",",$Zones[$a]);
if(count($Nbre_coord)==2)//Cela veut dire qu'il n'y a qu'un pixels de contour
{
unset($Zones[$a]);
}
else
{
$Map.=str_replace(array("\"~","~",-1),array("\"",",",0),$Zones[$a]);
}
}//-1 est présent lorsqu'en épaississant les contours on déborde vers les bords haut et/ou gauche de l'image.
else//Par soucis d'esthétisme, on remet à la place un 0. On pourrait faire pareil pour les bords supérieurs lorsque les contours touchent à droite et/ou en bas
{
unset($Zones[$a]);
}
}
//On déplace le fichier téléchargé pour affichage de l'image
$Fichier=Chemin_copie_img.'Map_'.time().$Ext;
copy($_FILES['Fichier']['tmp_name'],$Fichier);
echo '
<script language="javascript" type="text/javascript">
function Changt()
{
var Areas=document.getElementsByTagName("AREA");
var Textes=document.getElementsByTagName("input");
var Map=document.getElementsByTagName("MAP");
var Compteur=0
Map=Map[0];
Map.innerHTML=Map.innerHTML.replace(/title/gi,"alt");
for(a=0;a<Textes.length;a++)
{
if(Textes[a].name.substring(0,4)=="Lien")
{
Areas[Compteur].href=Textes[a].value;
}
else if(Textes[a].name.substring(0,9)=="Infobulle")
{
Areas[Compteur].alt=Textes[a].value;
Compteur++;
}
}
//Firefox ne reconnait pas les alt. On change cette propriété en title
Map.innerHTML=Map.innerHTML.replace(/alt/gi,"title");
document.Form1.Result.value="<MAP NAME=\"'.$_POST["Map"].'\">"+Map.innerHTML+"</MAP>";
}
</script>
<MAP NAME="'.$_POST["Map"].'">'.
$Map.'</MAP>
<P name="P_Image" align="center">
<img src="'.str_replace(Chemin_copie_img,URL_img,$Fichier).'" name="Image" usemap="#'.$_POST["Map"].'">
</p>
<p><textarea name="Result" rows="10" cols="100" wrap="soft"><MAP NAME="'.$_POST["Map"].'">'.
$Map.'</MAP></textarea></p>
<p>Pour changer le lien d\'une zone de l\'image modifiez la valeur dans la zone <b>"Changer le lien"</b>.
<p>Pour changer l\'infobulle d\'une zone de l\'image modifiez la valeur dans la zone <b>"Changer l\'infobulle"</b></p>
<table width="100%">
<tr>
<td>
<p align="center">Changer le lien</p>
</td>
<td >
<p align="center">Changer l\'infobulle</p>
</td>
</tr>';
//On met de quoi changer la valeur des liens et des infobulles
foreach($Zones as $Cle=>$Val)
{
echo '<tr>
<td align="center">
<input type="text" name="Lien'.$Cle.'" value="'.$_POST["Lien"].'" size="50%">
</td>
<td align="center">
<input type="text" name="Infobulle'.$Cle.'" value="'.$_POST["Infobulle"].'" size="50%">
</td>
</tr>';
}
echo '</table>
<p align="center">
<input type="button" name="B_Valid" value="Valider les changements" onclick="Changt()">
<input type="button" name="B_Reinit" value="Play it again Sam" onclick="javascript:window.location=\''.htmlentities($_SERVER['PHP_SELF']).'\'"></p>
';
}
?>
</form>
</body>
</html>
Conclusion
Une petite astuce :
Pour avoir une MAP évidée, séparez la forme d'une bande de minimum trois pixels d'épaisseur.
Historique
- 06 février 2009 15:17:19 :
- Oubli du lien vers la démo
- 10 février 2009 20:41:45 :
- Résolution d'une faille de sécurité sur $_SERVER['PHP_SELF'] et ajout d'une astuce.
- 10 février 2009 20:44:20 :
- Résolution d'une faille de sécurité sur $_SERVER['PHP_SELF'] et ajout d'une astuce
Sources du même auteur
Sources de la même categorie
Commentaires et avis
Discussions en rapport avec ce code source dans le forum
balise map html [ par djbabou ]
Bonjour a tous, J'aimerais vous poser une petite question concernant le mapping en php. J'ai une image sur une de mes pages sur laquelle j'applique
CSS MAP et AREA [ par gabs77 ]
Bonjour, Je voudrais savoir si on peut combiner du CSS avec les balises AREA ???En fait, j'ai une image PNG avec un fond transparent que j'affiche en
image une sur l'autre php/ html [ par djbabou ]
Bonjour a tous. J'ai un petit problème de manipulation d'image. En fat, j'aimerais superposer deux images l'une sur l'autre. J'y suis arrivé grace à
générateur de page html [ par novaconcept ]
bonjour, je voulais savoir si il était possible d'avoir une page .php. et De cette dernière générer(créer) une page .html avec le contenu qu'on aura
Mail HTML avec image en PJ [ par Shenron42000 ]
Bonjour,Voila, ca fait plusieurs jours que je planche dessus et pas moyen d'y arriver.Le script à pour but de récupérer le code HTML qui provient d'un
de l'HTML avec mail() ? [ par Drazounet ]
Salut à tous J'essaie actuellement de faire un petit programme de mailing en PHP à l'aide de la fonction mail().Mais, lorsque j'essaie d'intégrer une
Decaler une image + map [ par spiritofdivx ]
J ai un probleme dans la mise en page d une image (.gif) , je voudrais savoir ce qu il faut que j ajoute a ma ligne pour qu elle soit plus a droite da
HELP-ME!! Compteur d'affichages [ par eax ]
bonjour,j'ai encore un problème ;)je souhaite connaitre le nombre d'affichages d'une image :je mets donc ceci dans ma page html:<img scr="image.php
question HTML [ par benjiman2 ]
Bonjour a tous !Voila, mon probleme est vraiment simple !Mais pour moi , je ne vois pas de solution a mon niveau !VOila, j'ai une image sur mon ordina
agrandir une image [ par billy67000 ]
function ImageMax(chemin) { html='<html> <head> <title>Titre</title> </head> <body bgcolor=white><IMG src="'+ch
|
Derniers Blogs
[WP7] DYNAMICALLY CHANGE STARTUP PAGE[WP7] DYNAMICALLY CHANGE STARTUP PAGE par KooKiz
Let's say that you want to allow the user to customize the startup page of your application. You can easily change the startup page by editing the 'NavigationPage' attribute in the manifest file. But the manifest cannot be modified once the applicatio...
Cliquez pour lire la suite de l'article par KooKiz SESSION SILVERLIGHT 5 3D : SLIDES ET DEMOSSESSION SILVERLIGHT 5 3D : SLIDES ET DEMOS par Groc
Durant les techdays, j'ai eu le plaisir d'animer une session sur Silverlight 5 et la 3D avec Simon Ferquel. Comme promis, voici nos slides et mes démos (celles avec le viper BSG) ici et là. Pour mémoire, les démos utilisent toutes le viper BSG...
Cliquez pour lire la suite de l'article par Groc [TECHDAYS 2012] SESSION WEBMATRIX 2 : LE COUTEAU SUISSE GRATUIT POUR VOS DéVELOPPEMENTS WEB - SLIDES[TECHDAYS 2012] SESSION WEBMATRIX 2 : LE COUTEAU SUISSE GRATUIT POUR VOS DéVELOPPEMENTS WEB - SLIDES par gpommier
Suite à la session que j'ai présenté sur WebMatrix 2, vous pouvez trouver les slides ici, ainsi que les démos en packages nuget : démos1 et démos2 J'en profite pour remercier chaleureusement tous ceux qui sont venus très nombreux à cette sess...
Cliquez pour lire la suite de l'article par gpommier [SHAREPOINT] LES SESSIONS TECHDAYS 2012.[SHAREPOINT] LES SESSIONS TECHDAYS 2012. par Patrick Guimonet
Voici donc pour ceux qui n'ont pas pu venir, ou ceux qui n'ont pas pu toutes les suivre la liste des sessions SharePoint aux TechDays 2012, que je mettrais à jour dès que les liens des vidéo seront disponibles. Ou ici : http...
Cliquez pour lire la suite de l'article par Patrick Guimonet TECHDAYS PARIS 2012 : SESSION PLEINIèRE JOUR 3TECHDAYS PARIS 2012 : SESSION PLEINIèRE JOUR 3 par ROMELARD Fabrice
Speaker: Bernard Ourghanlian Cette session est comme chaque jour transmise en live par BrainSonic, et j'ai donc suivi cette troisième pleinière par ce moyen sur mon iPad . Elle est dédiée comme chaque année à la mise en perspective de l'é...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice
Forum
RE : SONDAGE..RE : SONDAGE.. par phpAnonyme
Cliquez pour lire la suite par phpAnonyme RE : SONDAGE..RE : SONDAGE.. par TychoBrahe
Cliquez pour lire la suite par TychoBrahe
Logiciels
Tribler (2012)TRIBLER (2012)Tribler est un client pair à pair (P2P/Peer-to-Peer) open source avec la capacité de regarder des... Cliquez pour télécharger Tribler OneSwarm (2012)ONESWARM (2012)Le peer-to-peer qui protège votre vie privée, c'est OneSwarm.
Ce logiciel de peer-to-peer crypté... Cliquez pour télécharger OneSwarm PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO (V8.4)PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO (V8.4)PONAMEDIA TV DEVIENS HELLLOOO FLASH
LA TV SUR VOTRE ORDINATEUR.
Toute une plateforme Multi... Cliquez pour télécharger PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO Academy System (17.2.1.0)ACADEMY SYSTEM (17.2.1.0)Logiciel de gestion des établissements.
- élèves/étudiants (inscription, dossier, absence...)
-... Cliquez pour télécharger Academy System Easy-Planning (1.0.0.1)EASY-PLANNING (1.0.0.1)Basé sur les mêmes principes que MyPlanning, Easy-Planning permet de créer des plannings sous la ... Cliquez pour télécharger Easy-Planning
|