﻿/*
	Rewrite of Mapstraction for Microsoft Virtual Earth and Google Maps.
	Parts taken from the Mapstraction API:
	Copyright (c) 2006-7, Tom Carden, Steve Coast, Mikel Maron, Andrew Turner, Henri Bergius
	All rights reserved.
	
	Reauthored 07/15/2008 by Florian Schnell
 */
 
function GetTileUrl_Mapnik(a, z) {
    return "http://tile.openstreetmap.org/" +
                z + "/" + a.x + "/" + a.y + ".png";
}

function GetTileUrl_Cyclemap(a, z) {
    return "http://www.thunderflames.org/tiles/cycle/" +
                z + "/" + a.x + "/" + a.y + ".png";
}

function parseHexColor(hex) {
	var r = parseHex(hex.substr(0, 2));
	var g = parseHex(hex.substr(2, 2));
	var b = parseHex(hex.substr(4, 2));
	return { 'r':r, 'g':g, 'b':b };
}

function parseHex(hex) {
	var i = 0;
	var sum = 0;
	hex = hex.toLowerCase();
	for (var n = 0; n < hex.length; n++) {
		var number = hex.charAt(n);
		switch (number) {
			case 'a':
				number = 10;
			break;
			case 'b':
				number = 11;
			break;
			case 'c':
				number = 12;
			break;
			case 'd':
				number = 13;
			break;
			case 'e':
				number = 14;
			break;
			case 'f':
				number = 15;
			break;
			default:
				number = parseInt(number);
			break;
		}
		sum += number * Math.pow(16, hex.length - (i + 1));
		i++;
	}
	return sum;
}

function MapApi(element, api) {
	this.api = api;
	this.map = null;
	this.loaded = false;
	this.markers = [];
	this.overlays = {};
	this.shapes = [];
	this.lines = [];
	this.mapElement = document.getElementById(element);
	
	this.addApi(this.mapElement, api);
}

MapApi.prototype.addApi = function (element, api) {
	switch (api) {
		case 'google':
			if (GMap2) {
				if (GBrowserIsCompatible()) {
					this.map = new GMap2(element);
					this.loaded = true;
					this.map.enableDoubleClickZoom();
					this.map.enableScrollWheelZoom();
					this.map.enableContinuousZoom();
					this.map.addMapType(G_PHYSICAL_MAP);
					
			var copyright = new GCopyright(1, new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)), 0, '(<a rel="license" href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>)');
			var copyrightCollection =  new GCopyrightCollection('Kartendaten &copy; 2009 <a href="http://www.openstreetmap.org/">OpenStreetMap</a> Contributors');
			copyrightCollection.addCopyright(copyright);

			// openstreetmap tilelayer
			var osm_tilelayers = [new GTileLayer(copyrightCollection, 1, 18)];
			osm_tilelayers[0].getTileUrl = GetTileUrl_Mapnik;
			osm_tilelayers[0].isPng = function () { return true; };
			osm_tilelayers[0].getOpacity = function () { return 1.0; };
			var OSM = new GMapType(osm_tilelayers,  G_SATELLITE_MAP.getProjection(), "OSM", { urlArg: 'mapnik', linkColor: '#000000'});
			this.map.addMapType(OSM);
			
			
		/*
			var ocm_tilelayers = [new GTileLayer(copyrightCollection, 1, 18)];
			ocm_tilelayers[0].getTileUrl = GetTileUrl_Cyclemap;
			ocm_tilelayers[0].isPng = function () { return true; };
			ocm_tilelayers[0].getOpacity = function () { return 1.0; };
			var CYCLE_MAP = new GMapType(ocm_tilelayers,  new GMercatorProjection(19), "OCM", { urlArg: 'cyclemap', linkColor: '#000000' });
			this.map.addMapType(CYCLE_MAP);	 
*/
/*	
			var hControl = new GHierarchicalMapTypeControl();
			
			hControl.addRelationship(G_SATELLITE_MAP, G_HYBRID_MAP, "Labels", true);
			hControl.addRelationship(G_NORMAL_MAP, G_PHYSICAL_MAP, "Gelände", false);
			hControl.addRelationship(G_NORMAL_MAP, MAPNIK_MAP, "OSM", false);
			hControl.addRelationship(G_NORMAL_MAP, TK50_MAP, "TK50", false);
			
			this.map.addControl(hControl);
*/			
			// http://econym.org.uk/gmap/example_maptypecontrols.htm
			
			this.map.addControl(new GMenuMapTypeControl());

			
			//var mapMenu = new GMenuMapTypeControl();
			//this.map.addControl(mapMenu);			
					
					
					//this.map.hideControls();
				} else {
					alert('browser not compatible with Google Maps');
				}
			} else {
				alert('Google map script not imported');
			}
		break;
		case 'microsoft':
			if (VEMap) {
			
				// set new style position ...
				element.style.position='relative';
				
				// VE hack for firefox 2 ...
				var ffv = 0;
				var ffn = "Firefox/";
				var ffp = navigator.userAgent.indexOf(ffn);
				if (ffp != -1) {
					ffv = parseFloat(navigator.userAgent.substring(ffp+ffn.length));
				} 
				if (ffv >= 1.5 || window.opera) { // added fix for opera browsers ...
					Msn.Drawing.Graphic.CreateGraphic = function(f,b) { return new Msn.Drawing.SVGGraphic(f, b); };
				}

				this.map = new VEMap(element.id);
				this.map.LoadMap();
				this.map.SetScaleBarDistanceUnit(VEDistanceUnit.Kilometers);
				this.map.ClearInfoBoxStyles();
								
				this.loaded = true;
			} else {
				alert('Virtual Earth map script not imported');
			}
		break;
	}
};

