if(!Terrascape) {
	var Terrascape = {};
}
Terrascape.MapHandler = Class.create();
Terrascape.MapHandler.prototype = {
	initialize: function(attributes) {
		Object.extend(this, {
			mouseMove: null,
			mouseDown: null,
			mouseUp: null
		});
		if(attributes) {
			Object.extend(this. attributes);
		}
	}
};

Terrascape.MapDisplay = Class.create();
Terrascape.MapDisplay.prototype = {
	initialize: function(container, bounds) {
		this.container = $(container);
		this.bounds = bounds;
		this.viewBoxChanged = null;
	},

	//General methods
	loadCursor: function(name, url) {
	},
	setCursor: function(name) {
	},
	enableCursors: function(enabled) {
		this.disabledCursors = !enabled;
	},
	//Control methods
	setHandler: function(handler) {
		this.clearHandler();
		this.handler = handler;
		return handler;
	},

	clearHandler: function() {
		if(this.handler) {
			this.handler.destroy();
		}
		this.handler = null;
	},

	getHandlerType: function() {
		return this.handler ? this.handler.type : "";
	},

	centerOnPoint: function centerOn(p) {
	},

	zoomToPoint: function(p) {
	},

	zoomFromPoint: function(p) {
	},

	zoomToRect: function(r) {
	},

	addShape: function(shape, layer) {
	},

	removeShape: function(shape, layer) {
	},

	getLayerById: function(id) {
	},

	getLayer: function(idOrLayer) {
		if(typeof(idOrLayer) == "string") {
			return this.getLayerById(idOrLayer);
		} else {
			return idOrLayer;
		}
	},

	getLayerVisibility: function(layer) {
		return null;
	},

	setLayerVisibility: function(layer, visible) {
	},

	showLayer: function(layer) {
	},

	createLayer: function(id) {
	},

	removeLayer: function(layer) {
	},

	clearLayer: function(layer) {
	},

	pointToMap: function(p) {
	},

	pointToContainer: function(p) {
	}
};

Terrascape.MapDisplay.Event = Class.create();
Terrascape.MapDisplay.Event.prototype = {
	initialize: function(e) {
		this.point = new Point(e.clientX, e.clientY);
		this.shift = e.shiftKey;
	}
};

Terrascape.MapDisplay.Layer = Class.create();
Terrascape.MapDisplay.Layer.prototype = {
	initialize: function(id, mapDisplay) {
		this.id = id;
		this.mapDisplay = mapDisplay;
	},
	getAttribute: function(name) {
		return null;
	},
	setAttribute: function(name, value) {
	}
};

