view tools/makegmaps.php @ 237:d858383547c7 gmap2

Comment cosmetics.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 17 Mar 2014 01:21:24 +0200
parents d0789eab0c47
children 885ef4bdb0de
line wrap: on
line source

#!/usr/bin/php
<?php

// Paths and files
$binConvert = "convert";
$binMercurial = "hg";
$binMake = "make";

$pathMapUtils = "maputils/";
$pathImageCache = "cache/";
$pathMarkerData = "../";
$pathTileData = "../tiles/";

$pathRawMaps = $pathMapUtils."maps/";

$rawSuffix = ".new";
$rawAltSuffix = ".map";

$fontFile = "./lucon.ttf";
$tileDim = 256;

$minZoom = 1;
$maxZoom = 10;

// Font sizes
$fontSize = array();
$fontSize[ 8] = 7;
$fontSize[16] = 13;
$fontSize[32] = 26;


/* Internal data and settings
 */
$locPath = $pathMapUtils."maps/";
$worldJS = $pathMarkerData."world.js";
$tradelaneOut = $pathMarkerData."tradelane.json";
$tradelaneOverlay = $pathMarkerData."trlines.json";
$binMkLoc = $pathMapUtils."mkloc";

$modes = array(
  "xml"     => array("batclient.xml"     , 0, 0),
  "json"    => array("markers.json"       , 0, 0),
);

$mapPalette = array();
$mapPalette["!"]  = array(204,255,255);
$mapPalette["%"]  = array(  0,170,170);
$mapPalette["-"]  = array( 51, 51, 51);
$mapPalette["|"]  = array( 51, 51, 51);
$mapPalette["/"]  = array( 51, 51, 51);
$mapPalette["+"]  = array( 51, 51, 51);
$mapPalette["\\"] = array( 51, 51, 51);
$mapPalette["="]  = array( 72, 67, 57);
$mapPalette["@"]  = array(255,107,  0);
$mapPalette["F"]  = array(  0,136,  0);
$mapPalette["L"]  = array(255, 80,  0);
$mapPalette["S"]  = array( 68,204,204);
$mapPalette["^"]  = array(113,130,146);
$mapPalette["c"]  = array( 95, 86, 85);
$mapPalette["f"]  = array(  0,182,  0);
$mapPalette["i"]  = array(255,255,255);
$mapPalette["l"]  = array(100,100,255);
$mapPalette["s"]  = array(157,168, 10);
$mapPalette["v"]  = array( 34,221, 34);
$mapPalette["x"]  = array(138,131, 96);
$mapPalette["z"]  = array(177,164,133);
$mapPalette["#"]  = array( 79, 54, 69);
$mapPalette["."]  = array( 85,146,  0);
$mapPalette[","]  = array(140, 87, 56);
$mapPalette["?"]  = array(255,255,  0);
$mapPalette["C"]  = array(153,153,  0);
$mapPalette["H"]  = array(102, 63,  0);
$mapPalette["R"]  = array( 51,102,255);
$mapPalette["V"]  = array(255, 51,  0);
$mapPalette["b"]  = array(207,196,165);
$mapPalette["d"]  = array(238,187, 34);
$mapPalette["h"]  = array(153,102,  0);
$mapPalette["j"]  = array( 19,150, 54);
$mapPalette["r"]  = array(102,153,255);
$mapPalette["t"]  = array( 97,195,162);
$mapPalette["w"]  = array(119,170,255);
$mapPalette["y"]  = array(167,204, 20);
$mapPalette["~"]  = array( 51, 51,170);
$mapPalette["1"]  = array(255,102, 16);
$mapPalette[3]    = array(0,0, 0);


//
// Helper functions
//
function stMakeDir($path)
{
  if (file_exists($path))
    return TRUE;
  else
    return mkdir($path, 0755, TRUE);
}


function stOutputToFile($sfilename, $sdata)
{
  if (file_put_contents($sfilename, $sdata, LOCK_EX) === FALSE)
    die("Error writing to '".$sfilename."'.\n");
}


/*
 * Create htaccess files
 */