MapApi.prototype.registerEvent = function (event_name, callback) {
	switch (this.api) {
		case 'google':
			switch (event_name) {
				case 'onclick':
					GEvent.addListener(this.map, "onclick", callback);
				break;
				case 'movestart':
					GEvent.addListener(this.map, 'movestart', callback);
				break;
				case 'moveend':
					GEvent.addListener(this.map, 'moveend', callback);
				break;
				case 'zoomend':
					GEvent.addListener(this.map, 'zoomend', callback);
				break;
				case 'onchangemapstyle':
					GEvent.addListener(this.map, 'maptypechanged', callback);
				break;
				case 'mouseover':
					GEvent.addListener(this.map, 'mouseover', callback);
				break;
				case 'mouseout':
					GEvent.addListener(this.map, 'mouseout', callback);
				break;
				case 'onbubbleclose':
					GEvent.addListener(this.map, 'infowindowclose', callback);
				break;
			}
      	break;
		case 'microsoft':
			switch (event_name) {
				case 'onmousemove':
					this.map.AttachEvent("onmousemove", callback);
				break;
				case 'onclick':
					this.map.AttachEvent("onclick", callback);
				break;
				case 'moveend':
					this.map.AttachEvent("onendpan", callback);
				break;
				case 'movestart':
					this.map.AttachEvent("onstartpan", callback);
				break;
				case 'zoomend':
					this.map.AttachEvent("onendzoom", callback);
				case 'onchangemapstyle':
					this.map.AttachEvent("onchangemapstyle", callback);
				break;
			}			
		break;
	}
};

MapApi.prototype.hideControls = function () {
	switch (this.api) {
		case 'google':
			this.map.hideControls();
		break;
		case 'microsoft':
			this.map.HideDashboard();
		break;
	}
};

MapApi.prototype.showControls = function () {
	switch (this.api) {
		case 'google':
			this.map.showControls();
		break;
		case 'microsoft':
			// not implemented
		break;
	}
};

MapApi.prototype.getCenter = function () {
	switch (this.api) {
		case 'google':
			var pt = this.map.getCenter();
      		return new LatLonPoint(pt.lat(),pt.lng());
      	break;
		case 'microsoft':
			var pt = this.map.GetCenter();
			return new LatLonPoint(pt.Latitude,pt.Longitude);
		break;
	}
};

MapApi.prototype.setCenter = function (center) {
	switch (this.api) {
		case 'google':
			this.map.setCenter(center.toGoogle());
      	break;
		case 'microsoft':
			this.map.SetCenter(center.toMicrosoft());
		break;
	}
};

MapApi.prototype.getZoom = function () {
	switch (this.api) {
		case 'google':
			return this.map.getZoom();
		case 'microsoft':
			return this.map.GetZoomLevel();
	}
};

MapApi.prototype.getBounds = function () {
    switch (this.api) {
        case 'google':
            var gbox = this.map.getBounds();
            sw = gbox.getSouthWest();
            ne = gbox.getNorthEast();
            return new BoundingBox(sw.lat(), sw.lng(), ne.lat(), ne.lng());
        case 'microsoft':
            var mbox = this.map.GetMapView();
            nw = mbox.TopLeftLatLong;
            se = mbox.BottomRightLatLong;
            return new BoundingBox(se.Latitude,nw.Longitude,nw.Latitude,se.Longitude);
    }
};


MapApi.prototype.zoomToBounds = function (object) {
	switch (this.api) {
		case 'google':
			
			// create new bounds object ...
			var bounds = new GLatLngBounds();
			for (var i = 0; i < object.length; i++) {
				
				// if object is a line ...
				if (object[i].type == 'line') {
					object[i] = object[i].toGoogle();
					var lbounds = object[i].getBounds();
					bounds.extend(lbounds.getSouthWest());
					bounds.extend(lbounds.getNorthEast());
				}
				
				// if object is a marker ...
				if (object[i].type == 'marker') {
					object[i] = object[i].toGoogle();
					bounds.extend(object[i].getLatLng());
				}
			}
			
			// now zoom to bounds ...
			var zoom = 18;
			if (this.map.getBoundsZoomLevel(bounds) < 18) zoom = this.map.getBoundsZoomLevel(bounds)
			this.map.setZoom(zoom);
			this.map.setCenter(bounds.getCenter());
		break;
		
		case 'microsoft':
			for (var i = 0; i < object.length; i++) {
				object[i] = object[i].toMicrosoft();
			}
			this.map.SetMapView(object);
		break;
	}
};

MapApi.prototype.setCenterAndZoom = function (center, zoom) {
	switch (this.api) {
		case 'google':
			this.map.setCenter(center.toGoogle(), zoom);
		break;
		case 'microsoft':
			this.map.SetCenterAndZoom(center.toMicrosoft(), zoom);
		break;
	}
};