Terrascape.MapDisplay.AdobeSvg3 = Class.extend(Terrascape.MapDisplay, {
	initialize: function(container, bounds) {
		this.parent(container, bounds);
		this.object = this.container;
		this.svg = this.object.getSVGDocument();
		this.document = this.svg.documentElement;
		this.root = this.svg.firstChild;
		this.map = this.svg.getElementById("mapRoot");

		this.root.addEventListener("mousemove", this._mouseMove.bindAsEventListener(this), false);
		this.root.addEventListener("mousedown", this._mouseDown.bindAsEventListener(this), false);
		this.root.addEventListener("mouseup", this._mouseUp.bindAsEventListener(this), false);

		this._resetViewBox();
		var currentViewBox = this.map.getAttribute("viewBox");
		var currentViewBoxCoords = currentViewBox.split(" ");
		this.extents = new Rect(parseFloat(currentViewBoxCoords[0]), parseFloat(currentViewBoxCoords[1]), parseFloat(currentViewBoxCoords[2]), parseFloat(currentViewBoxCoords[3]));

	},

	centerOnPoint: function centerOn(p) {
		this._resetViewBox();
		var currentViewBox = this.map.getAttribute("viewBox");
		var currentViewBoxCoords = currentViewBox.split(" ");
		var currentX = parseFloat(currentViewBoxCoords[0]);
		var currentY = parseFloat(currentViewBoxCoords[1]);
		var currentW = parseFloat(currentViewBoxCoords[2]);
		var currentH = parseFloat(currentViewBoxCoords[3]);
		var pixelW = currentW / this.object.offsetWidth;
		var pixelH = currentH / this.object.offsetHeight;
		var pixel = Math.max(pixelW, pixelH);
		var newX = currentX - pixel * ((this.object.offsetWidth / 2) - p.x);
		var newY = currentY - pixel * ((this.object.offsetHeight / 2) - p.y);
		var newW = currentW;
		var newH = currentH;
		this.map.setAttribute("viewBox", newX + " " + newY + " " + newW + " " + newH);
		this._resetViewBox();
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
	},

	zoomToPoint: function(p) {
		this.centerOnPoint(p);
		var currentViewBox = this.map.getAttribute("viewBox");
		var currentViewBoxCoords = currentViewBox.split(" ");
		var currentX = parseFloat(currentViewBoxCoords[0]);
		var currentY = parseFloat(currentViewBoxCoords[1]);
		var currentW = parseFloat(currentViewBoxCoords[2]);
		var currentH = parseFloat(currentViewBoxCoords[3]);
		var pixelW = currentW / this.object.offsetWidth;
		var pixelH = currentH / this.object.offsetHeight;
		var pixel = Math.max(pixelW, pixelH);
		var mapperW = this.object.offsetWidth * pixel;
		var mapperH = this.object.offsetHeight * pixel;
		var newW = mapperW / 2;
		var newH = mapperH / 2;
		var newX = currentX + newW / 2;
		var newY = currentY + newH / 2;
		this.map.setAttribute("viewBox", newX + " " + newY + " " + newW + " " + newH);
		this._resetViewBox();
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
	},

	zoomFromPoint: function(p) {
		this.centerOnPoint(p);
		var currentViewBox = this.map.getAttribute("viewBox");
		var currentViewBoxCoords = currentViewBox.split(" ");
		var currentX = parseFloat(currentViewBoxCoords[0]);
		var currentY = parseFloat(currentViewBoxCoords[1]);
		var currentW = parseFloat(currentViewBoxCoords[2]);
		var currentH = parseFloat(currentViewBoxCoords[3]);
		var pixelW = currentW / this.object.offsetWidth;
		var pixelH = currentH / this.object.offsetHeight;
		var pixel = Math.max(pixelW, pixelH);
		var mapperW = this.object.offsetWidth * pixel;
		var mapperH = this.object.offsetHeight * pixel;
		var newW = mapperW * 5 / 3;
		var newH = mapperH * 5 / 3;
		var newX = currentX - (newW - mapperW) / 2;
		var newY = currentY - (newH - mapperH) / 2;
		this.map.setAttribute("viewBox", newX + " " + newY + " " + newW + " " + newH);
		this._resetViewBox();
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
	},

	zoomToRect: function(r) {
		this.map.setAttribute("viewBox", r.x + " " + r.y + " " + r.w + " " + r.h);
		this._resetViewBox();
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
	},

	zoomToExtents: function() {
		this.zoomToRect(this.extents);
	},

	getShapeById: function(id) {
	},

	addShape: function(shape, layer) {
		switch(shape.type) {
			case "point":
				shape.svg = this.svg.createElement("circle");
				this._applyPoint(shape);
				break;
			case "line":
				shape.svg = this.svg.createElement("line");
				this._applyLine(shape);
				break;
			case "lineString":
				shape.svg = this.svg.createElement("path");
				this._applyLineString(shape);
				break;
			case "rect":
				shape.svg = this.svg.createElement("rect");
				this._applyRect(shape);
				break;
			case "polygon":
				shape.svg = this.svg.createElement("path");
				this._applyPolygon(shape);
				break;
		}
		if(layer) {
			this.getLayer(layer).svg.appendChild(shape.svg);
		} else {
			this.map.appendChild(shape.svg);
		}
	},

	readdShape: function(shape, layer) {
		if(layer) {
			this.getLayer(layer).svg.appendChild(shape.svg);
		} else {
			this.map.appendChild(shape.svg);
		}
	},

	removeShape: function(shape, layer) {
		if(layer) {
			this.getLayer(layer).svg.removeChild(shape.svg);
		} else {
			this.map.removeChild(shape.svg);
		}
	},

	applyShape: function(shape) {
		switch(shape.type) {
			case "point":
				this._applyPoint(shape);
				break;
			case "line":
				this._applyLine(shape);
				break;
			case "lineString":
				this._applyLineString(shape);
				break;
			case "rect":
				this._applyRect(shape);
				break;
			case "polygon":
				this._applyPolygon(shape);
				break;
		}
	},

	getShape: function(id) {
		var svg = this.document.getElementById(id);
		if(svg) {
			return this._shapeFromSwf(svg);
		} else {
			return null;
		}
	},

	getShapeFromPoint: function(layer, p) {
		var l = this.getLayer(layer);
		if(!l) return null;
		var layerData = Terrascape.MapData.getLayer(l.id);
		if(!layerData) return null;
		if(!layerData.points) {
			layerData.points = [];
		}
		var child = l.svg.firstChild;
		while(child) {
			if(child.nodeName != "#text") {
				var bbox = child.getBBox();
				if(bbox.x == -1 || pointInBox(p, bbox)) {
					var V = layerData.points[child.id];
					if(!V) {
						V = makeSvgPointsFromFeature(child);
						layerData.points[child.id] = V;
					}
					if(V) {
						if(V[0] == V[V.length - 1]) {
							if(wn_PnPoly(p, V, V.length - 1)) {
								return this._shapeFromSwf(child, V);
							}
						}
						else {
							if(pointNearLine(p, V, V.length - 1, 1)) {
								return this._shapeFromSwf(child, V);
							}
						}
					}
				}
			}
			child = child.nextSibling;
		}
		return null;
	},

	getShapesInRect: function(layer, r) {
		var l = this.getLayer(layer);
		if(!l) return null;
		var p0 = new Point(r.x, r.y);
		var p1 = new Point(r.x + r.w, r.y + r.h);
		var layerData = Terrascape.MapData.getLayer(l.id);
		if(!layerData.points) {
			layerData.points = [];
		}
		var shapes = [];
		var child = l.svg.firstChild;
		while(child) {
			if(child.nodeName != "#text") {
				var bbox = child.getBBox();
				if(bbox.x == -1 || rectBoxIntersect(r, bbox)) {
					var V = layerData.points[child.id];
					if(!V) {
						V = makeSvgPointsFromFeature(child);
						layerData.points[child.id] = V;
					}
					if(V) {
						if(V[0] == V[V.length - 1]) {
							if(polyInRectFull(V, V.length - 1, p0, p1)) {
								shapes.add(this._shapeFromSwf(child, V));
							}
						}
						else {
							if(lineInRectFull(V, V.length - 1, p0, p1)) {
								shapes.add(this._shapeFromSwf(child, V));
							}
						}
					}
				}
			}
			child = child.nextSibling;
		}
		return shapes;
	},

	getShapesIntersectRect: function(layer, r) {
		var l = this.getLayer(layer);
		if(!l) return null;
		var p0 = new Point(r.x, r.y);
		var p1 = new Point(r.x + r.w, r.y + r.h);
		var layerData = Terrascape.MapData.getLayer(l.id);
		if(!layerData.points) {
			layerData.points = [];
		}
		var child = l.svg.firstChild;
		var shapes = [];
		while(child) {
			if(child.nodeName != "#text") {
				var bbox = child.getBBox();
				if(bbox.x == -1 || rectBoxIntersect(r, bbox)) {
					var V = layerData.points[child.id];
					if(!V) {
						V = makeSvgPointsFromFeature(child);
						layerData.points[child.id] = V;
					}
					if(V) {
						if(V[0] == V[V.length - 1]) {
							if(polyInRectPartial(V, V.length - 1, p0, p1)) {
								shapes.add(this._shapeFromSwf(child, V));
							}
						}
						else {
							if(lineInRectPartial(V, V.length - 1, p0, p1)) {
								shapes.add(this._shapeFromSwf(child, V));
							}
						}
					}
				}
			}
			child = child.nextSibling;
		}
		return shapes;
	},

	getShapeBBox: function(shape) {
		if(shape.getBBox) {
			var bbox = shape.getBBox();
		} else {
			var bbox = shape.svg.getBBox();
		}
		return new Rect(bbox.x, bbox.y, bbox.width, bbox.height);
	},

	_applyPoint: function(shape) {
		var svg = shape.svg;
		svg.setAttribute("id", shape.id);
		svg.setAttribute("cx", shape.x);
		svg.setAttribute("cy", shape.y);
		svg.setAttribute("r", shape.radius);
		svg.setAttribute("stroke", shape.strokeColor);
		svg.setAttribute("stroke-width", shape.strokeWidth);
		svg.setAttribute("stroke-opacity", shape.strokeOpacity);
		svg.setAttribute("fill", shape.fillColor);
		svg.setAttribute("fill-opacity", shape.fillOpacity);
		var styles = this.getStyleArray(svg.getAttribute("style"));
		styles["stroke"] = shape.strokeColor;
		styles["stroke-width"] = shape.strokeWidth;
		styles["stroke-opacity"] = shape.strokeOpacity;
		styles["fill"] = shape.fillColor;
		styles["fill-opacity"] = shape.fillOpacity;
		svg.setAttribute("style", this.getStyleString(styles));
	},

	_applyLine: function(shape) {
		var svg = shape.svg;
		svg.setAttribute("id", shape.id);
		svg.setAttribute("x1", shape.x);
		svg.setAttribute("y1", shape.y);
		svg.setAttribute("x2", shape.x1);
		svg.setAttribute("y2", shape.y1);
		svg.setAttribute("stroke", shape.strokeColor);
		svg.setAttribute("stroke-width", shape.strokeWidth);
		svg.setAttribute("stroke-opacity", shape.strokeOpacity);
		var styles = this.getStyleArray(svg.getAttribute("style"));
		styles["stroke"] = shape.strokeColor;
		styles["stroke-width"] = shape.strokeWidth;
		styles["stroke-opacity"] = shape.strokeOpacity;
		svg.setAttribute("style", this.getStyleString(styles));
	},

	_applyRect: function(shape) {
		var svg = shape.svg;
		svg.setAttribute("id", shape.id);
		svg.setAttribute("x", shape.x);
		svg.setAttribute("y", shape.y);
		svg.setAttribute("width", shape.width);
		svg.setAttribute("height", shape.height);
		svg.setAttribute("stroke", shape.strokeColor);
		svg.setAttribute("stroke-width", shape.strokeWidth);
		svg.setAttribute("stroke-opacity", shape.strokeOpacity);
		svg.setAttribute("fill", shape.fillColor);
		svg.setAttribute("fill-opacity", shape.fillOpacity);
		var styles = this.getStyleArray(svg.getAttribute("style"));
		styles["stroke"] = shape.strokeColor;
		styles["stroke-width"] = shape.strokeWidth;
		styles["stroke-opacity"] = shape.strokeOpacity;
		styles["fill"] = shape.fillColor;
		styles["fill-opacity"] = shape.fillOpacity;
		svg.setAttribute("style", this.getStyleString(styles));
	},

	_applyPolygon: function(shape) {
		var svg = shape.svg;
		svg.setAttribute("id", shape.id);
		svg.setAttribute("stroke", shape.strokeColor);
		svg.setAttribute("stroke-width", shape.strokeWidth);
		svg.setAttribute("stroke-opacity", shape.strokeOpacity);
		svg.setAttribute("fill", shape.fillColor);
		svg.setAttribute("fill-opacity", shape.fillOpacity);
		var styles = this.getStyleArray(svg.getAttribute("style"));
		styles["stroke"] = shape.strokeColor;
		styles["stroke-width"] = shape.strokeWidth;
		styles["stroke-opacity"] = shape.strokeOpacity;
		styles["fill"] = shape.fillColor;
		styles["fill-opacity"] = shape.fillOpacity;
		svg.setAttribute("style", this.getStyleString(styles));
		var d = "M" + shape.points[0].x + "," + shape.points[0].y;
		for(var i = 1; i < shape.points.length; ++i) {
			d += " L" + shape.points[i].x + "," + shape.points[i].y;
		}
		d += " Z";
		svg.setAttribute("d", d);
	},

	_applyLineString: function(shape) {
		var svg = shape.svg;
		svg.setAttribute("id", shape.id);
		svg.setAttribute("stroke", shape.strokeColor);
		svg.setAttribute("stroke-width", shape.strokeWidth);
		svg.setAttribute("stroke-opacity", shape.strokeOpacity);
		svg.setAttribute("fill", "none");
		var styles = this.getStyleArray(svg.getAttribute("style"));
		styles["stroke"] = shape.strokeColor;
		styles["stroke-width"] = shape.strokeWidth;
		styles["stroke-opacity"] = shape.strokeOpacity;
		styles["fill"] = "none";
		svg.setAttribute("style", this.getStyleString(styles));
		var d = "M" + shape.points[0].x + "," + shape.points[0].y;
		for(var i = 1; i < shape.points.length; ++i) {
			d += " L" + shape.points[i].x + "," + shape.points[i].y;
		}
		svg.setAttribute("d", d);
	},

	getStyleArray: function(style) {
		var styleRules = style.split(";");
		var styles = [];
		styleRules.each(function(rule) {
			var s = rule.split(":");
			styles[s[0]] = s[1];
		});
		return styles;
	},

	getStyleString: function(styles) {
		var style = "";
		for(s in styles) {
			style += s + ":" + styles[s] + ";";
		}
		return style;
	},

	_shapeFromSwf: function(svg, points) {
		var styles = this.getStyleArray(svg.getAttribute("style"));
		var attributes = {
			id: svg.getAttribute("id"),
			strokeColor: styles["stroke"] || svg.getAttribute("stroke"),
			strokeOpacity: styles["stroke-opacity"] || svg.getAttribute("stroke-opacity"),
			strokeWidth: styles["stroke-width"] || svg.getAttribute("stroke-width"),
			fillColor: styles["fill"] || svg.getAttribute("fill"),
			fillOpacity: styles["fill-opacity"] || svg.getAttribute("fill-opacity"),
			svg: svg
		};
		if(!points) {
			points = makeSvgPointsFromFeature(svg);
		}
		switch(svg.nodeName) {
			case "path": {
				attributes.points = points;
				if(points[0] == points[points.length - 1]) {
					return new Terrascape.Polygon(attributes);
				} else {
					return new Terrascape.LineString(attributes);
				}
			}
			case "rect": {
				attributes.x = points[0].x;
				attributes.y = points[0].y;
				attributes.width = points[2].x - attributes.x;
				attributes.height = points[2].y - attributes.y;
				return new Terrascape.Rect(attributes);
			}
			case "circle": {
				attributes.x = svg.getAttribute("cx");
				attributes.y = svg.getAttribute("cy");
				attributes.radius = svg.getAttribute("r");
				return new Terrascape.Point(attributes);
			}
			case "use": {
				//TODO: Make new Shape for "use"
				attributes.x = svg.getAttribute("x0");
				attributes.y = svg.getAttribute("y0");
				attributes.radius = svg.getAttribute("r0");
				return new Terrascape.Point(attributes);
			}
			case "image": {
				//TODO: Make new Shape for "image"
				attributes.x = points[0].x;
				attributes.y = points[0].y;
				attributes.width = points[2].x - attributes.x;
				attributes.height = points[2].y - attributes.y;
				return new Terrascape.Rect(attributes);
			}
			case "polygon": {
				attributes.points = points;
				return new Terrascape.Polygon(attributes);
			}
		}
	},

	//Layer methods
	getLayerById: function(id) {
		var svg = this.root.getElementById(id);
		if(svg) {
			return new Terrascape.MapDisplay.AdobeSvg3.Layer(id, this, svg);
		}
	},

	getLayerVisibility: function(layer) {
		var v = this.getLayer(layer).svg.getAttribute("visibility");
		return v == "visible" || v == "";
	},

	setLayerVisibility: function(layer, visible) {
		this.getLayer(layer).svg.setAttribute("visibility", visible ? "visible" : "hidden");
	},

	createLayer: function(id) {
		var layer = this.svg.createElement("g");
		layer.setAttribute("id", id);
		this.map.appendChild(layer);
		return new Terrascape.MapDisplay.AdobeSvg3.Layer(id, this, layer);
	},

	removeLayer: function(layer) {
	},

	clearLayer: function(layer) {
		this.getLayer(layer).clear();
	},

	//Point conversions
	containerPointToMap: function(p) {
		var p0 = this.getViewBoxOrigin();
		var ratio = this.getViewBoxRatio();
		return new Point(p0.x + ratio * p.x, p0.y + ratio * p.y);
	},

	mapPointToContainer: function(p) {
		var p0 = mapController.getViewBoxOrigin();
		var ratio = mapController.getViewBoxRatio();
		return new Point(p0.x - ratio * p.x, p0.y - ratio * p.y);
	},

	containerPointToWorld: function(p) {
		var p0 = this.containerPointToMap(p);
		return new Point(p0.x + this.bounds.x, this.bounds.y - p0.y);
	},

	worldPointToContainer: function(p) {
		//TODO: Check this function
		return new Point(this.bounds.x - p.x, p.y + this.bounds.y);
	},

	//Mouse events
	_mouseDown: function(e) {
		if(this.handler) {
			this.handler.mouseDown(new Terrascape.MapDisplay.Event(e));
		}
	},

	_mouseMove: function(e) {
		if(this.handler) {
			this.handler.mouseMove(new Terrascape.MapDisplay.Event(e));
		}
	},

	_mouseUp: function(e) {
		if(this.handler) {
			this.handler.mouseUp(new Terrascape.MapDisplay.Event(e));
		}
	},

	//Viewbox methods
	_resetViewBox: function resetViewBox() {
		var currentViewBox = this.map.getAttribute("viewBox");
		var currentViewBoxCoords = currentViewBox.split(" ");
		var currentX = parseFloat(currentViewBoxCoords[0]);
		var currentY = parseFloat(currentViewBoxCoords[1]);
		var currentW = parseFloat(currentViewBoxCoords[2]);
		var currentH = parseFloat(currentViewBoxCoords[3]);
		var pixelW = currentW / this.object.offsetWidth;
		var pixelH = currentH / this.object.offsetHeight;
		var pixel = Math.max(pixelW, pixelH);
		var mapperW = this.object.offsetWidth * pixel;
		var mapperH = this.object.offsetHeight * pixel;
		var newX = currentX - (mapperW - currentW) / 2;
		var newY = currentY - (mapperH - currentH) / 2;
		var newViewBox = newX + " " + newY + " " + mapperW + " " + mapperH
		this.map.setAttribute("viewBox", newViewBox);
		this.root.setAttribute("viewBox", 0 + " " + 0 + " " + mapperW + " " + mapperH);
		var clip = this.svg.getElementById("__clipPathRect");
		clip.setAttribute("x", newX - 100);
		clip.setAttribute("y", newY - 100);
		clip.setAttribute("width", mapperW + 200);
		clip.setAttribute("height", mapperH + 200);
	},

	getViewBoxOrigin: function() {
		this._resetViewBox();
		var currentViewBox = this.map.getAttribute("viewBox");
		var currentViewBoxCoords = currentViewBox.split(" ");
		var currentX = parseFloat(currentViewBoxCoords[0]);
		var currentY = parseFloat(currentViewBoxCoords[1]);
		return new Point(currentX, currentY);
	},

	getViewBox: function() {
		this._resetViewBox();
		var currentViewBox = this.map.getAttribute("viewBox");
		var currentViewBoxCoords = currentViewBox.split(" ");
		var currentX = parseFloat(currentViewBoxCoords[0]);
		var currentY = parseFloat(currentViewBoxCoords[1]);
		var currentW = parseFloat(currentViewBoxCoords[2]);
		var currentH = parseFloat(currentViewBoxCoords[3]);
		return new Rect(currentX, currentY, currentW, currentH);
	},

	getViewBoxRatio: function() {
		var p = this.getViewBoxRatios();
		return Math.max(p.x, p.y);
	},

	getViewBoxRatios: function() {
		this._resetViewBox();
		var currentViewBox = this.map.getAttribute("viewBox");
		var currentViewBoxCoords = currentViewBox.split(" ");
		var currentX = parseFloat(currentViewBoxCoords[0]);
		var currentY = parseFloat(currentViewBoxCoords[1]);
		var currentW = parseFloat(currentViewBoxCoords[2]);
		var currentH = parseFloat(currentViewBoxCoords[3]);
		var pixelW = currentW / this.object.offsetWidth;
		var pixelH = currentH / this.object.offsetHeight;
		return new Point(pixelW, pixelH);
	},

	getViewBoxCenter: function() {
		this._resetViewBox();
		var currentViewBox = this.map.getAttribute("viewBox");
		var currentViewBoxCoords = currentViewBox.split(" ");
		var currentX = parseFloat(currentViewBoxCoords[0]);
		var currentY = parseFloat(currentViewBoxCoords[1]);
		var currentW = parseFloat(currentViewBoxCoords[2]);
		var currentH = parseFloat(currentViewBoxCoords[3]);
		return new Point(currentX + currentW / 2, currentY + currentH / 2);
	},

	//TODO: Remove addSvg and abstract
	svgns: "http://www.w3.org/2000/svg",
	xlinkns: "http://www.w3.org/1999/xlink",
	addSvg: function(parent, data) {
		if(data.childNodes.length == 0)	{
			return;
		}
		if(!this.svgData) {
			this.svgData = data;
			this.svgParent = parent;
			this.svgIndex = 0;
			setTimeout(this.addSvgWorker.bind(this), 1);
		} else {
			if(!this.svgDataQueue) {
				this.svgDataQueue = [];
				this.svgQueueIndex = 0;
			}
			this.svgDataQueue.add({ parent: parent, data: data });
		}
	},
	addSvgWorker: function() {
		if(this.svgIndex < this.svgData.childNodes.length) {
			this.addSvgChild(this.svgParent, this.svgData.childNodes[this.svgIndex]);
			++this.svgIndex;
			setTimeout(this.addSvgWorker.bind(this), 1);
		} else {
			this.svgIndex = 0;
			if(this.svgDataQueue && this.svgQueueIndex < this.svgDataQueue.length) {
				var svgQueuedItem = this.svgDataQueue[this.svgQueueIndex];
				this.svgQueueIndex++;
				this.svgData = svgQueuedItem.data;
				this.svgParent = svgQueuedItem.parent;
				setTimeout(this.addSvgWorker.bind(this), 1);
			} else {
				this.svgDataQueue = null;
				this.svgQueueIndex = 0;
				this.svgData = null;
				this.svgParent = null;
			}
		}
	},
	addSvgChildren: function(parent, data) {
		for(var i = 0; i < data.childNodes.length; ++i) 	{
			this.addSvgChild(parent, data.childNodes[i]);
		}
	},
	addSvgChild: function(parent, child) {
		var svgElement = this.addSvgFeature(parent, child);
		if(child.nodeName != "#text") {
			this.addSvgChildren(svgElement, child);
		}
	},
	addSvgFeature: function(parent, child) {
		var svgElement = this.svg.createElement(child.nodeName);
		if(child.childNodes.length == 1 && child.childNodes[0].nodeName == "#text") {
			var text = this.svg.createTextNode(child.text);
			svgElement.appendChild(text);
		}
		if(child.attributes) {
			for(var ii=0; ii < child.attributes.length; ++ii) {
				if(child.attributes[ii].name.indexOf("xlink") == -1) {
					svgElement.setAttribute(child.attributes[ii].name, child.attributes[ii].value);
				} else {
					svgElement.setAttributeNS(this.xlinkns, child.attributes[ii].name, child.attributes[ii].value);
				}
			}
		}
		parent.appendChild(svgElement);
		return svgElement;
	}
});