function makeHtAccessFile($surlprefix, $sprefix, $spath)
{
  $sfile = $sprefix.$spath.".htaccess";
  if (!file_exists($sfile) && file_exists($sprefix.$spath."sea.png"))
  {
    if (!isset($surlprefix))
    {
      $sdone = FALSE;
      while (!$sdone)
      {
        echo "Enter URL prefix for tiles:\n";
        $surlprefix = trim(fgets(STDIN));
        if (substr($surlprefix, 0, 7) != "http://" &&
            substr($surlprefix, 0, 8) != "https://")
        {
          echo "Prefix must start with http:// or https://\n";
        }
        else
        if (substr($surlprefix, -1) != "/")
        {
          echo "Prefix must end with /\n";
        }
        else
        if (preg_match("/^https?:\/\/[a-zA-Z0-9]+[a-zA-Z0-9\/\.\:\-]*?\//", $surlprefix) === FALSE)
        {
          echo "Malformed URL (or atleast this silly regexp does not want it.\n";
        }
        else
        {
          echo "The URL prefix to be used is \"".$surlprefix."\", e.g.\n".
            "htaccess to be created would be: \"".$surlprefix."tiles/".$spath."sea.png\"\n".
            "Is this correct? [y/N]? ";
          
          $sprompt = strtolower(trim(fgets(STDIN)));
          if ($sprompt == "y")
            $sdone = TRUE;
          
          echo "\n";
        }
      }
    }
    
    
    echo "Creating ".$sfile."\n";
    outputToFile($sfile, "ErrorDocument 404 ".$surlprefix."tiles/".$spath."sea.png\n");
  }
}

makeHtAccessFile(&$urlTilePrefix, $pathTileData, "");
for ($i = $minZoom; $i <= $maxZoom; $i++)
  makeHtAccessFile(&$urlTilePrefix, $pathTileData, $i."/");


if (!file_exists($pathMapUtils))
//
// Build maputils and fetch latest map data
//
{
  // If maputils does not exist, clone the repository
  $tmp = $binMercurial." clone http://pupunen.net/hg/maputils/ ".escapeshellarg($pathMapUtils);
  echo "* $tmp\n";
  passthru($tmp) == 0 or die("Error executing: ".$tmp."\n");

  // Clone th-libs
  $tmp = $binMercurial." clone http://tnsp.org/hg/th-libs/ ".escapeshellarg($pathMapUtils."th-libs/");
  echo "* $tmp\n";
  passthru($tmp) == 0 or die("Error executing: ".$tmp."\n");
}
else
{
  $tmp = "cd ".escapeshellarg($pathMapUtils)." && ".$binMercurial." pull && ".$binMercurial." update";
  echo "* $tmp\n";
  passthru($tmp) == 0 or die("Error executing: ".$tmp."\n");

  $tmp = "cd ".escapeshellarg($pathMapUtils."th-libs/")." && ".$binMercurial." pull && ".$binMercurial." update";
  echo "* $tmp\n";
  passthru($tmp) == 0 or die("Error executing: ".$tmp."\n");
}

$tmp = "cd ".escapeshellarg($pathMapUtils)." && ".$binMake." ".escapeshellarg(basename($binMkLoc));
echo "* $tmp\n";
passthru($tmp) == 0 or die("Error executing: ".$tmp."\n");


if (!file_exists($binMkLoc))
  die($binMkLoc." not found. Maputils package not built, or some other error occured.\n");

$tmp = "cd ".escapeshellarg($pathMapUtils)." && ".$binMake;
passthru($tmp) == 0 or die("Error executing: ".$tmp."\n");

$tmp = "cd ".escapeshellarg($pathRawMaps)." && ".$binMake." fetch 2> /dev/null";
passthru($tmp) == 0 or die("Error executing: ".$tmp."\n");


/*
 * Include continent and tradelane configuration 
 */
$config = $pathMapUtils."www/world.inc.php";
if (!file_exists($config))
  die("Required continent/tradelane configuration file '$config' not found.\n");

require $config;


//
// Generate continents JavasScript data file
///
echo "* Generating $worldJS ...\n";
$str  = "var pmapWorld = {";
foreach ($worldMap as $wkey => $wval)
  $str .= "\"".$wkey."\": ".$wval.", ";
$str .= "};\n";