MapApi.prototype.searchMapType = function (type_name){
	m = this.map.getMapTypes()
	for (var i = 0; i < m.length; i++){
		if (m[i].getName() == type_name)
			result = m[i]
	}
	return result;
}

MapApi.prototype.setMapType = function (type) {
	switch (this.api) {
		case 'google':
			switch (type) {
				case 'ROAD':
					this.map.setMapType(G_NORMAL_MAP);
					break;
				case 'SATELLITE':
					this.map.setMapType(G_SATELLITE_MAP);
					break;
				case 'HYBRID':
					this.map.setMapType(G_HYBRID_MAP);
					break;
				case 'PHYSICAL':
					this.map.setMapType(G_PHYSICAL_MAP);
					break;
				case 'OSM':
					this.map.setMapType(this.searchMapType('OSM'));
					break;
				case 'TK50':
					this.map.setMapType(this.searchMapType('TK50'));
					break;
				case 'DOK':
					this.map.setMapType(this.searchMapType('DOK'));
					break;			
				case 'DOP':
					this.map.setMapType(this.searchMapType('DOP'));
					break;		
				default:
					this.map.setMapType(G_NORMAL_MAP);
				break;
			}
		break;
		case 'microsoft':
			switch (type) {
				case 'ROAD':
					this.map.SetMapStyle(VEMapStyle.Road);
					break;
				case 'SATELLITE':
					this.map.SetMapStyle(VEMapStyle.Aerial);
					break;
				case 'HYBRID':
					this.map.SetMapStyle(VEMapStyle.Hybrid);
					break;
				case 'PHYSICAL':
					this.map.SetMapStyle(VEMapStyle.Shaded);
					break;
				case 'BIRDSEYE':
					this.map.SetMapStyle(VEMapStyle.Birdseye);
					break;					
				default:
					this.map.SetMapStyle(VEMapStyle.Road);
				break;
			}
		break;
	}
};

/**
 * getMapType gets the imagery type for the map.
 * The type can be one of:
 * Mapstraction.ROAD
 * Mapstraction.SATELLITE
 * Mapstraction.HYBRID
 */
MapApi.prototype.getMapType = function() {
    switch (this.api) {
        case 'google':
            type = this.map.getCurrentMapType();
            switch(type) {
                case G_NORMAL_MAP:
                    return 'ROAD';
                case G_SATELLITE_MAP:
                    return 'SATELLITE';
                case G_HYBRID_MAP:
                    return 'HYBRID';
				case G_PHYSICAL_MAP:
					return 'PHYSICAL';
				case this.map.getMapTypes()[4]:
				//case this.searchMapType('OSM'):
					return 'OSM';
				case this.searchMapType('TK50'):
					return 'TK50';
				case this.searchMapType('DOK'):
					return 'DOK';
				case this.searchMapType('DOP'):
					return 'DOP';
                default:
                    return null;
            }
            break;
        case 'microsoft':
            type = this.map.GetMapStyle();
            switch(type) {
                case VEMapStyle.Road:
                    return 'ROAD';
                case VEMapStyle.Aerial:
                    return 'SATELLITE';
                case VEMapStyle.Hybrid:
                    return 'HYBRID';
                case VEMapStyle.Shaded:
                    return 'PHYSICAL';
                case VEMapStyle.Birdseye:
                    return 'BIRDSEYE';					
                default:
                    return null;
            }
            break;
    }
};

MapApi.prototype.getMapTypes = function () {
	switch (this.api) {
		case 'google':
			return this.map.getMapTypes();
		break;
		case 'microsoft':
			// not implemented yet
		break;
	}
};

MapApi.prototype.addLargeControls = function (type) {
	switch (this.api) {
		case 'google':
      		//this.map.addControl(new GMapTypeControl());
      		this.map.addControl(new GOverviewMapControl());
			this.map.addControl(new GLargeMapControl());
			// this.map.addControl(new ExtLargeMapControl()); //don´t works with getMinimumResolution() for min/max-zoom
			
			
			var otherOpts = { 
			buttonStartingStyle: {display:'block',color:'black',background:'white',width:'7em',textAlign:'center',
	            fontFamily:'Verdana',fontSize:'11px',border:'1px solid gray',paddingBottom:'1px',cursor:'pointer'},
	          buttonHTML: 'Zoom auf Ausschnitt',
	          buttonZoomingHTML: 'Bitte mit der Maus Karten-Ausschnitt w&auml;hlen (zum abbrechen hier klicken)',
	          buttonZoomingStyle: {background:'yellow'},
	          backButtonHTML: 'zur&uuml;ck',  
	          backButtonStyle: {display:'none',marginTop:'3px',background:'#FFFFC8'},
	          backButtonEnabled: true
	        }
			
			this.map.addControl(new DragZoomControl({}, otherOpts, {}), new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(7,45)));
			this.map.addControl(new GScaleControl());
			this.map.addControl(new MoreControl());
			this.map.addControl(new GNavLabelControl());

			var context_opts = { whatsHere: false };

			this.map.addControl(new ContextMenuControl( context_opts ));
			
			
	/*brightness-control for all maps
	var opacity_tilelayer = new GTileLayer(new GCopyrightCollection(), 0, 17);
	opacity_tilelayer.opacity = 0.1; // Default opacity value
	opacity_tilelayer.getTileUrl = function(tile, zoom) {return "../visitcity/img/white_map_tile.png";};
	opacity_tilelayer.getOpacity = function() {return this.opacity;};
	
	this.map.addOverlay(new GTileLayerOverlay(opacity_tilelayer));			
	*/	
	// to implement...	
	//this.map.addControl(new OpacityControl( opacity_tilelayer ));
			
		break;
		case 'microsoft':
			// not implemented ...
		break;
	}
};

