/*
 * Geometry Algorithms and Utility Functions 1.1
 * © 2005 E-Technik Website Development (Pty) Ltd
 * Author: Marc Heiligers (marc@e-technik.com)
 */

function getFeatureId(id) {
	var fullid = id;
	var p = fullid.lastIndexOf(",");
	return fullid.substr(p + 1, fullid.length - p - 1);
}

function getLayerId(id) {
	var fullid = id;
	var p = fullid.lastIndexOf(",");
	return fullid.substr(0, p);
}

function makeFeatureId(layerId, featureId) {
	return layerId + "," + featureId;
}

//makeSvgPoints reads through the svg for the features in a layer
//and calculates the coordinates of each point. It calls the callback
//after each feature to allow updates and must be call again by the callback
//The callback must take a "more" parameter:
//	if true, call again; if false, done
//TODO: rewrite as a class
var msp_currentLayer = null;
var msp_currentChild = null;
var msp_currentArray = null;
var msp_callback = null;
function makeSvgPoints(svgLayer, callback)
{
	var layerData = mapData.GetLayer(svgLayer.id);
	if (!layerData)
	{
		return false;
	}
	if (layerData.Points && layerData.MinScale == -1)
	{
		return false;
	}
	layerData.Points = new Array();
	msp_currentLayer = svgLayer;
	msp_currentArray = layerData.Points;
	msp_currentChild = svgLayer.firstChild;
	msp_callback = callback;
	makeSvgPointsWorker();
}

function makeSvgPointsWorker() {
	var layer = msp_currentLayer;
	var children = msp_currentArray;
	var child = msp_currentChild;
	if (!child) {
		msp_currentLayer = null;
		msp_currentChild = null;
		msp_currentArray = null;
		msp_callback(false);
		return;
	}
	children[child.id] = makeSvgPointsFromFeature(child);
	msp_currentChild = child.nextSibling;
	msp_callback(true);
}

function makeSvgPointsFromFeature(child) {
	switch(child.nodeName)
	{
		case "path":
		{
			var points = child.attributes.getNamedItem("d").value.split(" ");
			var V = new Array();
			var p0 = new Point(0, 0);
			var x = null;
			var y = null;
			var mode = null;
			for(var i = 0; i < points.length; ++i)
			{
				p = points[i].substr(0, 1);
				if (p == "M" || p == "m" || p == "L" || p == "l")
				{
					var vals = points[i].substr(1);
					var xy = vals.split(",");
					if (xy.length != 2)
						continue;
					var x = parseFloat(xy[0]);
					var y = parseFloat(xy[1]);
					if (isNaN(x) || isNaN(y))
						continue;
					switch(p)
					{
						case "M":
						case "L":
							V[V.length] = new Point(x, y);
							break;
						case "m":
						case "l":
							V[V.length] = new Point(p0.x + x, p0.y + y);
							break;
					}
				}
				else if (p == "Z" || p == "z")
				{
					V[V.length] = V[0];
				}
			}
			return V;
		}
		case "rect":
		{
			var x = parseFloat(child.attributes.getNamedItem("x").value);
			var y = parseFloat(child.attributes.getNamedItem("y").value);
			var w = parseFloat(child.attributes.getNamedItem("width").value);
			var h = parseFloat(child.attributes.getNamedItem("height").value);
			var V = new Array();
			V[0] = new Point(x, y);
			V[1] = new Point(x + w, y);
			V[2] = new Point(x + w, y + h);
			V[3] = new Point(x, y + h);
			V[4] = V[0];
			return V;
		}
		case "circle":
		{
			var x = parseFloat(child.attributes.getNamedItem("cx").value);
			var y = parseFloat(child.attributes.getNamedItem("cy").value);
			var r = parseFloat(child.attributes.getNamedItem("r").value);
			var V = new Array();
			V[0] = new Point(x, y - r);
			V[1] = new Point(x + r, y);
			V[2] = new Point(x, y + r);
			V[3] = new Point(x - r, y);
			V[4] = V[0];
			return V;
		}
		case "use":
		{
			try
			{
				var x = parseFloat(child.attributes.getNamedItem("x0").value);
				var y = parseFloat(child.attributes.getNamedItem("y0").value);
				var r = parseFloat(child.attributes.getNamedItem("r0").value);
				var V = new Array();
				V[0] = new Point(x, y - r);
				V[1] = new Point(x + r, y);
				V[2] = new Point(x, y + r);
				V[3] = new Point(x - r, y);
				V[4] = V[0];
				return V;
			}
			catch(ex){}
			break;
		}
		case "image":
		{
			var x = parseFloat(child.attributes.getNamedItem("x").value);
			var y = parseFloat(child.attributes.getNamedItem("y").value);
			var w = parseFloat(child.attributes.getNamedItem("width").value);
			var h = parseFloat(child.attributes.getNamedItem("height").value);
			var V = new Array();
			V[0] = new Point(x, y);
			V[1] = new Point(x + w, y);
			V[2] = new Point(x + w, y + h);
			V[3] = new Point(x, y + h);
			V[4] = V[0];
			return V;
		}
		case "polygon":
		{
			var points = child.attributes.getNamedItem("points").value.split(" ");
			var V = new Array();
			for(var i = 0; i < points.length; ++i)
			{
				var xy = points[i].split(",");
				V[V.length] = new Point(parseFloat(xy[0]), parseFloat(xy[1]));
			}
			V[V.length] = V[0];
			return V;
		}
	}
}