$str .= "var pmapContinents =\n[\n";
foreach ($continentList as $name => $data)
if ($data[4] && $data[7])
{
  $str .= sprintf("  [%-15s, %5d, %5d, %5d, %5d, %5d, %5d],\n",
  "\"".$data[0]."\"",
  $data[1],
  $data[2],
  $data[1] + $data[5] - 1,
  $data[2] + $data[6] - 1,
  $data[5], $data[6]);
}
$str .= "];\n";

stOutputToFile($worldJS, $str);



//
// Generate marker files from LOC data
///
echo "Converting location data from LOC files to GMaps markers...\n";

foreach ($modes as $mode => $mdata)
{
  $tmp = escapeshellcmd($binMkLoc)." -v -o ".escapeshellarg($pathMarkerData.$mdata[0])." -G ".$mode." ";

  foreach ($continentList as $name => $data)
  {
    if ($data[4] && $data[7])
    {
      // has a map
      $tmp .=
      "-l ".escapeshellarg($locPath.$name.".loc")." ".
      "-c ".escapeshellarg($data[0])." ".
      "-x ".escapeshellarg($worldMap["ox"] + $data[1] + $mdata[1])." ".
      "-y ".escapeshellarg($worldMap["oy"] + $data[2] + $mdata[2])." ";
    }
  }

  passthru($tmp) == 0 or die("Error executing: ".$tmp."\n");
}


/* Calculate worldmap coordinates from continent coordinates
 */
function getWorldCoords($cont, $xp, $yp, &$xc, &$yc)
{
  global $worldMap, $continentList;

  if (!isset($continentList[$cont]))
    return FALSE;

  $xc = $worldMap["ox"] + $continentList[$cont][1] + $xp - 1;
  $yc = $worldMap["oy"] + $continentList[$cont][2] + $yp - 1;
  return TRUE;
}


/* Get worldmap coordinates for a given tradelane waypoint
 */
function getWaypointCoords($waypoint, &$xc, &$yc)
{
  global $tradelanePoints;
  
  if (!isset($tradelanePoints[$waypoint]))
    return FALSE;

  return getWorldCoords($tradelanePoints[$waypoint][0],
    $tradelanePoints[$waypoint][1], $tradelanePoints[$waypoint][2], $xc, $yc);
}


function outputToJSONFile($qfilename, $qdata)
{
  outputToFile($qfilename, json_encode($qdata));
}


//
// Export tradelane waypoint data
//
if (!isset($tradelanePoints))
  die("PHP array \$tradelanePoints not set, '$config' is old or incompatible.\n");

echo "\nCreating tradelane waypoint data '".$tradelaneOut."' ...\n";

$qdata = array();

foreach ($tradelanePoints as $name => $data)
{
  $html = "<b>TRADELANE WPT</b><br>".htmlentities($name);

  if (!getWorldCoords($data[0], $data[1], $data[2], $xc, $yc))
    die("Invalid tradelane waypoint '".$name."', continent '".$data[0]."' not defined.\n");

  $qdata[] = array(
    "x" => $xc,
    "y" => $yc,
    "name" => $name,
    "html" => $html,
    "continent" => "",
    "type" => "tradelane",
    "flags" => 0,
  );
}

stOutputToJSONFile($tradelaneOut, $qdata);


//
// Export tradelane polyline data
//
echo "\nCreating tradelane polyline data '".$tradelaneOverlay."' ...\n";
if (!isset($tradelaneDefs))
  die("PHP array \$tradelaneDefs not set, '$config' is old or incompatible.\n");

$qdata = array();
foreach ($tradelaneDefs as $index => $points)
{
  $qline = array();

  foreach ($points as $point)
  {
    if (!getWaypointCoords($point, $xc, $yc))
      die("Invalid tradelane definition #$index: waypoint '".$point."' not defined.\n");

    $qline[] = array(
      "x" => $xc,
      "y" => $yc
    );
  }
  
  $qdata[] = $qline;
}


stOutputToJSONFile($tradelaneOverlay, $qdata);