MapApi.prototype.resizeTo = function (width, height) {
	switch (this.api) {
		case 'google':
			var center = this.map.getCenter();
			this.mapElement.style.width = width + 'px';
      		this.mapElement.style.height = height + 'px';
      		this.map.checkResize();
      		this.map.setCenter(center);
		break;
		case 'microsoft':
			this.map.Resize(width, height);
		break;
	}
};

MapApi.prototype.addMarker = function (marker) {
	switch (this.api) {
		case 'google':
			var gpin = marker.toGoogle();
      		marker.setChild(gpin);
      		this.map.addOverlay(gpin);
      		this.markers.push(marker);
      		GEvent.addListener(gpin, "mouseover",  function (e) {
				if (marker.onmouseover) marker.onmouseover(e);
			});
      		GEvent.addListener(gpin, "mouseout",  function (e) {
				if (marker.onmouseout) marker.onmouseout(e);
			});
      		GEvent.addListener(gpin, "click",  function (e) {
				if (marker.onclick) marker.onclick(e);
			});
		break;
		case 'microsoft':
			var mpin = marker.toMicrosoft();
			marker.setChild(mpin);
			this.map.AddShape(mpin);
			this.markers.push(marker);
			var map = this.map;
			this.map.AttachEvent("onclick",
			function (mapevent) {
				if (map.GetShapeByID(mapevent.elementID) == mpin) {
					if (marker.onclick) marker.onclick();
				}
			});
		break;
	}
};

MapApi.prototype.removeMarker = function(marker) {
	var tmparray = [];
	while(this.markers.length > 0){
		var current_marker = this.markers.pop();
		if(marker == current_marker) {
			switch (this.api) {
				case 'google':
					this.map.removeOverlay(marker.proprietary_marker);
				break;
				case 'microsoft':
					this.map.DeleteShape(marker.proprietary_marker);
				break;      
			}
			marker.onmap = false;
			break;
		} else {
			tmparray.push(current_marker);
		}
	}
	this.markers = this.markers.concat(tmparray);
};

MapApi.prototype.addLine = function (line) {
	switch (this.api) {
		case 'google':
			var gline = line.toGoogle();
			line.setChild(gline);
			this.map.addOverlay(gline);
			this.lines.push(line);
			GEvent.addListener(gline, "click",  function (e) {
				if (line.onclick) line.onclick(e);
			});
			GEvent.addListener(gline, "mouseover",  function (e) {
				if (line.onmouseover) line.onmouseover(e);
			});
			GEvent.addListener(gline, "mousemove",  function (e) {
				if (line.onmousemove) line.onmousemove(e);
			});
			GEvent.addListener(gline, "mouseout",  function (e) {
				if (line.onmouseout) line.onmouseout(e);
			});
		break;
		case 'microsoft':
			var mline = line.toMicrosoft();
			line.setChild(mline);
			this.map.AddShape(mline);
			var map = this.map;
			if (line.title) {
				this.map.AttachEvent("onmousemove",
				function (mapevent) {
					if (map.GetShapeByID(mapevent.elementID) == mline) {
						if (line.onmousemove) line.onmousemove(mapevent);
					}
				});
				this.map.AttachEvent("onmouseout",
				function (mapevent) {
					if (map.GetShapeByID(mapevent.elementID) == mline) {
						if (line.onmouseout) line.onmouseout(mapevent);
					}
				});
				this.map.AttachEvent("onclick",
				function (mapevent) {
					if (map.GetShapeByID(mapevent.elementID) == mline) {
						if (line.onclick) line.onclick(mapevent);
					}
				});
			}
			this.lines.push(line);
		break;
	}
};

MapApi.prototype.removeLine = function (line) {
	var tmparray = [];
	while (this.lines.length > 0) {
		var current_line = this.lines.pop();
		if (current_line == line) {
			switch (this.api) {
				case 'google':
					this.map.removeOverlay(line.proprietary_line);
				break;
				case 'microsoft':
					this.map.DeleteShape(line.proprietary_line);
				break;
			}
		} else {
			tmparray.push(current_line);
		}
	}
	this.lines = this.lines.concat(tmparray);
};

MapApi.prototype.addOverlay = function(layer) {
		
	switch (this.api) {
		case 'google':
            this.map.addOverlay(layer);
		break;
		case 'microsoft':
		break;
		default:
			if(this.debug) {
				alert(this.api + ' not supported by Mapstraction.addOverlay');
			}
		break;
	}
};

MapApi.prototype.removeOverlay = function(layer) {
	switch (this.api) {
		case 'google':
            this.map.removeOverlay(layer);
			break;
		case 'microsoft':
			alert("nicht implementiert");
			break;
	}
};

MapApi.prototype.getOverlayShapes = function (url) {
	switch (this.api) {
		case 'microsoft':
			return this.shapes[url];
		break;
		case 'google':
			return this.shapes[url];
		break;
	}
};