function transform(t, p)
{
	var s = t.split(")");
	var n, xy;
	for(var i = 0; i < s.length; ++i)
	{
		s[i] = trim(s[i]);
		if (s[i] != "")
		{
			switch(s[i].substr(0, 1))
			{
				case "s":
					n = s[i].split("(");
					xy = n[1].split(",");
					p.x /= parseFloat(trim(xy[0]));
					p.y /= parseFloat(trim(xy[1]));
					break;
				case "t":
					n = s[i].split("(");
					xy = n[1].split(",");
					p.x -= parseFloat(trim(xy[0]));
					p.y -= parseFloat(trim(xy[1]));
					break;
				case "r":
				/*
				var str = s[i] + "\n" + p.x + " : " + p.y + "\n";
					n = s[i].split("(");
					rxy = n[1].split(",");
					p.x -= parseFloat(trim(rxy[1]));
					p.y -= parseFloat(trim(rxy[2]));
				str += p.x + " : " + p.y + "\n";
					var xp = p.x * Math.cos(parseFloat(trim(rxy[0]))) + p.y * Math.sin(parseFloat(trim(rxy[0])));
					var yp = p.y * Math.cos(parseFloat(trim(rxy[0]))) + p.x * Math.sin(parseFloat(trim(rxy[0])));
				str += xp+ " : " + yp + "\n";
					p.x = xp + parseFloat(trim(rxy[1]));
					p.y = yp + parseFloat(trim(rxy[2]));
				//alert(str + p.x + " : " + p.y);
				*/
					break;

			}
		}
	}
}

function untransform(t, p)
{
	var s = t.split(")");
	var n, xy;
	for(var i = 0; i < s.length; ++i)
	{
		s[i] = trim(s[i]);
		if (s[i] != "")
		{
			switch(s[i].substr(0, 1))
			{
				case "s":
					n = s[i].split("(");
					xy = n[1].split(",");
					p.x *= parseFloat(trim(xy[0]));
					p.y *= parseFloat(trim(xy[1]));
					break;
				case "t":
					n = s[i].split("(");
					xy = n[1].split(",");
					p.x += parseFloat(trim(xy[0]));
					p.y += parseFloat(trim(xy[1]));
					break;
				case "r":
					n = s[i].split("(");
					rxy = n[1].split(",");
					p.x -= parseFloat(trim(rxy[1]));
					p.y -= parseFloat(trim(rxy[2]));
					var xp = p.x * Math.cos(-parseFloat(trim(rxy[0]))) + p.y * Math.sin(-parseFloat(trim(rxy[0])));
					var yp = p.y * Math.cos(-parseFloat(trim(rxy[0]))) + p.x * Math.sin(-parseFloat(trim(rxy[0])));
					p.x = xp + parseFloat(trim(rxy[1]));
					p.y = yp + parseFloat(trim(rxy[2]));
			}
		}
	}
}

function unscale(t, p)
{
	var s = t.split(")");
	var n, xy;
	for(var i = 0; i < s.length; ++i)
	{
		s[i] = trim(s[i]);
		if (s[i] != "")
		{
			switch(s[i].substr(0, 1))
			{
				case "s":
					n = s[i].split("(");
					xy = n[1].split(",");
					p.x *= parseFloat(trim(xy[0]));
					p.y *= parseFloat(trim(xy[1]));
					break;
			}
		}
	}
}