//
// Generate PNG maps
//
function makeMap($inFilename, $outFilename, $zlevel, $data)
{
  global $mapPalette, $fontFile, $fontSize;

  // Try to open input file
  $file = @fopen($inFilename, "r");
  if ($file === FALSE)
  {
    echo "Could not open input '".$inFilename."'\n";
    return FALSE;
  }

  // Derp
  $zoom = pow(2, $zlevel - 1);
  $width = $data[5];
  $height = $data[6];

  // Create image and assign colors
  $im = @imagecreate($width*$zoom, $height*$zoom);
  if ($im === FALSE)
  {
    echo "Could not allocate image data for '".$inFilename."'\n";
    return FALSE;
  }

  $black = imagecolorallocate($im, 0, 0, 0);
  foreach ($mapPalette as $id => $val)
    $colors[$id] = imagecolorallocate($im, $val[0], $val[1], $val[2]);

  imagefilledrectangle($im, 0, 0, $width*$zoom, $height*$zoom, $black);

  // Read input raw
  $y = 0;
  while ($y < $height && ($data = fgets($file, 4096)) !== FALSE) 
  {
    for ($x = 0; $x < $width; $x++)
    {
      if ($zoom == 1)
      {
        imagesetpixel($im, $x, $y, $colors[$data[$x]]);
      }
      else 
      if ($zoom < 6)
      {
        imagefilledrectangle($im, $x*$zoom, $y*$zoom, ($x+1)*$zoom-1, ($y+1)*$zoom-1, $colors[$data[$x]]);
      }
      else
      {
        imagettftext($im,
          $fontSize[$zoom], 0,
          $x*$zoom + $fontSize[$zoom]/4,
          $y*$zoom + $fontSize[$zoom],
          $colors[$data[$x]],
          $fontFile,
          $data[$x]);
      }
    }
    $y++;
    echo ".";
  }

  if (imagepng($im, $outFilename) === FALSE)
  {
    echo "Error creating '".$outFilename."'\n";
    imagedestroy($im);
    return FALSE;
  }

  imagedestroy($im);
  return TRUE;
}

if (!stMakeDir($pathImageCache)) {
  die("Failed to create cache directory '".$pathImageCache."'!\n");
}

echo "Generating basic map data...\n";
foreach ($continentList as $name => $data)
if ($data[4] && $data[7])
{
  $inFilename = $pathRawMaps.$name.$rawSuffix;
  if (!file_exists($inFilename))
  {
    $inFilename = $pathRawMaps.$name.$rawAltSuffix;
    if (!file_exists($inFilename))
      die("Required file '".$pathRawMaps.$name."(".$rawSuffix."|".$rawAltSuffix.")' does not exist.\n");
  }
  $inMtime = filemtime($inFilename);
  
  for ($zoom = 1; $zoom <= 5; $zoom++)
  {
    $outFilename = $pathImageCache.$name."_".($zoom + 4).".png";
    $outMtime = file_exists($outFilename) ? filemtime($outFilename) : -1;
    echo "- ".$name." (".$data[0]."): ";
    if ($inMtime > $outMtime)
    {
      $res = makeMap($inFilename, $outFilename, $zoom, $data);
      echo ($res ? "OK" : "FAIL")."\n";
    }
    else
      echo "SKIPPED\n";
  }
}


/*
 * Generate small versions
 */
echo "\nGenerating scaled small map data...\n";
$mapScales = array("50", "25", "12.5", "6.25", "3.125");
foreach ($continentList as $name => $data)
if ($data[4] && $data[7])
{
  $n = count($mapScales);
  $inFilename = $pathImageCache.$name."_".$n.".png";
  if (!file_exists($inFilename))
  {
    die("Required file '".$inFilename."' does not exist.\n");
  }
  $inMtime = filemtime($inFilename);

  foreach ($mapScales as $scale)
  {
    $n--;
    $outFilename = $pathImageCache.$name."_".$n.".png";
    $outMtime = file_exists($outFilename) ? filemtime($outFilename) : -1;

    echo "* ".$inFilename." -> ".$outFilename.": ";
    if ($inMtime > $outMtime)
    {
      $tmp = escapeshellcmd($binConvert)." ".escapeshellarg($inFilename)." -scale ".escapeshellarg($scale."%")." -type Palette ".escapeshellarg($outFilename);
      passthru($tmp) == 0 or die("Error executing: ".$tmp."\n");
      echo "OK\n";
    }
    else
      echo "SKIPPED\n";
  }
}