MapApi.prototype.getOverlay = function (url) {
	return this.overlays[url];
};

MapApi.prototype.getMap = function () {
	return this.map;
};

MapApi.prototype.dispose = function () {
	switch (this.api) {
		case 'microsoft':
			return this.map.Dispose();
		break;
		case 'google':
			// not implemented ...
		break;
	}
};

// LatLonPoint //////////////////////////////////////////////////////////////

function LatLonPoint(lat, lon) {
  this.lat = lat;
  this.lon = lon;
  this.lng = lon; // lets be lon/lng agnostic
}

LatLonPoint.prototype.toGoogle = function() {
  return new GLatLng(this.lat,this.lon);
};

LatLonPoint.prototype.toMicrosoft = function() {
  return new VELatLong(this.lat,this.lon);
};

LatLonPoint.prototype.toString = function() {
  return this.lat + ', ' + this.lon;
};


LatLonPoint.prototype.distance = function(otherPoint) {
	var d, dr;
	dr = 0.017453292519943295; // 2.0 * PI / 360.0; or, radians per degree
	d = Math.cos(otherPoint.lon*dr - this.lon*dr) * Math.cos(otherPoint.lat*dr - this.lat*dr);
	return Math.acos(d)*6378.137; // equatorial radius
	//return -1; 
};

LatLonPoint.prototype.equals = function(otherPoint) {
  return this.lat == otherPoint.lat && this.lon == otherPoint.lon;
};

//////////////////////////
//
//  BoundingBox
//
//////////////////////////

/**
 * BoundingBox creates a new bounding box object
 * @param {double} swlat the latitude of the south-west point
 * @param {double} swlon the longitude of the south-west point
 * @param {double} nelat the latitude of the north-east point
 * @param {double} nelon the longitude of the north-east point
 * @returns a new BoundingBox
 * @type BoundingBox
 * @constructor
 * @classDescription BoundingBox
 */
function BoundingBox(swlat, swlon, nelat, nelon) {
    //FIXME throw error if box bigger than world
    //alert('new bbox ' + swlat + ',' +  swlon + ',' +  nelat + ',' + nelon);
    this.sw = new LatLonPoint(swlat, swlon);
    this.ne = new LatLonPoint(nelat, nelon);
}

/**
 * getSouthWest returns a LatLonPoint of the south-west point of the bounding box
 * @returns the south-west point of the bounding box
 * @type LatLonPoint
 */
BoundingBox.prototype.getSouthWest = function() {
    return this.sw;
};

/**
 * getNorthEast returns a LatLonPoint of the north-east point of the bounding box
 * @returns the north-east point of the bounding box
 * @type LatLonPoint
 */
BoundingBox.prototype.getNorthEast = function() {
    return this.ne;
};

/**
 * isEmpty finds if this bounding box has zero area
 * @returns whether the north-east and south-west points of the bounding box are the same point
 * @type boolean
 */
BoundingBox.prototype.isEmpty = function() {
    return this.ne == this.sw; // is this right? FIXME
};

/**
 * contains finds whether a given point is within a bounding box
 * @param {LatLonPoint} point the point to test with
 * @returns whether point is within this bounding box
 * @type boolean
 */
BoundingBox.prototype.contains = function(point){
    return point.lat >= this.sw.lat && point.lat <= this.ne.lat && point.lon >= this.sw.lon && point.lon <= this.ne.lon;
};

/**
 * toSpan returns a LatLonPoint with the lat and lon as the height and width of the bounding box
 * @returns a LatLonPoint containing the height and width of this bounding box
 * @type LatLonPoint
 */
BoundingBox.prototype.toSpan = function() {
    return new LatLonPoint( Math.abs(this.sw.lat - this.ne.lat), Math.abs(this.sw.lon - this.ne.lon) );
};

/**
 * extend extends the bounding box to include the new point
 */
BoundingBox.prototype.extend = function(point) {
    if(this.sw.lat > point.lat) {
        this.sw.lat = point.lat;
    }
    if(this.sw.lon > point.lon) {
        this.sw.lon = point.lon;
    }
    if(this.ne.lat < point.lat) {
        this.ne.lat = point.lat;
    }
    if(this.ne.lon < point.lon) {
        this.ne.lon = point.lon;
    }
    return;
};



// Labeled Marker for GoogleMaps /////////////////////////////////////////////////////////
/*
 *  This is a temporary method to implement labeled POIs in google maps until someone 
 *  spend some time implementing it into the marker class
 */
MapApi.prototype.addLabeledMarker = function(lat, lng, iconURL, labelText) {
	switch (this.api) {
	case 'google':
		var latlng = new GLatLng(lat, lng);

		var icon = new GIcon();
		icon.image = iconURL;
		icon.iconSize = new GSize(19,24);
		icon.iconAnchor = new GPoint(5, 25);
		icon.infoWindowAnchor = new GPoint(25, 7);

		opts = { 
		  "icon": icon,
		  "clickable": false,
		  "labelText": String(labelText),
		  "labelOffset": new GSize(14,-23),
		  "labelClass": "poiLabel"
		};
		var marker = new LabeledMarker(latlng, opts);
/*	for now (the print page) i dont need any event
		GEvent.addListener(marker, "click", function() {
		  marker.openInfoWindowHtml("I'm a Labeled Marker!");
		});
*/

		
		this.map.addOverlay(marker);
	}
}


