Creating and Using Dynamic Legends / Layer Controls Using PHPMapScript

This page created by Chip Hankley.


09/12/2002 - Legend script writes to a static HTML file, which is then included(). The script compares the file date for the mapfile with the static HTML file. If the static HTML file does not exist, OR, the mapfile is newer, a new legend is created. This saves on server processing... there's no reason to generate a new legend w/ each request... it should only be regenerated when needed.

09/12/2002 - Now assesses whether or not layers are outside of MINSCALE or MAXSCALE bounds, and responds by disabling the checkbox and making the layer description gray. This document describes how to create dynamic legends / layer controls using PHP MapScript.


Controlling layers (i.e. telling MapServer which layers to draw) can be one of the trickier part to designing and implementing a MapScript application. Encapsulating this control in a meaningful legend is often even more difficult (at least it was for me!).

The snippet of PHP code at the bottom shows how you can use MapScript to dynamically create a legend with controls for turning layers on and off.

There are several caveats to following this:

1) The name that appears in the legend is drawn from the Metadata tag (see the sample layer definition below). 2) Everything must have a group name. 3) Layers that have multiple classes need to have names for each class 4) Layer controls (i.e. the checkboxes) are named with the with a 'lyr' prefix... i.e.

input type='checkbox' name='lyrRoads' value='roads'>

The script depends on this... so you can't have any other form controls that are prefixed 'lyr.'

5) You now need to specify some of paths at the beginning of the script.

Note that this is not meant to be an exhaustive script... it's really just set up to show how you would do this. It's pretty simple, and could easily be modified for changing needs. I took the basic concept for this out of the example script (test_draw_legend_icon.phtml) that comes with the MapServer source (in the PHP directory). Along those lines, I put the script together so that, after entering a valid mapfile name, you can test it out by submitting the form and watching how layer status state is maintained.

Also note that I used CSS to make the 'legend' look nice... but you could just as easily use tables or other HTML tools to control indents, etc.

Sample Layer Definition

  NAME "roads"
  GROUP "Base Data"
    LYRNAME "Roads"
  DATA ...
    NAME 'Highways'
    COLOR 255 0 0
    SYMBOL 'solid_line'
    SIZE 3
    OVERLAYSYMBOL 'solid_line'
    NAME 'Major Roads'
    COLOR 0 0 0
    NAME 'Local Roads'
    COLOR 204 204 204

Dynamic Legend Script

$path_to_mapfile = "C:\\Inetpub\\wwwroot\\Lockport\\";
$path_to_legend = "C:\\Inetpub\\wwwroot\\Lockport\\legend\\";
$url_to_legend = "legend/";
$map = ms_newMapObj($path_to_mapfile);
$img = $map->draw();
$url = $img->saveWebImage(MS_PNG, $map->transparent, $map->interlace, 50);

//Get the last modified date from the mapfile and the legend
$mapfile_modtime = filemtime($path_to_mapfile);
//use '@' to suppress error when there is no 'legend.php' file.
// In this case the default action will be to create one.
$legend_modtime = @filemtime($path_to_legend . "legend.php");

//get all layers
$allLayers = $map->getAllLayerNames();
//get all of the group names
$groupNames = $map->getAllGroupNames();