function scale(t, p)
{
	var s = t.split(")");
	var n, xy;
	for(var i = 0; i < s.length; ++i)
	{
		s[i] = trim(s[i]);
		if (s[i] != "")
		{
			switch(s[i].substr(0, 1))
			{
				case "s":
					n = s[i].split("(");
					xy = n[1].split(",");
					p.x /= parseFloat(trim(xy[0]));
					p.y /= parseFloat(trim(xy[1]));
					break;
			}
		}
	}
}

//Calculates the scale in a svg transform.
//Assumes that x-scale && y-scale are equal
function getScale(t)
{
	var s = t.split(")");
	var n, xy;
	var m = 1;
	for(var i = 0; i < s.length; ++i)
	{
		s[i] = trim(s[i]);
		if (s[i] != "")
		{
			switch(s[i].substr(0, 1))
			{
				case "s":
					n = s[i].split("(");
					m *= parseFloat(trim(n[0]));
					break;
			}
		}
	}
}

//Calculates the translate in a svg transform.
//Assumes that there is a single translate in the transform
function getTranslate(t) {
	var s = t.split(")");
	var n, xy;
	var m = 1;
	for(var i = 0; i < s.length; ++i) {
		s[i] = trim(s[i]);
		if (s[i] != "") {
			switch(s[i].substr(0, 1)) {
				case "t":
					n = s[i].split("(");
					n2 = n.split(",");
					return new Point(parseFloat(n2[0]), parseFloat(n2[1]));
			}
		}
	}
}
function pointInBox(p, b) {
	return (p.x > b.x && p.x < b.x + b.width && p.y > b.y && p.y < b.y + b.height);
}
function rectIntersect(r1, r2) {
    return !( r2.x > r1.x + r1.w || r2.x + r2.w < r1.x || r2.y > r1.y + r1.h || r2.y + r2.h < r1.y);
}
function rectBoxIntersect(r, b) {
    return !( b.x > r.x + r.w || b.x + b.width < r.x || b.y > r.y + r.h || b.y + b.height < r.y);
}
function inViewBox(r, v) {
	return rectBoxIntersect(r, v);
}

function polyInRectFull(V, n, p0, p1) {
	var x0 = p0.x, x1 = p1.x, y0 = p0.y, y1 = p1.y;
	if (x0 > x1) {
		x0 = x1;
		x1 = p0.x;
	}
	if (y0 > y1) {
		y0 = y1;
		y1 = p0.y;
	}
	for(var i = 0; i < n; ++i) {
		if (V[i].x <= x0 || V[i].x >= x1 || V[i].y <= y0 || V[i].y >= y1) {
			return false;
		}
	}
	return true;
}

function polyInRectPartial(V, n, p0, p1) {
	var x0 = p0.x, x1 = p1.x, y0 = p0.y, y1 = p1.y;
	if (x0 > x1) {
		x0 = x1;
		x1 = p0.x;
	}
	if (y0 > y1) {
		y0 = y1;
		y1 = p0.y;
	}
	for(var i = 0; i < n; ++i) {
		if (V[i].x >= x0 && V[i].x <= x1 && V[i].y >= y0 && V[i].y <= y1) {
			return true;
		}
		if (wn_PnPoly(new Point(x0, y0), V, n) ||
			wn_PnPoly(new Point(x1, y0), V, n) ||
			wn_PnPoly(new Point(x0, y1), V, n) ||
			wn_PnPoly(new Point(x1, y1), V, n)) {
			return true;
		}
	}
	return false;
}

function pointNearLine(p, V, n, d)
{
	for(var i = 0; i < n; ++i)
	{
		if (dist_Point_to_Segment(p, new Line(V[i], V[i + 1])) < d)
		{
			return true;
		}
	}
	return false;
}

function pointToPoint(p0, p1)
{
	var x = p0.x - p1.x;
	var y = p0.y - p1.y
	return Math.sqrt(x * x + y * y);
}

function lineInRectFull(V, n, p0, p1)
{
	var x0 = p0.x, x1 = p1.x, y0 = p0.y, y1 = p1.y;
	if (x0 > x1)
	{
		x0 = x1;
		x1 = p0.x;
	}
	if (y0 > y1)
	{
		y0 = y1;
		y1 = p0.y;
	}
	for(var i = 0; i < 1; ++i)
	{
		if (V[i].x <= x0 || V[i].x >= x1 || V[i].y <= y0 || V[i].y >= y1)
		{
			return false;
		}
	}
	return true;
}

