<?php if (!defined('PmWiki')) exit();
/*
Use http://www.topomap.co.nz/ to display an excerpt from a map in PmWiki
+
| Copyright 2013 Simon Davis
| This program is free software; you can redistribute it and/or modify it under the terms of the
| GNU General Public License, Version 2, as published by the Free Software Foundation.
| http://www.gnu.org/copyleft/gpl.html
| 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.
+
# 2013-07-15 Initial version
*/
# Version date
$RecipeInfo['NZTopo']['Version'] = '2013-07-19';
$FmtPV['$NZTopoVersion'] = "'NZTopo version {$RecipeInfo['NZTopo']['Version']}'"; // return version as a custom page variable

SDV($NZTopoZoom, 12); # set default zoom factor

# declare $NZTopo for (:if enabled NZTopo:) recipe installation check
global $NZTopo; $NZTopo = 1;

## Add a custom markup 
# (:nztopo ll=-41.293722,174.871482 :)
# (:nztopo topo50=BP33912708,BP33876687 height=300 width=400 pin=1 label='destination' float=right clear=both zoom=12:)
# directive arguments are
#  ll= -- decimal latitude,longitude
#  llbs = -- -- decimal latitude,longitude;latitude,longitude
  $vlat     = '[-+]?\d{1,2}[.]\d+'; # -90 .. 90
  $vlong    = '[-+]?[1]?\d{1,2}[.]\d+'; # -180 .. 180
  $vlatlong = $vlat . '[,]' . $vlong; # latitude,longitude
  $pll      = 'll=' . $vlatlong;
  $pllbs    = 'llbs=' . $vlatlong . '(?:[;]' . $vlatlong . ')+'; # two or more pairs of lat long co-ordinates
# nztm
  $vnztm    = '\d{7}[.]\d{0,3}'; # single NZTM co-ordinate
  $vnztm2   = $vnztm . '[,]' . $vnztm; # pair of NZTM co-ordinates, easting, northing
  $pnzne    = 'nzne=' . $vnztm2;
  $pnzbs    = 'nzbs=' . $vnztm2 . '(?:[;]' . $vnztm2 . ')+'; # two or more pairs of NZTM co-ordinates
# topo50= --  topo50 grid coordinate, or two grid coordinates (not currently implemented in nztopomap
  $ptopo50  = 'topo50=[ABC][A-Z][0-4]\d{7}(?:[,][ABC][A-Z][0-4]\d{7})?';
# -- only one of topo50, ll, llbs, nzne, nzbs,, kml or gpx can be supplied
  $vscheme  = '(?:https?|ftp):\/\/'; # 
  $vurl     = '(?:(?:\'' . $vscheme . '[^\']+\')|(?:"' . $vscheme . '[^"]+"))'; # provide for single and double quoted strings
# kml= -- URL to kml file
  $pkml     = 'kml=' . $vurl;
# kml= -- URL to kml file
  $pgpx     = 'gpx=' . $vurl;
#  height= -- image height in pixels
  $pheight  = 'height=\d{1,5}';
#  width= -- image width in pixels
  $pwidth   = 'width=\d{1,5}';
#  pin= -- show pin
  $ppin     = 'pin=[01]';
#  label -- tool tip label for pin
  $plabel   = 'label=(?:(?:\'[^\']+\')|(?:"[^"]+")|(?:\w+))'; # provide for single and double quoted strings, and unquoted string
# zoom= -- scale factor for map
  $pzoom    = 'zoom=\d{1,2}';
# float= -- left or right
  $pfloat   = 'float=(?:left|right)';
# clear= -- left, right, both
  $pclear   = 'clear=(?:left|right|both)';
# only one location can be supplied
  $qlocale  = '(?:' . $pll . ')|(?:' . $pllbs . ')|(?:' . $pkml . ')|(?:' . $pgpx . ')|(?:' . $pnzne . ')|(?:' . $pnzbs . ')';
# display modifications
  $qmods    = "(" . $pheight . ")\s*|(" . $pwidth . ")\s*|(" . $ppin . ")\s*|(" . $plabel . ")\s*|(" . $pzoom . ")\s*|(" . $pfloat . ")\s*|(" . $pclear .  ")\s*";
#
Markup( 'NZTopo', 
  'directives',
  "/\\(:nztopo (" . $qlocale . ")\s*(?:" . $qmods . "){0,7}\s*:\\)/ei", # 
  "Keep(NZTopo_Parse(PSS('$1'), PSS('$2'), PSS('$3'), PSS('$4'), PSS('$5'), PSS('$6'), PSS('$7'), PSS('$8'), PSS('$9')))" );
# s = dot matches all chars including newline
# e = backreference substitution by expression
# i = case insensitive
# m = multiline
# uses lazy evaluation, preserves leading and trailing white space
# Keep prevents PmWiki markup being applied
#
  $debugon = false; # debug
/** Main NZTopo parser
 *   /param   arguements as documented above
 *   /return  The HTML-formatted NZTopo markup wrapped in a <div> of class "nztopo", see http://www.topomap.co.nz/.
 */