// Marker /////////////////////////////////////////////////////////////////

function Marker(point) {
	this.type = 'marker';
	this.api = false;
	this.location = point;
	this.onmap = false;
	this.proprietary_marker = false;
	this.attributes = [];
	this.pinID = "mspin-"+new Date().getTime()+'-'+(Math.floor(Math.random()*Math.pow(2,16)));
	this.iconUrl = false;
	this.onclick = false;
	this.onmousemove = false;
	this.onmouseout = false;
	this.onmouseover = false;
	this.visible = true;
}

Marker.prototype.getLocation = function() {
	return this.location;
};

Marker.prototype.setChild = function(some_proprietary_marker) {
	this.proprietary_marker = some_proprietary_marker;
	this.onmap = true;
};

Marker.prototype.setLabel = function(labelText) {
 	this.labelText = labelText;
};

Marker.prototype.addData = function(options){
	for(var sOptKey in options) {
		switch(sOptKey) {
			case 'label':
				this.setLabel(options.label);
			break;
			case 'infoBubble':
				this.setInfoBubble(options.infoBubble);
			break;
			case 'icon':
				if(options.iconSize && options.iconAnchor) {
					this.setIcon(options.icon, options.iconSize, options.iconAnchor);
				} else if(options.iconSize) {
					this.setIcon(options.icon, options.iconSize);
				} else {
					this.setIcon(options.icon);					
				}
			break;
			case 'printImage':
				this.setPrintImage(options.printImage);
			break;
			case 'iconShadow':
				if(options.iconShadowSize)
					this.setShadowIcon(options.iconShadow, new Array(options.iconShadowSize[0], options.iconShadowSize[1]));
				else
					this.setIcon(options.iconShadow);
			break;
			case 'infoDiv':
				this.setInfoDiv(options.infoDiv[0],options.infoDiv[1]);
			break;
			case 'draggable':
				this.setDraggable(options.draggable);
			break;
			case 'hover':
				this.setHover(options.hover);
				// no break statement here intentionally
			case 'hoverIcon':
				this.setHoverIcon(options.hoverIcon);
			break;
			case 'openBubble':
				this.openBubble();
			break;
			case 'groupName':
				this.setGroupName(options.groupName);
			break;
			default:
				// don't have a specific action for this bit of 
				// data so set a named attribute
				this.setAttribute(sOptKey, options[sOptKey]);
			break;
		}
	}    
};

Marker.prototype.setInfoBubble = function(infoBubble) {
	this.infoBubble = infoBubble;
};

Marker.prototype.setInfoDiv = function(infoDiv,div){
	this.infoDiv = infoDiv;
	this.div = div;
};

Marker.prototype.setIcon = function(iconUrl, iconSize, iconAnchor) {
	this.iconUrl = iconUrl;
	if(iconSize) { this.iconSize = iconSize; }
	if(iconAnchor) { this.iconAnchor = iconAnchor; }
};

Marker.prototype.setIconSize = function(iconSize){
	if(iconSize) { this.iconSize = iconSize; }
};

Marker.prototype.setIconAnchor = function(iconAnchor){
	if(iconAnchor) { this.iconAnchor = iconAnchor; }
};

Marker.prototype.setPrintImage = function(printImage){
	if(printImage) { this.printImage = printImage; }
};

Marker.prototype.setShadowIcon = function(iconShadowUrl, iconShadowSize){
	this.iconShadowUrl = iconShadowUrl;
	if(iconShadowSize) { this.iconShadowSize = iconShadowSize; }
};

Marker.prototype.setTransparentIcon = function(iconTransparentUrl){
	this.iconTransparentUrl = iconTransparentUrl;
};

Marker.prototype.setHoverIcon = function(hoverIconUrl){
	this.hoverIconUrl = hoverIconUrl;
};

Marker.prototype.setDraggable = function(draggable) {
	this.draggable = draggable;
};

Marker.prototype.setHover = function(hover) {
	this.hover = hover;
};

Marker.prototype.setGroupName = function(sGrpName) {
	this.groupName = sGrpName;
};