function lineInRectPartial(V, n, p0, p1)
{
	var x0 = p0.x, x1 = p1.x, y0 = p0.y, y1 = p1.y;
	if (x0 > x1)
	{
		x0 = x1;
		x1 = p0.x;
	}
	if (y0 > y1)
	{
		y0 = y1;
		y1 = p0.y;
	}
	for(var i = 0; i < 1; ++i)
	{
		if (V[i].x >= x0 && V[i].x <= x1 && V[i].y >= y0 && V[i].y <= y1)
		{
			return true;
		}
	}
	var top = new Line(p0, new Point(p1.x, p0.y));
	var right = new Line(new Point(p1.x, p0.y), p1);
	var bottom = new Line(new Point(p0.x, p1.y), p1);
	var left = new Line(p0, new Point(p0.x, p1.y));
	for(var i = 0; i < n; ++i)
	{
		if ((dist3D_Segment_to_Segment(top, new Line(V[i], V[i] + 1)) < SMALL_NUM) ||
			(dist3D_Segment_to_Segment(right, new Line(V[i], V[i] + 1)) < SMALL_NUM) ||
			(dist3D_Segment_to_Segment(bottom, new Line(V[i], V[i] + 1)) < SMALL_NUM) ||
			(dist3D_Segment_to_Segment(left, new Line(V[i], V[i] + 1)) < SMALL_NUM))
			return true;
	}
	return false;
}

function getEnvelope(V, n, p0, p1)
{
	p0.x = 2147483648;
	p0.y = 2147483648;
	p1.x = -2147483648;
	p1.y = -2147483648;
	for(var i = 0; i < n; ++i)
	{
		if (V[i].x <= p0.x)
		{
			p0.x = V[i].x;
		}
		if (V[i].y <= p0.y)
		{
			p0.y = V[i].y;
		}
		if (V[i].x >= p1.x)
		{
			p1.x = V[i].x;
		}
		if (V[i].y >= p1.y)
		{
			p1.y = V[i].y;
		}
	}
}

function getEnvelopeWith(V, n, p0, p1)
{
	for(var i = 0; i < n; ++i)
	{
		if (V[i].x <= p0.x)
		{
			p0.x = V[i].x;
		}
		if (V[i].y <= p0.y)
		{
			p0.y = V[i].y;
		}
		if (V[i].x >= p1.x)
		{
			p1.x = V[i].x;
		}
		if (V[i].y >= p1.y)
		{
			p1.y = V[i].y;
		}
	}
}

// Copyright 2001, softSurfer (www.softsurfer.com)
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// SoftSurfer makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.

//    a Point is defined by its coordinates {int x, y;}
//===================================================================

/* Code ported to JavaScript by Marc Heiligers (marc@e-technik.com)
 * Original source can be found at:
 *   http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
 */

// isLeft(): tests if a point is Left|On|Right of an infinite line.
//    Input:  three points P0, P1, and P2
//    Return: >0 for P2 left of the line through P0 and P1
//            =0 for P2 on the line
//            <0 for P2 right of the line
//    See: the January 2001 Algorithm "Area of 2D and 3D Triangles and Polygons"
function isLeft( P0, P1, P2 )
{
    return ( (P1.x - P0.x) * (P2.y - P0.y)
            - (P2.x - P0.x) * (P1.y - P0.y) );
}
//===================================================================