function NZTopo_Parse($p1, $p2, $p3, $p4, $p5, $p6, $p7, $p8, $p9) {
#
global $NZTopoZoom;
global $debugon; # for debug
// Initialise variables
  $retval  = ''; # return value
  $opt     = ParseArgs($p1 . ' ' . $p2 . ' ' . $p3 . ' ' . $p4 . ' ' . $p5 . ' ' . $p6 . ' ' . $p7 . ' ' . $p8 . ' ' . $p9);
  $version = 2; # fixed version of nztopomap interface
  $newwin  = 1; # open map in new window
  $ppcm    = 80; # 80 pixels per centrimetre to provide base scale for maps
  $width   = $opt['width']; # iframe parameter
  $height  = $opt['height']; # iframe parameter
  $float   = $opt['float']  ? 'float:' . $opt['float'] . ';' : ""; # div parameter
  $clear   = $opt['clear']  ? 'clear:' . $opt['clear'] . ';' : ""; # div parameter
  $pdiv    = $clear . $float ? ' style="' . $clear . $float . '"' : ""; 
  
// query string parameters
  $bllbs = (boolean) $opt['llbs'];
  if( $bllbs ) { # get size of map in km
    list ($widthkm, $heightkm, $dbgval) = llbskm($opt['llbs']);
    $retval .= $dbgval;
    $width  = max ($widthkm * $ppcm, $width); # override height
    $height = max ($heightkm * $ppcm, $width); # override height
  }
  $query  = ''; 
  $query .= '&new=' . $newwin;
  $query .= $opt['ll']     ? '&ll='     . $opt['ll']     : "";
  $query .= $opt['llbs']   ? '&llbs='   . $opt['llbs']   : "";
  $query .= $opt['topo50'] ? '&topo50=' . $opt['topo50'] : "";
  $query .= $opt['nzbs']   ? '&nzbs='   . $opt['nzbs']   : "";
  $query .= $opt['nzne']   ? '&nzne='   . $opt['nzne']   : "";
  $query .= $opt['kml']    ? '&kml='    . rawurlencode($opt['kml']) : "";
  $query .= $opt['gpx']    ? '&gpx='    . rawurlencode($opt['gpx']) : "";
  $query .= $opt['pin']    ? '&pin='    . $opt['pin']    : "";
  $query .= $opt['label']  ? '&lbl='    . rawurlencode(htmlentities($opt['label'])) : ""; # double url encode unicode chars for them to work with the topo map
  $query .= $opt['zoom']   ? '&z='      . $opt['zoom']   : '&z=' . $NZTopoZoom;
  $awidth  = $width  ? ' width="'  . $width  . '"' : ""; # iframe parameter
  $aheight = $height ? ' height="' . $height . '"' : ""; # iframe parameter
  $src    = 'http://www.topomap.co.nz/NZTopoMapEmbedded?&v=' . $version . $query;

  $retval .= '<div class="nztopo"' . $pdiv . '>' . "\n";
  $retval .= '<iframe seamless frameborder="0" scrolling="no" marginheight="0" marginwidth="0"' . $awidth . $aheight . ' src="' . $src . '">';
  $retval .= "\n" . '</iframe>' . $width . '+' . $height;
  $retval .= '</div>' . "\n";
/* example HTML for nztopomap
<iframe width="300" height="300" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://www.topomap.co.nz/NZTopoMapEmbedded?v=2&ll=-41.293722,174.871482&z=15&pin=1"></iframe>
*/
  if( $debugon ) {
    $retval .= '<div style="font-size:smaller;">p1=' . $p1 . ' p2=' . $p2 . ' p3=' . $p3 . ' p4=' . $p4 . ' p5=' . $p5 . ' p6=' . $p6 . ' p7=' . $p7 . ' p8=' . $p8 . ' p9=' . $p9 . '</br>' . "\n";
    $retval .= 'src=' . $src . '</div>' . "\n";
  }
  return $retval;
} # NZTopo_Parse

function llbskm($llbs) {
  global $debugon; # for debug
  $dbgval = '';
  $latlngs = preg_split ('/[,;]/', $llbs);
  $lats = array_filter($latlngs, "odd");
  $lngs = array_filter($latlngs, "even");
  $maxlat = max ($lats);
  $maxlng = max ($lngs);
  $minlat = min ($lats);
  $minlng = min ($lngs);
/* rectangle coordinates are (lat/long)
       left:      right:
  top: max/min    max/max
  bot: min/min    min/max
*/
  $width1  = distance ($maxlat, $minlng, $maxlat, $maxlng);
  $width2  = distance ($minlat, $minlng, $minlat, $maxlng);
  $height1 = distance ($maxlat, $minlng, $minlat, $minlng);
  $height2 = distance ($maxlat, $maxlng, $minlat, $maxlng);
  $widthkm = ceil (max ($width1, $width2)); # take largest width and round up to km
  $heightkm = ceil (max ($height1, $height2)); # take largest height and round up to km
  if( $debugon ) {
    $dbgval .= '<small>lats="' . var_export ($lats, TRUE) . '"</small></br>' . "\n";
    $dbgval .= '<small>lngs="' . implode ('", "', $lngs) . '"</small></br>' . "\n";
    $dbgval .= '<small>minlat=' . $minlat . ', maxlat=' . $maxlat . ', minlng=' . $minlng . ', maxlng=' . $maxlng . '</small></br>'  . "\n";
    $dbgval .= '<small>w1=' . $width1 . ', w2=' . $width2 . ', h1=' . $height1 . ', h2=' . $height2 . ', w=' . $widthkm . ', h=' . $heightkm . '</small></br>'  . "\n";
  }
  return array ($widthkm, $heightkm, $dbgval);
}

# http://snipplr.com/view/2531/
function distance($lat1, $lng1, $lat2, $lng2)
{
	$pi80 = M_PI / 180;
	$lat1 *= $pi80;
	$lng1 *= $pi80;
	$lat2 *= $pi80;
	$lng2 *= $pi80;
 
	$r = 6372.797; // mean radius of Earth in km
	$dlat = $lat2 - $lat1;
	$dlng = $lng2 - $lng1;
	$a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
	$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
	$km = $r * $c;
 
	return $km;
} # distance

function odd($var)
{   // returns whether the input integer is odd
    return($var % 2);
} # odd

function even($var)
{   // returns whether the input integer is even
    return(!($var % 2));
} # even