//
// OSM Boundary Relations Javascript
//
// This file Copyright 2010-2011 Andrew M. Bishop
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//


//==================================================//
// Customise this for specific selector information //
//==================================================//

var selector={selecturl: "index.txt"};


//================================================//
// Customise this for specific filter information //
//================================================//

var filters=[ {number: 1, autolist: {any: true, name: "boundary",    singular: "Type",       plural: "Types"}},
              {number: 2, autolist: {any: true, name: "admin_level", singular: "Admin Level",plural: "Admin Levels"}},
              {number: 3, checkbox: {prepare: limitToMapPrepare, check: limitToMapCheck}} ];

// The "limit to map" filter

function limitToMapPrepare()
{
 this.mapbounds=map.getExtent().clone();
 this.mapbounds.transform(epsg900913,epsg4326);
}

function limitToMapCheck(datum)
{
 if(!this.doc_checkbox.checked)
    return(true);

 if(datum.lonmax<this.mapbounds.left)   return(false);
 if(datum.latmax<this.mapbounds.bottom) return(false);
 if(datum.lonmin>this.mapbounds.right)  return(false);
 if(datum.latmin>this.mapbounds.top)    return(false);

 return(true);
}

function limitToMapMoved()
{
 if(!filters[2].doc_checkbox.checked)
    return;

 selectFromFilterList();
}

function limitToMapEnable()
{
 var doc_checkbox=document.getElementById("filter3_checkbox");

 doc_checkbox.checked=true;
}


//================================================//
// Customise this for specific sorter information //
//================================================//

var sorters=[ {number: 1, name: "ID",          sort: sortByID},
              {number: 2, name: "Name",        sort: sortByName},
              {number: 3, name: "Type",        sort: sortByBoundary},
              {number: 4, name: "Admin Level", sort: sortByAdminlevel},
              {number: 5, name: "Length",      sort: sortByLength},
              {number: 6, name: "Ways",        sort: sortByWays},
              {number: 7, name: "Nodes",       sort: sortByNodes} ];

// Sort functions

function sortByID(data1,data2)
{
 if(Number(data1.id) < Number(data2.id))
    return(-1);
 else if(Number(data1.id) > Number(data2.id))
    return(1);
 else
    return(0);
}

function sortByName(data1,data2)
{
 if(data1.name < data2.name)
    return(-1);
 else if(data1.name > data2.name)
    return(1);
 else
    return(sortByID(data1,data2));
}

function sortByBoundary(data1,data2)
{
 if(data1.boundary < data2.boundary)
    return(-1);
 else if(data1.boundary > data2.boundary)
    return(1);
 else
    return(0);
}

function sortByAdminlevel(data1,data2)
{
 if(Number(data1.admin_level) < Number(data2.admin_level))
    return(-1);
 else if(Number(data1.admin_level) > Number(data2.admin_level))
    return(1);
 else
    return(sortByName(data1,data2));
}

function sortByLength(data1,data2)
{
 if(Number(data1.length) < Number(data2.length))
    return(-1);
 else if(Number(data1.length) > Number(data2.length))
    return(1);
 else
    return(sortByName(data1,data2));
}

function sortByWays(data1,data2)
{
 nways1=data1.ways.match(/^[0-9]+/);
 nways2=data2.ways.match(/^[0-9]+/);

 if(Number(nways1) < Number(nways2))
    return(-1);
 else if(Number(nways1) > Number(nways2))
    return(1);
 else
    return(sortByNodes(data1,data2));
}

function sortByNodes(data1,data2)
{
 if(Number(data1.nodes) < Number(data2.nodes))
    return(-1);
 else if(Number(data1.nodes) > Number(data2.nodes))
    return(1);
 else
    return(sortByName(data1,data2));
}


//================================================//
// Customise this for specific result information //
//================================================//

var results={name: "name", singular: "Boundary", plural: "Boundaries",
             clear: clearResults, select: selectResult};


//
// Callback for clearing the results list.
//

function clearResults()
{
 showBox(-1);
}


//
// Select a result (click on list or click on map)
//

var display_optional_tags=["description","note","source","wikipedia","url"];