Marker.prototype.toGoogle = function() {
	var options = {};
	if (this.labelText) {
		options.title =  this.labelText;
	}
	if (this.iconUrl) {
		var icon = new GIcon(G_DEFAULT_ICON,this.iconUrl);
		icon.image = this.iconUrl;
		if (this.iconSize) {
			icon.iconSize = new GSize(this.iconSize[0], this.iconSize[1]);
			var anchor;
			if(this.iconAnchor) {
				anchor = new GPoint(this.iconAnchor[0], this.iconAnchor[1]);                
			} else {
				// FIXME: hard-coding the anchor point
				anchor = new GPoint(this.iconSize[0], this.iconSize[1]);
			}
			icon.iconAnchor = anchor;
		}
		if(this.iconShadowUrl) {
			icon.shadow = this.iconShadowUrl;
			if(this.iconShadowSize) {
				icon.shadowSize = new GSize(this.iconShadowSize[0], this.iconShadowSize[1]);
			}		
		}
		if(this.iconTransparentUrl) {
			icon.transparent = this.iconTransparentUrl;
		}		
		if (this.printImage) {
			icon.printImage = this.printImage;
			icon.mozPrintImage  = this.printImage;
		}
		options.icon = icon;
		
	}

	if(this.draggable){
		options.draggable = this.draggable;
	}
	var gmarker = new GMarker( this.location.toGoogle(),options);
	
	if(this.infoBubble){
		var theInfo = this.infoBubble;
		var event_action;
		if(this.hover) {
			event_action = "mouseover";
		} else {
			event_action = "click";
		}
		GEvent.addListener(gmarker, event_action, function() {
			gmarker.openInfoWindowHtml(theInfo, {maxWidth: 100});
			//gmarker.openInfoWindowTabsHtml([new GInfoWindowTab("Info",theInfo, {maxWidth: 100}), new GInfoWindowTab("tab2","Inhalt tab2")]);
		});
	}
	
 	if(this.hoverIconUrl){
		GEvent.addListener(gmarker, "mouseover", function() {
			gmarker.setImage(this.hoverIconUrl);
		});
		GEvent.addListener(gmarker, "mouseout", function() {
			gmarker.setImage(this.iconUrl);
		});
	}
	
	if(this.infoDiv){
		var theInfo = this.infoDiv;
		var div = this.div;
		var event_action;
		if(this.hover) {
			event_action = "mouseover";
		} else {
			event_action = "click";
		}
		GEvent.addListener(gmarker, event_action, function() {
			document.getElementById(div).innerHTML = theInfo;
		});
	}
	this.api = 'google';
	return gmarker;
};

Marker.prototype.toMicrosoft = function () {
	var pin = new VEShape(VEShapeType.Pushpin, this.location.toMicrosoft());
	pin.SetTitle(this.labelText);
	pin.SetDescription(this.infoBubble);
	
	if (this.iconUrl) {
		var icon = new VECustomIconSpecification();
		icon.CustomHTML = "<img style='position:relative;margin-left:"+this.iconAnchor[0]+"px;margin-top:"+this.iconAnchor[1]+"px' src='"+this.iconUrl+"'></img>";
		icon.Image = this.iconUrl;
		icon.ImageOffset = new VEPixel(this.iconAnchor[0], this.iconAnchor[1]);
		icon.TextContent = '  ';
		pin.SetCustomIcon(icon);
	}
	
	this.api = 'microsoft';
	return pin;
};

Marker.prototype.setAttribute = function(key,value) {
	this.attributes[key] = value;
};

Marker.prototype.getAttribute = function(key) {
	return this.attributes[key];
};

Marker.prototype.openBubble = function() {
  if (this.api) { 
    switch (this.api) {
      case 'google':
        var gpin = this.proprietary_marker;
		gpin.openInfoWindowHtml(theInfo, {maxWidth: 100});
        //gpin.openInfoWindowTabsHtml([new GInfoWindowTab("Info",this.infoBubble), new GInfoWindowTab("tab2","Inhalt tab2")]);
        break;
      case 'microsoft':
        var pin = this.proprietary_marker;
        // bloody microsoft -- this is broken
        var el = $m(this.pinID + "_" + this.maps[this.api].GUID).onmouseover;
        setTimeout(el, 1000); // wait a second in case the map is booting as it cancels the event
        break;
    }
  } else {
    alert('You need to add the marker before opening it');
  }
};

Marker.prototype.hide = function() {
	this.visible = false;
	if (this.api) {
		switch (this.api) {
			case 'google':
				this.proprietary_marker.hide();
			break;
			case 'microsoft':
				this.proprietary_marker.Hide();
			break;
			default:
				alert(this.api + "not supported by Marker.hide");
			break;
		}
	}
};

Marker.prototype.show = function() {
	this.visible = true;
	if (this.api) {
		switch (this.api) {
			case 'google':
				this.proprietary_marker.show();
			break;
			case 'microsoft':
				this.proprietary_marker.Show();
			break;
			default:
				alert(this.api + "not supported by Marker.show");
			break;
		}
	}
};

// Line /////////////////////////////////////////////////////////////////////

function Line () {
	this.type = 'line';
	this.api = false;
	this.color = "0000FF";
	this.alpha = 1.0;
	this.width = 2;
	this.points = [];
	this.proprietary_line = false;
	this.description = '';
	this.title = '';
	this.uid = 0;
	this.onclick = false;
	this.onmousemove = false;
	this.onmouseout = false;
	this.onmouseover = false;
	this.visible = true;
// folgende Werte wichtig für Encodierung http://www.svennerberg.com/2008/11/polylines-in-google-maps-part-2/
	this.levels = "PMP"; 
	this.numLevels = 18;
	this.zoomFactor = 2;
};

Line.prototype.setTitle = function (title) {
	this.title = title;
};

Line.prototype.setDescription = function (desc) {
	this.description = desc;
};

Line.prototype.getLocation = function() {
	return this.points[0];
};

Line.prototype.setColor = function (rgb, a) {
	this.color = rgb;
	this.alpha = a;
};

Line.prototype.setWidth = function (width) {
	this.width = width;
};

Line.prototype.addPoint = function (point) {
	this.points.push(point);
};

Line.prototype.setChild = function (child) {
	this.proprietary_line = child;
};