//Set the status of various layers based
// on what is checked. This only needs
// to be done if this is not the first
// load. On the first load, we will simply
// use the STATUS values as set in the map
// file
if (!empty($_POST)) {
  //Turn all layers off
  foreach($allLayers as $a) {
    SetStatus($a, 0);
  //Turn the layers ON that were checked
  // in the previous submit. Since we named
  // the checkboxes that control layer display
  // with a prefix of 'lyr', we can basically
  // loop through the submitted form variables
  // and key in on those that relate to layer
  // control.
  $POST_KEYS = array_keys($_POST);
  foreach($POST_KEYS as $a) {
    if (substr($a, 0, 3) == "lyr")
      SetStatus($_POST[$a], 1);
//Check the scale status for each layer. Dump the
// results into an array to access when building the legend
// NOTE that in an application, you would need to call this
// AFTER you draw the map, or scale will not be properly
// set.
foreach($allLayers as $a) {
  $LayerScale[$a] = CheckScale($a);

//Next line is for demo purposes... encapsulating this in a form
// just allows us to review the results. and see that it is
// working. Normally, you would probably include all of your
// map related info in one form.
echo "<form method=POST action=dyn_legend.php>";

//Only generate the legend IF the mapfile is older than the
// legend file.
if ($mapfile_modtime > $legend_modtime) {
// Generate a legend dynamically, and write it to a static HTML file
//delete all of the existing files in the LEGEND directory
system("del " . $path_to_legend . " /q");
$img_count = 0;

//create a file to write the HTML to
$fp = fopen($path_to_legend . "legend.php", "w+");

fwrite($fp, "<span style=\"font-family: arial; font-size: 11pt; font-weight: bold; padding-left: 5px\">Legend</span>\n<HR>\n");
fwrite($fp, "<div style=\"padding-left:10px\">\n");

foreach($groupNames as $a) {
  fwrite($fp, "<span style=\"font-family: arial; font-weight: bold; font-size: 10pt\">" .$a . "</span><BR>\n");
  foreach($map->getLayersIndexByGroup($a) as $b) {
    $layer = $map->getLayer($b);
    $title = $layer->getMetadata("LYRNAME");
    $name = $allLayers[$b];
    $checked = GetStatus($map, $name);

    if ($layer->type==3) {
      fwrite($fp, "<span style=\"font-family: arial; font-size: 9pt; padding-left: 10px;");
      fwrite($fp, "<?echo \$LayerScale[$name][fontcolor]?>\"><input type=\"checkbox\" name=\"lyr$name\" value=\"$name\" <? echo \$LayerScale[$name][checked]; echo \$LayerScale[$name][disabled]?>>$title</span><BR>\n");
    //Layers with one class
    elseif ($layer->numclasses <= 1) {
      $myClass = $layer->GetClass(0);
      $img = $myClass->createLegendIcon($map->keysizex, $map->keysizey);
      $img_name = $name . ++$img_count . ".png";
      $img->saveImage($path_to_legend . $img_name, MS_PNG, 0, 0, 0);
      $url = $url_to_legend . $img_name;
      fwrite($fp, "<span style=\"font-family: arial; font-size: 9pt; padding-left: 10px; <?echo \$LayerScale[$name][fontcolor]?>\"><input type=\"checkbox\" name=\"lyr$name\" value=\"$name\" <? echo \$LayerScale[$name][checked]; echo \$LayerScale[$name][disabled]?>>");
      fwrite($fp, "<IMG SRC=\"" . $url . "\">  " . $title . "</span><BR>\n");
    //Layers with multiple classes
    else {
      fwrite($fp, "<span style=\"font-family: arial; font-size: 9pt; padding-left: 10px; <?echo \$LayerScale[$name][fontcolor]?>\"><input type=\"checkbox\" name=\"lyr$name\" value=\"$name\" <? echo \$LayerScale[$name][checked]; echo \$LayerScale[$name][disabled]?>>");
      fwrite($fp, "  $title");
      fwrite($fp, "<div style=\"font-family: arial; font-size: 8pt; padding-left: 20px;<?echo \$LayerScale[$name][fontcolor]?>\">");
      for ($j=0; $j<$layer->numclasses; $j++) {
        $myClass = $layer->GetClass($j);
        $img = $myClass->createLegendIcon($map->keysizex, $map->keysizey);
        $img_name = $name . ++$img_count . ".png";
        $img->saveImage($path_to_legend . $img_name, MS_PNG, 0, 0, 0);
        $url = $url_to_legend . $img_name;
        fwrite($fp, "<IMG SRC=\"" . $url . "\">  " . $myClass->name . "<BR>");
      fwrite($fp, "</div>");
fwrite($fp, "</div>\n");
// End of dynamic legend creation, and write it to a static HTML file

include($path_to_legend . "legend.php");

echo "<input name=\"SubmitButton\" type=\"submit\" value=\"GO!\">";
echo "</form>";
function GetStatus($map, $LayerName)
  $Layer = $map->getlayerbyname($LayerName);
  if ($Layer->status)
    $Status = " checked";
  else $Status = "";
  return ($Status);
function SetStatus($lyrname, $value) {
  $map = $GLOBALS["map"];
  $layer->set("status", $value);
function CheckScale($lyrname) {
  $map = $GLOBALS["map"];
  //echo "<P>MapScale: $map->scale<BR>MaxScale: $layer->maxscale<BR>MinScale: $layer->minscale</p>";
  if ($layer->maxscale == -1 AND $layer->minscale == -1) {
    $scale_params["fontcolor"] = " color: black; ";
    $scale_params["disabled"] = "";
  elseif ($map->scale > $layer->maxscale AND $layer->maxscale <> -1) {
    $scale_params["fontcolor"] = " color: gray; ";
    $scale_params["disabled"] = " disabled";
  elseif ($map->scale < $layer->minscale AND $layer->maxscale <> -1) {
    $scale_params["fontcolor"] = " color: gray; ";
    $scale_params["disabled"] = " disabled";
  else {
    $scale_params["fontcolor"] = " color: black; ";
    $scale_params["disabled"] = "";
  $scale_params["checked"] = GetStatus($map, $lyrname);
  return $scale_params;


back to PHPMapScript

Last modified 10 years ago Last modified on Jan 30, 2009 10:46:48 AM