function selectResult(index)
{
 if(index==-1)
   {
    results.doc_info.innerHTML="<i>Please select boundary</i>";
   }
 else
   {
    showBox(index);

    // Update the result information in the main panel.

    var html="";

    html = html + "id = " + results.list[index].id;
    html = html + "<br>";
    html = html + "name = " + results.list[index].name;
    html = html + "<br>";
    html = html + "boundary = " + results.list[index].boundary;
    html = html + "<br>";
    html = html + "admin_level = " + results.list[index].admin_level;

    for(var t=0;t<display_optional_tags.length;t++)
      {
       var tag=display_optional_tags[t];

       if(results.list[index][tag] != undefined && results.list[index][tag] != "")
         {
          html = html + "<br>";
          html = html + tag + " = " + results.list[index][tag];
         }
      }

    html = html + "<br>";
    html = html + "length = " + results.list[index].length + " km";
    html = html + "<br>";
    html = html + "ways = " + results.list[index].ways;
    html = html + "<br>";
    html = html + "nodes = " + results.list[index].nodes;
    html = html + "<br>";
    html = html + "shape = " + results.list[index].shape;

    html = html + "<p style=\"margin-bottom: 0px\">";
    html = html + "Map: [&nbsp;";
    if(layerGPX[index] != undefined && layerGPX[index] != null)
      {
       html = html + "Add";
       html = html + "&nbsp;|&nbsp;";
       html = html + "<a href='getgpx.cgi?id=" + results.list[index].id + "' onclick='removeGPX(\"" + index + "\");return false'>Remove</a>";
      }
    else
      {
       html = html + "<a href='getgpx.cgi?id=" + results.list[index].id + "' onclick='addGPX(\"" + index + "\");return false'>Add</a>";
       html = html + "&nbsp;|&nbsp;";
       html = html + "Remove";
      }
    html = html + "&nbsp;|&nbsp;";
    html = html + "<a href='getgpx.cgi?id=" + results.list[index].id + "' onclick='zoomTo(\"" + index + "\");return false'>Zoom</a>";
    html = html + "&nbsp;]";

    html = html + "<br>";
    html = html + "<a target='other' href='getgpx.cgi?id=" + results.list[index].id + "'>Download GPX</a>";

    results.doc_info.innerHTML=html;
   }
}


//
// Map configuration
//

var map;
var layerMap=[], layerBoxes, layerGPX;
var epsg4326, epsg900913;

var box;
var gpx_style;


// 
// Initialise the 'map' object
//

function map_init(lat,lon,zoom)
{
 // Map properties (North/South and East/West limits and zoom in/out limits) are now in mapprops.js
 // Map URLs are now in mapprops.js

 //
 // Create the map
 //

 epsg4326=new OpenLayers.Projection("EPSG:4326");
 epsg900913=new OpenLayers.Projection("EPSG:900913");

 map = new OpenLayers.Map ("map",
                           {
                            controls:[
                                      new OpenLayers.Control.Navigation(),
                                      new OpenLayers.Control.PanZoomBar(),
                                      new OpenLayers.Control.ScaleLine(),
                                      new OpenLayers.Control.LayerSwitcher()
                                      ],

                            projection: epsg900913,
                            displayProjection: epsg4326,

                            minZoomLevel: mapprops.zoomout,
                            numZoomLevels: mapprops.zoomin-mapprops.zoomout+1,
                            maxResolution: 156543.0339 / Math.pow(2,mapprops.zoomout),

                            maxExtent:        new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
                            restrictedExtent: new OpenLayers.Bounds(mapprops.westedge,mapprops.southedge,mapprops.eastedge,mapprops.northedge).transform(epsg4326,epsg900913),

                            units: "m"
                           });

 // Add map tile layers

 for(var l=0;l < mapprops.mapdata.length;l++)
   {
    layerMap[l] = new OpenLayers.Layer.TMS(mapprops.mapdata[l].label,
                                           mapprops.mapdata[l].baseurl,
                                           {
                                            emptyUrl: mapprops.mapdata[l].errorurl,
                                            type: 'png',
                                            getURL: limitedUrl,
                                            displayOutsideMaxExtent: true,
                                            buffer: 1
                                           });
    map.addLayer(layerMap[l]);
   }

 // Get a URL for the tile; limited to map restricted extent.

 function limitedUrl(bounds)
 {
  var z = map.getZoom() + map.minZoomLevel;

  if (z>=7 && (bounds.right  < map.restrictedExtent.left ||
               bounds.left   > map.restrictedExtent.right ||
               bounds.top    < map.restrictedExtent.bottom ||
               bounds.bottom > map.restrictedExtent.top))
     return this.emptyUrl;

  var res = map.getResolution();
  var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
  var limit = Math.pow(2, z);

  if (y < 0 || y >= limit)
    return this.emptyUrl;

  var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));

  x = ((x % limit) + limit) % limit;
  return this.url + z + "/" + x + "/" + y + "." + this.type;
 }

 // Define a Boxes layer

 layerBoxes = new OpenLayers.Layer.Boxes("Boxes");

 map.addLayer(layerBoxes);

 box=null;

 // Define a GPX layer but don't add it yet

 layerGPX={0: null};

 gpx_style=new OpenLayers.Style({},{strokeWidth: 3, strokeColor: "#0000FF"});

 // Set the map centre to the limited range specified

 map.setCenter(map.restrictedExtent.getCenterLonLat(), map.getZoomForExtent(map.restrictedExtent,true));
 map.maxResolution = map.getResolution();

 // Move the map

 if(lon != 'lon' && lat != 'lat' && zoom != 'zoom')
   {
    if(lon<mapprops.westedge) lon=mapprops.westedge;
    if(lon>mapprops.eastedge) lon=mapprops.eastedge;

    if(lat<mapprops.southedge) lat=mapprops.southedge;
    if(lat>mapprops.northedge) lat=mapprops.northedge;

    if(zoom<mapprops.zoomout) zoom=mapprops.zoomout;
    if(zoom>mapprops.zoomin)  zoom=mapprops.zoomin;

    var lonlat = new OpenLayers.LonLat(lon,lat).transform(epsg4326,map.getProjectionObject());

    map.moveTo(lonlat,zoom-map.minZoomLevel);

    limitToMapEnable();
   }

 map.events.register("moveend", map, mapMoved);
}