Terrascape.MapDisplay.AdobeSvg3.Layer = Class.extend(Terrascape.MapDisplay.Layer, {
	initialize: function(id, mapDisplay, svg) {
		this.parent(id, mapDisplay);
		this.svg = svg;
	},
	getAttribute: function(name) {
		return this.svg.getAttribute(name);
	},
	setAttribute: function(name, value) {
		this.svg.setAttribute(name, value);
	},
	getShapeCount: function() {
		return this.svg.childNodes.length;
	},
	getShapeAt: function(index) {
		return this.mapDisplay._shapeFromSwf(this.svg.childNodes.item(index));
	},
	getShape: function(id) {
		return this.mapDisplay.getShape(id);
	},
	getShapes: function() {
		if(this.layerData) {
			var layerData = this.layerData;
		} else {
			var layerData = Terrascape.MapData.getLayer(this.id);
		}
		if(!layerData) {
			layerData = new Terrascape.Layer(this.id);
		}
		if(!layerData.points) {
			layerData.points = [];
		}
		this.layerData = layerData;
		var shapes = [];
		var child = this.svg.firstChild;
		while(child) {
	        if(child.nodeName != "#text") {
				var V = layerData.points[child.id];
				if(!V) {
					V = makeSvgPointsFromFeature(child);
					layerData.points[child.id] = V;
				}
				shapes.add(this.mapDisplay._shapeFromSwf(child, V));
			}
			child = child.nextSibling;
		}
		return shapes;
	},
	containsShape: function(idOrShape) {
		if(typeof(idOrShape) == "string") {
			return this.mapDisplay.document.getElementById(idOrShape) != null;
		} else {
			return this.mapDisplay.document.getElementById(idOrShape.id) != null;
		}
	},
	clear: function() {
		var child = this.svg.firstChild;
		while(child) {
			var n = child.nextSibling;
			this.svg.removeChild(child);
			child = n;
		}
	}
});