// cn_PnPoly(): crossing number test for a point in a polygon
//      Input:   P = a point,
//               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
//      Return:  0 = outside, 1 = inside
// This code is patterned after [Franklin, 2000]
function cn_PnPoly( P, V, n )
{
    var    cn = 0;    // the crossing number counter

    // loop through all edges of the polygon
    for (var i=0; i<n; i++) {    // edge from V[i] to V[i+1]
       if (((V[i].y <= P.y) && (V[i+1].y > P.y))    // an upward crossing
        || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { // a downward crossing
            // compute the actual edge-ray intersect x-coordinate
            var vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
            if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
                ++cn;   // a valid crossing of y=P.y right of P.x
        }
    }
    return (cn&1);    // 0 if even (out), and 1 if odd (in)

}
//===================================================================

// wn_PnPoly(): winding number test for a point in a polygon
//      Input:   P = a point,
//               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
//      Return:  wn = the winding number (=0 only if P is outside V[])
function wn_PnPoly( P, V, n )
{
    var    wn = 0;    // the winding number counter

    // loop through all edges of the polygon
    for (var i=0; i<n; i++) {   // edge from V[i] to V[i+1]
        if (V[i].y <= P.y) {         // start y <= P.y
            if (V[i+1].y > P.y)      // an upward crossing
                if (isLeft( V[i], V[i+1], P) > 0)  // P left of edge
                    ++wn;            // have a valid up intersect
        }
        else {                       // start y > P.y (no test needed)
            if (V[i+1].y <= P.y)     // a downward crossing
                if (isLeft( V[i], V[i+1], P) < 0)  // P right of edge
                    --wn;            // have a valid down intersect
        }
    }
    return wn;
}
//===================================================================

/* Code ported to JavaScript by Marc Heiligers (marc@e-technik.com)
 *   and changed to 2D
 * Original source can be found at:
 *   http://softsurfer.com/Archive/algorithm_0102/algorithm_0102.htm
 */

// dot product (2D) which allows vector operations in arguments
function dot(u, v)  { return u.x * v.x + u.y * v.y }
function norm(v)    { return Math.sqrt(dot(v, v))  } 	// norm = length of vector
function d(u,v)     { return norm(diff(u, v)) }     	// distance = norm of difference
function diff(u, v) { return new Point(u.x - v.x, u.y - v.y); } //difference between two points. Added by Marc Heiligers
function smul(c, v) { return new Point(c * v.x, c * v.y); } 	//scalar multiplication of a vector (Point). Added by Marc Heiligers
function padd(p, v) { return new Point(p.x + v.x, p.y + v.y); } //translate a point by a vector (Point). Added by Marc heiligers

// dist_Point_to_Line(): get the distance of a point to a line.
//    Input:  a Point P and a Line L (in any dimension)
//    Return: the shortest distance from P to L
function dist_Point_to_Line(P, L)
{
    var v = diff(L.P1, L.P0);
    var w = diff(P, L.P0);

    var c1 = dot(w, v);
    var c2 = dot(v, v);
    var b = c1 / c2;

    var Pb = padd(L.P0, smul(b, v));
    return d(P, Pb);
}
//===================================================================

// dist_Point_to_Segment(): get the distance of a point to a segment.
//    Input:  a Point P and a Segment S (in any dimension)
//    Return: the shortest distance from P to S
function dist_Point_to_Segment(P, S)
{
    var v = diff(S.P1, S.P0);
    var w = diff(P, S.P0);

    var c1 = dot(w, v);
    if ( c1 <= 0 )
        return d(P, S.P0);

    var c2 = dot(v, v);
    if ( c2 <= c1 )
        return d(P, S.P1);

    var b = c1 / c2;

    var Pb = padd(S.P0, smul(b, v));
    return d(P, Pb);
}
//===================================================================

/* Code ported to JavaScript by Marc Heiligers (marc@e-technik.com)
 *   and changed to 2D
 * Original source can be found at:
 *   http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
 */

var SMALL_NUM  = 0.00000001 // anything that avoids division overflow

// dist3D_Segment_to_Segment():
//    Input:  two 3D line segments S1 and S2 //Obviously 2D since our Point is only 2D. Marc Heiligers
//    Return: the shortest distance between S1 and S2
function dist3D_Segment_to_Segment(S1, S2)
{
    var u = diff(S1.P1, S1.P0);
    var v = diff(S2.P1, S2.P0);
    var w = diff(S1.P0, S2.P0);
    var a = dot(u,u);        // always >= 0
    var b = dot(u,v);
    var c = dot(v,v);        // always >= 0
    var d = dot(u,w);
    var e = dot(v,w);
    var D = a*c - b*b;       // always >= 0
    var sc, sN, sD = D;      // sc = sN / sD, default sD = D >= 0
    var tc, tN, tD = D;      // tc = tN / tD, default tD = D >= 0

    // compute the line parameters of the two closest points
    if (D < SMALL_NUM) { // the lines are almost parallel
        sN = 0.0;        // force using point P0 on segment S1
        sD = 1.0;        // to prevent possible division by 0.0 later
        tN = e;
        tD = c;
    }
    else {                // get the closest points on the infinite lines
        sN = (b*e - c*d);
        tN = (a*e - b*d);
        if (sN < 0.0) {       // sc < 0 => the s=0 edge is visible
            sN = 0.0;
            tN = e;
            tD = c;
        }
        else if (sN > sD) {  // sc > 1 => the s=1 edge is visible
            sN = sD;
            tN = e + b;
            tD = c;
        }
    }

    if (tN < 0.0) {           // tc < 0 => the t=0 edge is visible
        tN = 0.0;
        // recompute sc for this edge
        if (-d < 0.0)
            sN = 0.0;
        else if (-d > a)
            sN = sD;
        else {
            sN = -d;
            sD = a;
        }
    }
    else if (tN > tD) {      // tc > 1 => the t=1 edge is visible
        tN = tD;
        // recompute sc for this edge
        if ((-d + b) < 0.0)
            sN = 0;
        else if ((-d + b) > a)
            sN = sD;
        else {
            sN = (-d + b);
            sD = a;
        }
    }
    // finally do the division to get sc and tc
    sc = (Math.abs(sN) < SMALL_NUM ? 0.0 : sN / sD);
    tc = (Math.abs(tN) < SMALL_NUM ? 0.0 : tN / tD);

    // get the difference of the two closest points
    var dP = padd(w, diff(smul(sc, u), smul(tc, v)));  // = S1(sc) - S2(tc)

    return norm(dP);   // return the closest distance
}
//===================================================================

/* Code ported to JavaScript by Marc Heiligers (marc@e-technik.com)
 *   and changed to 2D
 * Original source can be found at:
 *   http://softsurfer.com/Archive/algorithm_0101/algorithm_0101.htm
 */

// area2D_Polygon(): computes the area of a 2D polygon
//    Input:  int n = the number of vertices in the polygon
//            Point* V = an array of n+2 vertices
//                       with V[n]=V[0] and V[n+1]=V[1]
//    Return: the (float) area of the polygon
function area2D_Polygon(n, V)
{
    var area = 0;
    var i, j, k;     // indices

    for (i=1, j=2, k=0; i<=n; i++, j++, k++) {
        area += V[i].x * (V[j].y - V[k].y);
    }
    return area / 2.0;
}
//===================================================================

/* Translated to JavaScript by Marc Heiligers
 * Original source at: http://www.jalix.org/ressources/miscellaneous/~vrac/faqsys/gg.html
 * ANSI C code from the article
 * "Centroid of a Polygon"
 * by Gerard Bashein and Paul R. Detmer,
	(gb@locke.hs.washington.edu, pdetmer@u.washington.edu)
 * in "Graphics Gems IV", Academic Press, 1994
 */

function polyCentroid(n, V)
{
	var i, j;
	var ai, atmp = 0, xtmp = 0, ytmp = 0;
	if (n < 3) return null;
	for (i = n-1, j = 0; j < n; i = j, j++)
	{
		ai = V[i].x * V[j].y - V[j].x * V[i].y;
		atmp += ai;
		xtmp += (V[j].x + V[i].x) * ai;
		ytmp += (V[j],y + V[i].y) * ai;
	}
	if (atmp != 0)
	{
		return new Point(xtmp / (3 * atmp), ytmp / (3 * atmp));
	}
	return null;
}

//--- Dynamic Layer Loading classes -------------------------
function DynamicLayer(id, minScale, labelMinScale)
{
	this.Id = id;
	this.MinScale = minScale;
	this.LabelMinScale = labelMinScale;
}

function DynamicLayers()
{
	this.Layers = new Array();
	this.Add = function(layer)
	{
		this.Layers[this.Layers.length] = layer;
	}
	this.Get = function(id)
	{
		for(var i = 0; i < this.Layers.length; ++i)
		{
			if (this.Layers[i].Id == id)
			{
				return id;
			}
		}
		return null;
	}
	this.GetScaledAt = function(scale)
	{
		var layers = new Array();
		for(var i = 0; i < this.Layers.length; ++i)
		{
			if (this.Layers[i].MinScale >= scale)
			{
				layers[layers.length] = this.Layers[i];
			}
		}
		return layers;
	}
	this.GetLabelsScaledAt = function(scale)
	{
		var layers = new Array();
		for(var i = 0; i < this.Layers.length; ++i)
		{
			if (this.Layers[i].LabelMinScale >= scale)
			{
				layers[layers.length] = this.Layers[i];
			}
		}
		return layers;
	}
}