//
// Map has moved
//

function mapMoved()
{
 var centre = map.getCenter().clone();

 var lonlat = centre.transform(map.getProjectionObject(),epsg4326);

 var zoom = this.getZoom() + map.minZoomLevel;

 var custom_url=document.getElementById("custom_url");

 var args="lat=" + lonlat.lat + ";lon=" + lonlat.lon + ";zoom=" + zoom;

 var url="custom.cgi?" + args;

 custom_url.href=url;

 var links=document.getElementById('links');

 if(links.hasChildNodes())
   {
    var child=links.firstChild;
    
    do
      {
       if(child.firstChild)
          if(child.firstChild.tagName=='A' || child.firstChild.tagName=='a')
            {
             var orig_url=child.firstChild.href.slice(0,child.firstChild.href.lastIndexOf("/")+1);
             child.firstChild.href=orig_url+url;
            }
      }
    while(child=child.nextSibling);
   }

 limitToMapMoved();
}


//
// Show box
//

function showBox(index)
{
 if(box!=null)
    layerBoxes.removeMarker(box);

 if(index>=0)
   {
    var lon1=results.list[index].lonmin;
    var lat1=results.list[index].latmin;
    var lon2=results.list[index].lonmax;
    var lat2=results.list[index].latmax;

    var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,map.getProjectionObject());

    box = new OpenLayers.Marker.Box(bounds);

    layerBoxes.addMarker(box);
   }
}


//
// Add a GPX trace
//

function addGPX(index)
{
 if(layerGPX[index] != undefined && layerGPX[index] != null)
    return;

 // Add a GPX layer

 var url="getgpx.cgi?id=" + results.list[index].id;

 layerGPX[index] = new OpenLayers.Layer.GML("GPX (" + results.plural + " " + results.list[index].id + ")", url,
                                            {
                                             format:     OpenLayers.Format.GPX,
                                             style:      gpx_style,
                                             projection: map.displayProjection
                                            });

 map.addLayer(layerGPX[index]);

 selectResult(index);
}


//
// Remove a GPX trace
//

function removeGPX(index)
{
 map.removeLayer(layerGPX[index]);
 layerGPX[index].destroy();
 layerGPX[index]=null;

 selectResult(index);
}


//
// Zoom to a specific location
//

function zoomTo(index)
{
 var lon1=results.list[index].lonmin;
 var lat1=results.list[index].latmin;
 var lon2=results.list[index].lonmax;
 var lat2=results.list[index].latmax;

 var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,map.getProjectionObject());

 map.zoomToExtent(bounds,false);
}