Terrascape.MapDisplay.AdobeFlash9 = Class.extend(Terrascape.MapDisplay, {
	initialize: function(container, bounds) {
		this.parent(container, bounds);
		if(navigator.appName.indexOf("Microsoft") != -1) {
			this.swf = window[this.container.id];
		} else {
			this.swf = document[this.container.id];
		}
		eval("window." + this.container.id + "_mouseMove = this._mouseMove.bind(this)");
		eval("window." + this.container.id + "_mouseDown = this._mouseDown.bind(this)");
		eval("window." + this.container.id + "_mouseUp = this._mouseUp.bind(this)");

		this.swf.registerMouseEvents(
			this.swf.id + "_mouseMove",
			this.swf.id + "_mouseDown",
			this.swf.id + "_mouseUp"
		);

		//this._resetViewBox();
		this.extents = this.swf.getViewBox();
		eval("window." + this.container.id + "_mouseUp()");

	},

	//General methods
	loadCursor: function(name, url) {
		this.swf.loadCursor(name, url);
	},
	setCursor: function(name) {
		if(!this.disabledCursors) {
			this.swf.setCursor(name);
		}
	},
	//Control methods
	centerOnPoint: function(p) {
		var currentViewBox = this.swf.getAspectizedViewBox();
		var pixelW = currentViewBox.width / this.container.offsetWidth;
		var pixelH = currentViewBox.height / this.container.offsetHeight;
		var pixel = Math.max(pixelW, pixelH);
		var newX = currentViewBox.x - pixel * ((this.container.offsetWidth / 2) - p.x);
		var newY = currentViewBox.y - pixel * ((this.container.offsetHeight / 2) - p.y);
		var newW = currentViewBox.width;
		var newH = currentViewBox.height;
		var newRect = new Rect(newX, newY, newW, newH);
		this.swf.zoomTo(newRect);
		setTimeout(function() { this.swf.zoomTo(this.swf.getViewBox()); }.bind(this), 10);
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
		return newRect;
	},
	panBy: function(dx, dy) {
		this.swf.panBy(dx, dy);
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
	},
	panByPixels: function(dx, dy) {
		this.swf.panByPixels(dx, dy);
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
	},

	zoomToPoint: function(p) {
		//this.centerOnPoint(p);
		var currentViewBox = this.swf.getViewBox();
		var pixelW = currentViewBox.width / this.container.offsetWidth;
		var pixelH = currentViewBox.width / this.container.offsetHeight;
		var pixel = Math.max(pixelW, pixelH);
		var mapperW = this.container.offsetWidth * pixel;
		var mapperH = this.container.offsetHeight * pixel;
		var newW = currentViewBox.width / 2;
		var newH = currentViewBox.height / 2;
		//var newX = currentViewBox.x + newW / 2;
		//var newY = currentViewBox.y + newH / 2;
		var point = this.containerPointToMap(p);
		var newX = point.x - (newW / 2);
		var newY = point.y - (newH / 2);
		var r = new Rect(newX, newY, newW, newH);
		this.swf.zoomTo(r);
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
		return r;
	},

	zoomFromPoint: function(p) {
		//this.centerOnPoint(p);
		var currentViewBox = this.swf.getViewBox();
		var pixelW = currentViewBox.width / this.container.offsetWidth;
		var pixelH = currentViewBox.width / this.container.offsetHeight;
		var pixel = Math.max(pixelW, pixelH);
		var mapperW = this.container.offsetWidth * pixel;
		var mapperH = this.container.offsetHeight * pixel;
		var newW = currentViewBox.width * 5 / 3;
		var newH = currentViewBox.height * 5 / 3;
		//var newX = (currentViewBox.x - (newW - currentViewBox.width) / 2);
		//var newY = (currentViewBox.y - (newH - currentViewBox.height) / 2);
		var point = this.containerPointToMap(p);
		var newX = point.x - (newW / 2);
		var newY = point.y - (newH / 2);
		var r = new Rect(newX, newY, newW, newH);
		this.swf.zoomTo(r);
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
		return r;
	},

	zoomToRect: function(r) {
		this.swf.zoomTo(r);
		if(typeof(this.viewBoxChanged) == "function") {
			this.viewBoxChanged();
		}
		return r;
	},

	zoomToExtents: function() {
		this.zoomToRect(this.extents);
		return this.extents;
	},

	getShapeById: function(id) {
	},

	addShape: function(shape, layer) {
		//console.log("addShape", shape);
		if(layer) {
			this.swf.addFeature(shape, this.getLayer(layer).id);
		} else {
			this.swf.addFeature(shape);
		}
	},

	readdShape: function(shape, layer) {
		if(layer) {
			this.getLayer(layer).addShape(shape);
		} else {
			this.swf.addFeature(shape.swf);
		}
	},

	removeShape: function(shape, layer) {
		if(layer) {
			this.swf.removeFeature(shape.id, this.getLayer(layer).id);
		} else {
			this.swf.removeFeature(shape.id);
		}
	},

	applyShape: function(shape) {
		this.swf.applyFeature(shape);
	},

	getShape: function(id) {
		return new Terrascape.Shape(this.swf.getFeature(id));
	},

	getShapeFromPoint: function(layer, p) {

		var l = this.getLayer(layer);
		if(!l) return null;

		var s = this.swf.featureFromPoint(this.containerPointToMap(p), l.id);
		if(s) {
			return new Terrascape.Shape(s);
		}
		return null;
	},

	getShapesInRect: function(layer, r) {
		//TODO: getShapeInRect
		alert("getShapesInRect not implemented");
		/*var l = this.getLayer(layer);
		if(!l) return null;
		var p0 = new Point(r.x, r.y);
		var p1 = new Point(r.x + r.w, r.y + r.h);
		var layerData = Terrascape.MapData.getLayer(l.id);
		if(!layerData.points) {
			layerData.points = [];
		}
		var shapes = [];
		var child = l.svg.firstChild;
		while(child) {
			if(child.nodeName != "#text") {
				var bbox = child.getBBox();
				if(bbox.x == -1 || rectBoxIntersect(r, bbox)) {
					var V = layerData.points[child.id];
					if(!V) {
						V = makeSvgPointsFromFeature(child);
						layerData.points[child.id] = V;
					}
					if(V) {
						if(V[0] == V[V.length - 1]) {
							if(polyInRectFull(V, V.length - 1, p0, p1)) {
								shapes.add(this._shapeFromSwf(child, V));
							}
						}
						else {
							if(lineInRectFull(V, V.length - 1, p0, p1)) {
								shapes.add(this._shapeFromSwf(child, V));
							}
						}
					}
				}
			}
			child = child.nextSibling;
		}
		return shapes;*/
	},

	getShapesIntersectRect: function(layer, r) {
		//TODO: getShapesIntersectRect
		alert("getShapesIntersect not implemented");
		/*var l = this.getLayer(layer);
		if(!l) return null;
		var p0 = new Point(r.x, r.y);
		var p1 = new Point(r.x + r.w, r.y + r.h);
		var layerData = Terrascape.MapData.getLayer(l.id);
		if(!layerData.points) {
			layerData.points = [];
		}
		var child = l.svg.firstChild;
		var shapes = [];
		while(child) {
			if(child.nodeName != "#text") {
				var bbox = child.getBBox();
				if(bbox.x == -1 || rectBoxIntersect(r, bbox)) {
					var V = layerData.points[child.id];
					if(!V) {
						V = makeSvgPointsFromFeature(child);
						layerData.points[child.id] = V;
					}
					if(V) {
						if(V[0] == V[V.length - 1]) {
							if(polyInRectPartial(V, V.length - 1, p0, p1)) {
								shapes.add(this._shapeFromSwf(child, V));
							}
						}
						else {
							if(lineInRectPartial(V, V.length - 1, p0, p1)) {
								shapes.add(this._shapeFromSwf(child, V));
							}
						}
					}
				}
			}
			child = child.nextSibling;
		}
		return shapes;*/
	},

	getShapeBBox: function(shape) {
		return this.swf.getFeatureEnvelope(shape.id);
	},


	_shapeFromSwf: function(svg, points) {
		//TODO: _shapeFromSwf
		alert("_shapeFromSwf not implemented");
		/*
		var styles = this.getStyleArray(svg.getAttribute("style"));
		var attributes = {
			id: svg.getAttribute("id"),
			strokeColor: styles["stroke"] || svg.getAttribute("stroke"),
			strokeOpacity: styles["stroke-opacity"] || svg.getAttribute("stroke-opacity"),
			strokeWidth: styles["stroke-width"] || svg.getAttribute("stroke-width"),
			fillColor: styles["fill"] || svg.getAttribute("fill"),
			fillOpacity: styles["fill-opacity"] || svg.getAttribute("fill-opacity"),
			svg: svg
		};
		if(!points) {
			points = makeSvgPointsFromFeature(svg);
		}
		switch(svg.nodeName) {
			case "path": {
				attributes.points = points;
				if(points[0] == points[points.length - 1]) {
					return new Terrascape.Polygon(attributes);
				} else {
					return new Terrascape.LineString(attributes);
				}
			}
			case "rect": {
				attributes.x = points[0].x;
				attributes.y = points[0].y;
				attributes.width = points[2].x - attributes.x;
				attributes.height = points[2].y - attributes.y;
				return new Terrascape.Rect(attributes);
			}
			case "circle": {
				attributes.x = svg.getAttribute("cx");
				attributes.y = svg.getAttribute("cy");
				attributes.radius = svg.getAttribute("r");
				return new Terrascape.Point(attributes);
			}
			case "use": {
				//TODO: Make new Shape for "use"
				attributes.x = svg.getAttribute("x0");
				attributes.y = svg.getAttribute("y0");
				attributes.radius = svg.getAttribute("r0");
				return new Terrascape.Point(attributes);
			}
			case "image": {
				//TODO: Make new Shape for "image"
				attributes.x = points[0].x;
				attributes.y = points[0].y;
				attributes.width = points[2].x - attributes.x;
				attributes.height = points[2].y - attributes.y;
				return new Terrascape.Rect(attributes);
			}
			case "polygon": {
				attributes.points = points;
				return new Terrascape.Polygon(attributes);
			}
		}*/
	},

	//Layer methods
	getLayerById: function(id) {
		if(this.swf.layerExists(id)) {
			return new Terrascape.MapDisplay.AdobeFlash9.Layer(id, this);
		}
		return null;
	},

	getLayerVisibility: function(layer) {
		var l = this.getLayer(layer);
		if(l) {
			return this.swf.getLayerVisibility(l.id);
		}
		return false;
	},

	setLayerVisibility: function(layer, visible) {
		var l = this.getLayer(layer);
		if(l) {
			this.swf.setLayerVisibility(l.id, visible);
		}
	},

	createLayer: function(id) {
		return new Terrascape.MapDisplay.AdobeFlash9.Layer(id, this, this.swf.createLayer(id));
	},

	removeLayer: function(layer) {
		//TODO: removeLayer
		alert("removeLayer is not implemented");
		//NOTE: not implemented in SVG either
	},

	clearLayer: function(layer) {
		this.getLayer(layer).clear();
	},

	//Point conversions
	containerPointToMap: function(p) {
		var p0 = this.getViewBoxOrigin();
		var ratio = this.getViewBoxRatio();
		return new Point(p0.x + ratio * p.x, p0.y + ratio * p.y);
	},

	mapPointToContainer: function(p) {
		//var p0 = mapController.getViewBoxOrigin();
		//var ratio = mapController.getViewBoxRatio();
		//return new Point(p0.x - ratio * p.x, p0.y - ratio * p.y);

		//Herman:
		var p0 = this.getViewBoxOrigin();
		var ratio = this.getViewBoxRatio();
        return new Point((p.x - p0.x) / ratio, (p.y - p0.y) / ratio);
	},

	containerPointToWorld: function(p) {
		var p0 = this.containerPointToMap(p);
		return new Point(p0.x + this.bounds.x, this.bounds.y - p0.y);
	},

	worldPointToContainer: function(p) {
		//TODO: Check this function
		return new Point(this.bounds.x - p.x, p.y + this.bounds.y);
	},

	//(Herman
	aspectizeEnvelope: function(e) {
		var r = new Object();
		r = this.swf.aspectizeEnvelope(e);
		return r;

	},
	//Herman)

	//Mouse events
	_mouseDown: function(e) {
		if(this.handler) {
			this.handler.mouseDown(new Terrascape.MapDisplay.Event(e));
		}
	},

	_mouseMove: function(e) {
		if(this.handler) {
			this.handler.mouseMove(new Terrascape.MapDisplay.Event(e));
		}
	},

	_mouseUp: function(e) {
		if(this.handler) {
			this.handler.mouseUp(new Terrascape.MapDisplay.Event(e));
		}
	},

	//Viewbox methods
	_resetViewBox: function resetViewBox() {
		return;
		//TODO: _resetViewbox
		/*
		var pixel = this.getViewBoxRatio();
		var mapperW = this.container.offsetWidth * pixel;
		var mapperH = this.container.offsetHeight * pixel;
		var newX = currentX - (mapperW - currentW) / 2;
		var newY = currentY - (mapperH - currentH) / 2;
		*/
		//var newViewBox = newX + " " + newY + " " + mapperW + " " + mapperH
		//this.map.setAttribute("viewBox", newViewBox);
		//this.root.setAttribute("viewBox", 0 + " " + 0 + " " + mapperW + " " + mapperH);
	},

	getViewBoxOrigin: function() {
		var currentViewBox = this.swf.getAspectizedViewBox();
		return new Point(currentViewBox.x, currentViewBox.y);
	},

	getViewBox: function() {
		return this.swf.getAspectizedViewBox();
	},

	getViewBoxRatio: function() {
		var p = this.getViewBoxRatios();
		return Math.max(p.x, p.y);
	},

	getViewBoxRatios: function() {
		this._resetViewBox();
		var currentViewBox = this.swf.getAspectizedViewBox();
		var currentX = currentViewBox.x;
		var currentY = currentViewBox.y;
		var currentW = currentViewBox.width;
		var currentH = currentViewBox.height;
		var pixelW = currentW / this.container.offsetWidth;
		var pixelH = currentH / this.container.offsetHeight;
		return new Point(pixelW, pixelH);
	},

	getViewBoxCenter: function() {
		var currentViewBox = this.swf.getAspectizedViewBox();
		return new Point(currentViewBox.x + currentViewBox.w / 2, currentViewBox.y + currentViewBox.h / 2);
	},

	//TODO: Remove addSvg and abstract
	svgns: "http://www.w3.org/2000/svg",
	xlinkns: "http://www.w3.org/1999/xlink",
	addSvg: function(parent, data) {
		if(data.childNodes.length == 0)	{
			return;
		}
		if(!this.svgData) {
			this.svgData = data;
			this.svgParent = parent;
			this.svgIndex = 0;
			setTimeout(this.addSvgWorker.bind(this), 1);
		} else {
			if(!this.svgDataQueue) {
				this.svgDataQueue = [];
				this.svgQueueIndex = 0;
			}
			this.svgDataQueue.add({ parent: parent, data: data });
		}
	},
	addSvgWorker: function() {
		if(this.svgIndex < this.svgData.childNodes.length) {
			this.addSvgChild(this.svgParent, this.svgData.childNodes[this.svgIndex]);
			++this.svgIndex;
			setTimeout(this.addSvgWorker.bind(this), 1);
		} else {
			this.svgIndex = 0;
			if(this.svgDataQueue && this.svgQueueIndex < this.svgDataQueue.length) {
				var svgQueuedItem = this.svgDataQueue[this.svgQueueIndex];
				this.svgQueueIndex++;
				this.svgData = svgQueuedItem.data;
				this.svgParent = svgQueuedItem.parent;
				setTimeout(this.addSvgWorker.bind(this), 1);
			} else {
				this.svgDataQueue = null;
				this.svgQueueIndex = 0;
				this.svgData = null;
				this.svgParent = null;
			}
		}
	},
	addSvgChildren: function(parent, data) {
		for(var i = 0; i < data.childNodes.length; ++i) 	{
			this.addSvgChild(parent, data.childNodes[i]);
		}
	},
	addSvgChild: function(parent, child) {
		var svgElement = this.addSvgFeature(parent, child);
		if(child.nodeName != "#text") {
			this.addSvgChildren(svgElement, child);
		}
	},
	addSvgFeature: function(parent, child) {
		var svgElement = this.svg.createElement(child.nodeName);
		if(child.childNodes.length == 1 && child.childNodes[0].nodeName == "#text") {
			var text = this.svg.createTextNode(child.text);
			svgElement.appendChild(text);
		}
		if(child.attributes) {
			for(var ii=0; ii < child.attributes.length; ++ii) {
				if(child.attributes[ii].name.indexOf("xlink") == -1) {
					svgElement.setAttribute(child.attributes[ii].name, child.attributes[ii].value);
				} else {
					svgElement.setAttributeNS(this.xlinkns, child.attributes[ii].name, child.attributes[ii].value);
				}
			}
		}
		parent.appendChild(svgElement);
		return svgElement;
	}
});