Line.prototype.hide = function () {
	this.visible = false;
	switch (this.api) {
		case 'microsoft':
			this.proprietary_line.Hide();
		break;
		case 'google':
			this.proprietary_line.hide();
		break;
	}
};

Line.prototype.show = function () {
	this.visible = true;
	switch (this.api) {
		case 'microsoft':
			this.proprietary_line.Show();
		break;
		case 'google':
			this.proprietary_line.show();
		break;
	}
};

Line.prototype.toMicrosoft = function () {
	var vePoints = [];
	for (var i = 0; i < this.points.length; i++) {
		if (this.points[i].toMicrosoft) {
			var point = this.points[i].toMicrosoft();
			vePoints.push(point);
		}
	}
	var line = new VEShape(VEShapeType.Polyline, vePoints);
	line.SetLineWidth(this.width);
	var rgb = parseHexColor(this.color);
	line.SetLineColor(new VEColor(rgb.r, rgb.g, rgb.b, this.alpha));
	line.SetPoints(vePoints);
	line.SetDescription(this.description);
	line.SetTitle(this.title);
	line.HideIcon();
	this.api = 'microsoft';
	return line;
};

Line.prototype.toGoogle = function () {
	var gmPoints = [];
	for (var i = 0; i < this.points.length; i++) {
		var point = this.points[i].toGoogle();
		gmPoints.push(point);
	}
	//var line = new GPolyline(gmPoints, "#" + this.color, this.width, this.alpha);
	//var line = new GPolyline.fromEncoded("#" + this.color, this.width, this.alpha, gmPoints, this.zoomFactor, this.levels, this.numLevels);
	var polylineEncoder = new PolylineEncoder(18,2,0.000001,true); 
	var line = polylineEncoder.dpEncodeToGPolyline(gmPoints,"#" + this.color, this.width, this.alpha);

	this.api = 'google';
	return line;
};

/*
 * Opacity GControl by Klokan Petr Pridal (based on XSlider of Mike Williams)
 */
 
function OpacityControl( overlay ) {
  this.overlay = overlay;
}
OpacityControl.prototype = new GControl();

// This function positions the slider to match the specified opacity
OpacityControl.prototype.setSlider = function(pos) {
  var left = Math.round((58*pos));
  this.slide.left = left;
  this.knob.style.left = left+"px";
  this.knob.style.top = "0px";
}

// This function reads the slider and sets the overlay opacity level
OpacityControl.prototype.setOpacity = function() {
  this.overlay.getTileLayer().opacity = this.slide.left/58;
  this.map.removeOverlay(this.overlay);
  this.map.addOverlay(this.overlay);
}


// This gets called by the API when addControl(new OpacityControl())
OpacityControl.prototype.initialize = function(map) {
  var that=this;
  this.map = map;

  // Is this MSIE, if so we need to use AlphaImageLoader
  var agent = navigator.userAgent.toLowerCase();
  if ((agent.indexOf("msie") > -1) && (agent.indexOf("opera") < 1)){this.ie = true} else {this.ie = false}

  // create the background graphic as a <div> containing an image
  var container = document.createElement("div");
  container.style.width="70px";
  container.style.height="21px";

  // Handle transparent PNG files in MSIE
  if (this.ie) {
    var loader = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader("+
      "src='http://www.maptiler.org/img/opacity-slider.png', sizingMethod='crop');";
    container.innerHTML = '<div style="height:21px; width:70px; ' +loader+ '" ></div>';
  } else {
    container.innerHTML = '<div style="height:21px; width:70px; background-image:url(http://www.maptiler.org/img/opacity-slider.png)" ></div>';
  }

  // create the knob as a GDraggableObject
  // Handle transparent PNG files in MSIE
  if (this.ie) {
    var loader = "progid:DXImageTransform.Microsoft.AlphaImageLoader("+
      "src='http://www.maptiler.org/img/opacity-slider.png', sizingMethod='crop');";
    this.knob = document.createElement("div");
    this.knob.style.height="21px";
    this.knob.style.width="13px";
    this.knob.style.overflow="hidden";
    this.knob_img = document.createElement("div");
    this.knob_img.style.height="21px";
    this.knob_img.style.width="83px";
    this.knob_img.style.filter=loader;
    this.knob_img.style.position="relative";
    this.knob_img.style.left="-70px";
    this.knob.appendChild(this.knob_img);
  } else {
    this.knob = document.createElement("div");
    this.knob.style.height="21px";
    this.knob.style.width="13px";
    this.knob.style.backgroundImage="url(http://www.maptiler.org/img/opacity-slider.png)";
    this.knob.style.backgroundPosition="-70px 0px";
  }
  container.appendChild(this.knob);
  this.slide=new GDraggableObject(this.knob, {container:container});
  this.slide.setDraggableCursor('pointer');
  this.slide.setDraggingCursor('pointer');
  this.container = container;

  // attach the control to the map
  map.getContainer().appendChild(container);

  // init slider
  this.setSlider( this.overlay.getTileLayer().opacity );

  // Listen for the slider being moved and set the opacity
  GEvent.addListener(this.slide, "dragend", function() {that.setOpacity()});
  //GEvent.addListener(this.container, "click", function( x, y ) { alert(x, y) });

  return container;
}

// Set the default position for the control
OpacityControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 47));
}