/*
 * Build tiles
 */
function createTile($scale, $zoom, $x, $y, $mapData, $mapMtime)
{
  global $continentList, $worldMap, $pathImageCache, $pathTileData, $tileDim;

  $outFilename = $pathTileData.$zoom."/".$y."/".$x.".png";
  if (file_exists($outFilename) && filemtime($outFilename) >= $mapMtime)
  {
    echo "!";
    return;
  }

  $drawn = 0;
  $im = false;

  foreach ($continentList as $continent => $data)
  {
    if (!$data[4] || !$data[7])
      continue;
    
    $cx = $data[1] + $worldMap["ox"];
    $cy = $data[2] + $worldMap["oy"];
    $cw = $data[5];
    $ch = $data[6];
    
    $tx = -($cx*$scale - $x*$tileDim);
    $ty = -($cy*$scale - $y*$tileDim);

    if (($cx + $cw)*$scale > $x*$tileDim && 
        ($cy + $ch)*$scale > $y*$tileDim &&
        ($cx * $scale)     < ($x+1)*$tileDim && 
        ($cy * $scale)     < ($y+1)*$tileDim)
    {
      if ($drawn == 0)
      {
        if ($zoom < 9)
        {
          $im = @imagecreate($tileDim, $tileDim);
          $sea = imagecolorallocate($im, 51, 51, 170);
          imagefilledrectangle($im, 0, 0, $tileDim, $tileDim, $sea);
        }
        else
        {
          $inFilename = $pathTileData.$zoom."/sea.png";
          $im = @imagecreatefrompng($inFilename);
          if ($im === false)
            die("\nCould not open '".$inFilename."'.\n");
        }
      }

      $dx = $tileDim;
      $dy = $tileDim;
      $xx = 0;
      $yy = 0;

      if ($tx < 0)
      {
        $xx -= $tx;
        $dx += $tx+1;
        $tx  = 0;
      }

      if ($ty < 0)
      {
        $yy -= $ty;
        $dy += $ty+1;
        $ty  = 0;
      }

      if ($dx > $cw*$scale-$tx)
        $dx = $cw*$scale - $tx;

      if ($dy > $ch*$scale-$ty)
        $dy = $ch*$scale - $ty;

      if ($im !== false)
      {
        imagecopy($im, $mapData[$continent], $xx, $yy, $tx, $ty, $dx, $dy);
        $drawn++;
      }
    }
  }

  if ($drawn > 0)
  {
    stMakeDir($pathTileData.$zoom."/".$y, 0755);
    imagepng($im, $outFilename);
    imagedestroy($im);
  }
/* 
   else {
    if (file_exists($outFilename))
      unlink($outFilename);

    symlink($pathTileData."sea.png", $outFilename);
    echo "+";
  }
*/
}


echo "\nBuilding tiles data for all zoom levels ...\n";

$mapData = array();

for ($zoom = $minZoom; $zoom <= $maxZoom; $zoom++)
{
  $zoom2 = $zoom - 1;
  $scale = pow(2, $zoom2 - 5);

  stMakeDir($pathTileData.$zoom);

  echo "\nzoom level $zoom: ";

  $mx = ceil(($worldMap["w"] * $scale) / $tileDim);
  $my = ceil(($worldMap["h"] * $scale) / $tileDim);
  $mw = ceil( $worldMap["w"] * $scale);
  $mh = ceil( $worldMap["h"] * $scale);

  $mapMtime = -1;
  foreach ($continentList as $continent => $data)
  if ($data[4] && $data[7])
  {
    $mapFile = $pathImageCache.$continent."_".$zoom2.".png";
    $mapData[$continent] = @imagecreatefrompng($mapFile);
    $tmp = filemtime($mapFile);
    if ($tmp > $mapMtime)
      $mapMtime = $tmp;
  }

  for ($y = 0; $y < $mx; $y++)
  {
    for ($x = 0; $x < $my; $x++)
      createTile($scale, $zoom, $x, $y, $mapData, $mapMtime);
    echo ".";
  }
  echo "\n";

  // Free image data for each continent
  foreach($continentList as $continent => $data)
  {
    if ($data[4] && $data[7])
      imagedestroy($mapData[$continent]);
  }
}

?>