Terrascape.MapDisplay.AdobeFlash9.Layer = Class.extend(Terrascape.MapDisplay.Layer, {
	initialize: function(id, mapDisplay) {
		this.parent(id, mapDisplay);
	},
	getAttribute: function(name) {
		return;
		//return this.swf.getAttribute(name);
	},
	setAttribute: function(name, value) {
		return;
		//this.svg.setAttribute(name, value);
	},
	getShapeCount: function() {
		return this.mapDisplay.swf.getLayerFeatureCount(this.id);
	},
	getShapeAt: function(index) {
		return this.mapDisplay._shapeFromSwf(this.swf.getFeatureIdFromIndex(this.id, index), this);
	},
	getShape: function(id) {
		return this.mapDisplay.getShape(id, this);
	},
	getShapes: function() {
		var features = this.mapDisplay.swf.getLayerFeatures(this.id);
		var shapes = [];
		$A(features).each(function(feature) {
			shapes.push(new Terrascape.Shape(feature));
		});
		return shapes;
	},
	containsShape: function(idOrShape) {
		if(typeof(idOrShape) == "string") {
			return this.mapDisplay.swf.layerContainsFeature(this.id, idOrShape);
		} else {
			return this.mapDisplay.swf.layerContainsFeature(this.id, idOrShape.id);
		}
	},
	clear: function() {
		this.mapDisplay.swf.clearLayer(this.id);
	}
});

Terrascape.Shape = Class.create();
Terrascape.Shape.prototype = {
	initialize: function(attributes) {
		Object.extend(this, {
			id: "anonymous" + Terrascape.Shape.anonymousId++,
			type: "shape",
			x: 0,
			y: 0,
			visible: true,
			strokeColor: "black",
			strokeOpacity: 1,
			strokeWidth: 1,
			fillColor: "white",
			fillOpacity: 1
		});
		if(attributes) {
			Object.extend(this, attributes);
		}
	},
	clone: function() {
		return new Terrascape.Shape(this);
	},
	//TODO: Fix for abstraction
	getAttribute: function(name) {
		//return this.svg.getAttribute(name);
	},
	setAttribute: function(name, value) {
		//this.svg.setAttribute(name, value);
	}
};
Terrascape.Shape.anonymousId = 0;

Terrascape.Point = Class.extend(Terrascape.Shape, {
	initialize: function(attributes) {
		attributes.type = "point";
		this.parent(Object.extend( { radius: "1" }, attributes));
	}
});

Terrascape.Line = Class.extend(Terrascape.Shape, {
	initialize: function(attributes) {
		attributes.type = "line";
		this.parent(Object.extend( { x1: 0, y1: 0 }, attributes));
	}
});

Terrascape.LineString = Class.extend(Terrascape.Shape, {
	initialize: function(attributes) {
		attributes.type = "lineString";
		this.parent(Object.extend( { points: [] }, attributes));
	}
});

Terrascape.Rect = Class.extend(Terrascape.Shape, {
	initialize: function(attributes) {
		attributes.type = "rect";
		this.parent(Object.extend( { width: 0, height: 0 }, attributes));
	},
	toRect: function() {
		return new Rect(this.x, this.y, this.width, this.height);
	}
});

Terrascape.RoundRect = Class.extend(Terrascape.Rect, {
    initialize: function (attributes) {
        this.parent(Object.extend( {ellipseWidth:0, ellipseHeight: 0}, attributes));
        this.type = "roundRect";
    }
});

Terrascape.Polygon = Class.extend(Terrascape.Shape, {
	initialize: function(attributes) {
		attributes.type = "polygon";
		this.parent(Object.extend( { points: [] }, attributes));
	}
});

Terrascape.Label = Class.extend(Terrascape.Shape, {
	initialize: function(attributes) {
		attributes.type = "label";
		this.parent(Object.extend( { text: "" }, attributes));
	}
});

Terrascape.Image = Class.extend(Terrascape.Shape, {
	initialize: function(attributes) {
		attributes.type = "image";
		this.parent(Object.extend( { src: "", width: 0, height: 0 }, attributes));
	}
});

