Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/GS3MapLibrary.gwt.xml
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/GS3MapLibrary.gwt.xml	(revision 23890)
+++ 	(revision )
@@ -1,10 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.1//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.7.1/distro-source/core/src/gwt-module.dtd">
-<module rename-to='gs3maplibrary'>
-	<inherits name="com.google.gwt.user.User" />
-	<inherits name="com.google.gwt.maps.GoogleMaps" />
-	<inherits name='com.google.gwt.user.theme.standard.Standard'/>
-	<inherits name="com.google.gwt.http.HTTP"/>
-  	<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=false&amp;key=ABQIAAAAtgBCR-EMOIox9GYORrWZ4xT2yXp_ZAY8_ufC3CFXhHIE1NvwkxTdn97J3Y4bgIWO98Jw2xGa7SsjVQ"/>
-  	<entry-point class='org.greenstone.client.GS3MapLibrary'/>
-</module>
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/GS3MapLibrary.gwt.xml
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/GS3MapLibrary.gwt.xml	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/GS3MapLibrary.gwt.xml	(revision 23906)
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.1//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.7.1/distro-source/core/src/gwt-module.dtd">
+<module rename-to='gs3maplibrary'>
+	<inherits name="com.google.gwt.user.User" />
+	<inherits name="com.google.gwt.maps.GoogleMaps" />
+	<inherits name='com.google.gwt.user.theme.standard.Standard'/>
+	<inherits name="com.google.gwt.http.HTTP"/>
+	<inherits name="com.google.gwt.xml.XML" />
+	<!--<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=false&amp;key=ABQIAAAAtgBCR-EMOIox9GYORrWZ4xT20kFGbybagIuCnxcJa_GNd7_aLBSMK71WBmKu2gYDZKoonO56bPhAKQ"/>-->
+  	<!--<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=false&amp;key=ABQIAAAAtgBCR-EMOIox9GYORrWZ4xT2yXp_ZAY8_ufC3CFXhHIE1NvwkxTdn97J3Y4bgIWO98Jw2xGa7SsjVQ"/>-->
+  	<!--<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=false&amp;key=ABQIAAAAtgBCR-EMOIox9GYORrWZ4xRDDXBaPmHH9_0fCH2SRcUU9bjcjhT3cD_2Hv84s1XjkWPxzMb0RT2hQQ"/>-->
+  	<entry-point class='org.greenstone.client.GS3MapLibrary'/>
+</module>
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/FindPlaceService.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/FindPlaceService.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/FindPlaceService.java	(revision 23906)
@@ -0,0 +1,21 @@
+package org.greenstone.client;
+
+import java.util.ArrayList;
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+@RemoteServiceRelativePath("findPlace")
+public interface FindPlaceService extends RemoteService 
+{
+	Boolean isGazetteerLoaded();
+	Boolean findPlacesInText(String text, String html);
+	ArrayList<ArrayList<Place>> findPlacesInMultipleTexts(String[] texts);
+	Boolean loadGazetteer();
+	ArrayList<Place> getPlaces(double maxScore, double minScore, long minPopulation, boolean locality, boolean region, boolean country, int numberOfPlacesToGet);
+	Boolean adjustScoringParams(double penalty, double parentBonus, double indirectReferencePercent);
+	String getMarkedUpText();
+	ArrayList<Place> updateGazetteer(int start, int end);
+	Boolean addToDatabase(Place p, float lat, float lng);
+	ArrayList<String> spatialSearch(ArrayList<Float[]> points);
+	Place getPlaceById(long id);
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/FindPlaceServiceAsync.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/FindPlaceServiceAsync.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/FindPlaceServiceAsync.java	(revision 23906)
@@ -0,0 +1,20 @@
+package org.greenstone.client;
+
+import java.util.ArrayList;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+public interface FindPlaceServiceAsync 
+{
+	void getMarkedUpText(AsyncCallback<String> callback);
+	void isGazetteerLoaded(AsyncCallback<Boolean> callback);
+	void findPlacesInText(String text, String html, AsyncCallback<Boolean> callback);
+	void loadGazetteer(AsyncCallback<Boolean> callback);
+	void getPlaces(double maxScore, double minScore, long minPopulation, boolean locality, boolean region, boolean country, int numberOfPlacesToGet, AsyncCallback<ArrayList<Place>> callback);
+	void adjustScoringParams(double penalty, double parentBonus, double indirectReferencePercent, AsyncCallback<Boolean> callback);
+	void findPlacesInMultipleTexts(String[] texts, AsyncCallback<ArrayList<ArrayList<Place>>> callback);
+	void updateGazetteer(int start, int end,  AsyncCallback<ArrayList<Place>> callback);
+	void addToDatabase(Place p, float lat, float lng, AsyncCallback<Boolean> callback);
+	void spatialSearch(ArrayList<Float[]> points, AsyncCallback<ArrayList<String>> callback);
+	void getPlaceById(long id, AsyncCallback<Place> callback);
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/GS3MapLibrary.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/GS3MapLibrary.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/GS3MapLibrary.java	(revision 23906)
@@ -0,0 +1,2939 @@
+package org.greenstone.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.MouseMoveEvent;
+import com.google.gwt.event.dom.client.MouseMoveHandler;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.dom.client.MouseOverHandler;
+import com.google.gwt.event.dom.client.MouseUpEvent;
+import com.google.gwt.event.dom.client.MouseUpHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.maps.client.InfoWindow;
+import com.google.gwt.maps.client.InfoWindowContent;
+import com.google.gwt.maps.client.MapWidget;
+import com.google.gwt.maps.client.control.ControlAnchor;
+import com.google.gwt.maps.client.control.ControlPosition;
+import com.google.gwt.maps.client.control.LargeMapControl;
+import com.google.gwt.maps.client.control.MapTypeControl;
+import com.google.gwt.maps.client.control.Control.CustomControl;
+import com.google.gwt.maps.client.event.MapClickHandler;
+import com.google.gwt.maps.client.event.MapMoveHandler;
+import com.google.gwt.maps.client.event.PolygonClickHandler;
+import com.google.gwt.maps.client.geocode.Geocoder;
+import com.google.gwt.maps.client.geocode.LatLngCallback;
+import com.google.gwt.maps.client.geom.LatLng;
+import com.google.gwt.maps.client.overlay.Marker;
+import com.google.gwt.maps.client.overlay.MarkerOptions;
+import com.google.gwt.maps.client.overlay.Polygon;
+import com.google.gwt.maps.client.overlay.Polyline;
+import com.google.gwt.maps.client.overlay.PolylineOptions;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.ServiceDefTarget;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.FormPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Hidden;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.xml.client.NamedNodeMap;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.FormElement;
+import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Node;
+import com.google.gwt.dom.client.NodeCollection;
+import com.google.gwt.dom.client.NodeList;
+import com.google.gwt.dom.client.OptionElement;
+import com.google.gwt.dom.client.SelectElement;
+import com.google.gwt.dom.client.TextAreaElement;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class GS3MapLibrary implements EntryPoint
+{
+	protected MapWidget _map;
+
+	protected VerticalPanel _containerPanel = new VerticalPanel();
+	protected VerticalPanel _scoreAdjustPanel = new VerticalPanel();
+	protected VerticalPanel _headerDiv = new VerticalPanel();
+	protected HorizontalPanel _viewPanel = new HorizontalPanel();
+
+	protected StatusBar _statusBar = null;
+
+	protected HTML _gsPanel = new HTML();
+	protected HTML _columnViewBox = new HTML();
+
+	protected HorizontalPanel _contentDiv = new HorizontalPanel();
+	protected HorizontalPanel _footerDiv = new HorizontalPanel();
+
+	protected Menu _currentDocumentMenu = null;
+	protected Menu _lastCreatedPlaceMenu = null;
+
+	protected FlowPanel _radioPanel = new FlowPanel();
+	protected FlowPanel _checkBoxPanel = new FlowPanel();
+	
+	protected Label _loadingBox = new Label("");
+	protected Label _mapLoadingLabel = new Label("Loading map content");
+
+	protected ListBox _textViewSelector = new ListBox();
+
+	protected Button _geoQueryButton = new Button("Specify Search Square");
+
+	protected Timer _loadingTimer = null;
+	protected Timer _resizeCheckTimer = null;
+	protected Timer _scrollCheckTimer = null;
+	protected Timer _placeMouseOverTimer = null;
+	protected Timer _documentMouseOverTimer = null;
+	protected Timer _menuOverTimer = null;
+	protected Timer _menuOutTimer = null;
+
+	protected Geocoder _geocoder = null;
+
+	protected static FindPlaceServiceAsync _findPlaceService = GWT.create(FindPlaceService.class);
+
+	protected ArrayList<Place> _currentPlaces = null;
+	protected ArrayList<String> _highlightedPlaces = new ArrayList<String>();
+	protected ArrayList<String> _highlightedTextPlaces = new ArrayList<String>();
+	protected ArrayList<Boolean> _visiblePlaceSets = new ArrayList<Boolean>();
+	protected ArrayList<LatLng> _areaPoints = new ArrayList<LatLng>();
+	protected ArrayList<String> _removedPlaces = new ArrayList<String>();
+	
+	protected ArrayList<ArrayList<Place>> _currentMultiPlaces = null;
+	
+	protected HashMap<String, Place> _chosenPlaces = new HashMap<String, Place>();	
+
+	protected HandlerRegistration _fishEyeHandlerReg = null;
+	protected HandlerRegistration _columnViewHandlerReg = null;
+
+	protected MapMoveHandler _mapMoveHandler = null;
+	protected MapClickHandler _spatialSearchClickHandler = null;
+	protected SpatialSearchControls _searchControls = null;
+
+	protected final int _MAXVISIBLEMARKERS = 500;
+	protected int _currentZoomLevel = 2;
+	protected int _loadingCount = 0;
+	protected int _currentBrowserPixelWidth = 0;
+	protected int _currentBrowserPixelHeight = 0;
+	protected int _currentScrollTopValue = 0;
+
+	protected boolean _updatingMap = false;
+	protected boolean _gazetteerLoaded = false;
+	protected boolean _redraw = false;
+	protected boolean _resize = false;
+	protected boolean _mapVisible = false;
+	protected boolean _gsVisible = true;
+	protected boolean _frameLoaded = false;
+	protected boolean _initialised = false;
+	protected boolean _showAllPlaces = false;
+	protected boolean _mouseEventPause = false;
+	protected boolean _mapMoveHandlerOn = true;
+	protected boolean _searchResultsPage = false;
+
+	protected String _selectedPlaceName = "";
+	protected String _currentCollection = "";
+	protected String _currentURL = "";
+	protected final String _GREENSTONEURL = "http://taniwha.resnet.scms.waikato.ac.nz:8080/greenstone3";
+	//protected final String _GREENSTONEURL = "http://www.nzdl.org/atlasgreenstone3";
+	protected final String _GREENSTONEDEVURL = _GREENSTONEURL + "/dev";
+
+	protected com.google.gwt.xml.client.Document _currentNewPageDoc;
+
+	/**
+	 * GWT module entry point
+	 */
+	public void onModuleLoad()
+	{
+		_statusBar = new StatusBar(this);
+		//Make the Firebug console so that browsers that don't have it don't break
+		setUpConsole();
+		
+		//Get a Google geocoder to help find places that the Gazetteer does not know the coordinates for
+		_geocoder = new Geocoder();
+
+		((ServiceDefTarget) _findPlaceService).setServiceEntryPoint("http://taniwha.resnet.scms.waikato.ac.nz:8080/ATLAS/findPlace");
+		//((ServiceDefTarget) _findPlaceService).setServiceEntryPoint("http://www.nzdl.org/ATLAS/findPlace");
+		
+		//Setup native Javascript functions
+		setUpLoadPageFromUrl(this);
+		setUpLoadPageFromForm(this);
+		setUpLoadSpatialSearchPage(this);
+		
+		//Setup the map
+		setUpMap();
+		
+		//Set up the list box that stores the text view options
+		setUpTextViewSelector();
+
+		//Load the Greenstone home page
+		loadPageFromUrl("http://taniwha.resnet.scms.waikato.ac.nz:8080/greenstone3/dev?a=p&sa=home");
+	}
+
+	/**
+	 * Adds the three options to the text view selector
+	 */
+	public void setUpTextViewSelector()
+	{
+		_textViewSelector.addItem("Normal");
+		_textViewSelector.addItem("Fisheye");
+		_textViewSelector.addItem("Column");
+	}
+	
+	/**
+	 * Loads the page at the given URL
+	 * @param url is the url to load
+	 */
+	public void loadPageFromUrl(String url)
+	{
+		//Make sure a url was given 
+		if(url == null)
+		{
+			return;
+		}
+		
+		//Save the given url for later use
+		_currentURL = url;
+
+		//Set Greenstone to be visible
+		_gsVisible = true;
+		
+		//Clear the map
+		_map.clearOverlays();
+
+		updateHandlers();
+		clearStatusUpdates();
+
+		//Check if the page being loaded is a search page 
+		if (url.contains("s1.query="))
+		{
+			//Load the search page
+			loadSearchPageFromUrl(url);	
+		}
+		else
+		{
+			//This page is not a search page
+			_searchResultsPage = false;
+		}
+
+		//If the url contains a collection name
+		if (url.contains("&c="))
+		{
+			//Store the collection name
+			_currentCollection = getCollectionFromURL(url);
+		}
+
+		//Add the "Page loading" update to the status bar"		
+		_statusBar.addUpdate("Loading new page", "LoadPage");
+		
+		getNewPage(url);
+	}
+	
+	public void loadPageWithoutUrl()
+	{
+		//Set the current url to be empty
+		_currentURL = "";
+		
+		//Set Greenstone to be visible
+		_gsVisible = true;
+		
+		//Clear the map
+		_map.clearOverlays();
+
+		updateHandlers();
+		clearStatusUpdates();
+
+		//Add the "Page loading" update to the status bar"		
+		_statusBar.addUpdate("Loading new page", "LoadPage");
+	}
+	
+	public void getNewPage(String url)
+	{
+		RequestBuilder newPageRequest = new RequestBuilder(RequestBuilder.GET, url.contains("?") ? (url + "&excerpttag=body") : (url + "?excerpttag=body"));
+		try{
+			newPageRequest.sendRequest(null, new RequestCallback()
+			{
+				public void onResponseReceived(Request request, Response response)
+				{
+					if(response.getStatusCode() == Response.SC_OK)
+					{
+						HTML newPage = new HTML(response.getText());
+						newPage.getElement().setId("newpage");
+						Element oldNewPage = (Element) Document.get().getElementById("newpage");
+						if(oldNewPage != null)
+						{
+							oldNewPage.removeFromParent();
+						}
+						getElementsByTagName("body").get(0).appendChild(newPage.getElement());
+						newPage.setVisible(false);
+
+						showLoadedPage();
+					}
+				}
+				
+				public void onError(Request request, Throwable exception)
+				{
+					alert("Problem loading new page -> " + exception.getMessage());
+				}
+			});
+		}
+		catch(Exception ex)
+		{
+			alert("Exception while creating the new page request -> " + ex.getMessage());
+		}
+	}
+	
+	/**
+	 * Clears several of the status updates that should not persist across pages
+	 */
+	public void clearStatusUpdates()
+	{
+		_statusBar.removeUpdate("DisPlaces");
+		_statusBar.removeUpdate("SearchLoad");
+		_statusBar.removeUpdate("TextSearch");
+	}
+	
+	/**
+	 * Gets a collection name from a given url
+	 * @param url the url to search for a collection in
+	 * @return the collection name
+	 */
+	public String getCollectionFromURL(String url)
+	{
+		int start = (url.indexOf("&c=") + 3);
+		int end = -1;
+		if (url.indexOf("&", (url.indexOf("&c=") + 1)) == -1)
+		{
+			end = url.length();
+		}
+		else
+		{
+			end = url.indexOf("&", (url.indexOf("&c=") + 1));
+		}
+		return url.substring(start, end);
+	}
+	
+	/**
+	 * Sets up various handlers for a new page
+	 */
+	public void updateHandlers()
+	{
+		if (_fishEyeHandlerReg != null)
+		{
+			_fishEyeHandlerReg.removeHandler();
+			_fishEyeHandlerReg = null;
+		}
+
+		if (!_mapMoveHandlerOn)
+		{
+			_mapMoveHandlerOn = true;
+			_map.addMapMoveHandler(_mapMoveHandler);
+		}
+
+		if (_searchControls != null)
+		{
+			_map.removeControl(_searchControls);
+			_searchControls = null;
+		}
+
+		if (_spatialSearchClickHandler != null)
+		{
+			_map.removeMapClickHandler(_spatialSearchClickHandler);
+			_spatialSearchClickHandler = null;
+		}
+	}
+	
+	/**
+	 * Moves the loaded page from the temporary frame to the main page
+	 */
+	private void showLoadedPage()
+	{	
+		//If ATLAS has not yet been initialised then do so
+		if (!_initialised)
+		{
+			//Fix webpage elements so that they point to the correct address
+			fixForms();
+			fixAnchors(); 
+			fixImages();
+			
+			initialiseATLAS();
+			
+			//Add <link> and <script> elements to <head>
+			//changeCSSLinksAndScripts();
+		}
+		else
+		{
+			//Add <link> and <script> elements to <head>
+			//changeCSSLinksAndScripts();
+
+			//Fix webpage elements so that they point to the correct address
+			fixForms();
+			fixAnchors(); 
+			fixImages(); 
+			
+			Element newPageElem = getElementById("newpage");
+			NodeList<com.google.gwt.dom.client.Element> divs = newPageElem.getElementsByTagName("div");
+			
+			Element newBanner = null;
+			Element newContent = null;
+			
+			for(int i = 0; i < divs.getLength(); i++)
+			{
+				if(divs.getItem(i).getId().equals("gs_banner"))
+				{
+					newBanner = (Element) divs.getItem(i);
+				}
+				else if(divs.getItem(i).getId().equals("gs_content"))
+				{
+					newContent = (Element) divs.getItem(i);
+				}
+			}
+			
+			//Swap the current banner with the new banner
+			Element oldBanner = getElementById("gs_banner");
+			oldBanner.getParentElement().removeChild(oldBanner);
+			_headerDiv.insert(HTML.wrap(newBanner), 0);
+
+			//If the content div was in column view mode then reset it
+			if (_contentDiv.getWidget(0).getElement().getId().equals("columnBox"))
+			{
+				_contentDiv.clear();
+				_contentDiv.add(_gsPanel);
+				_contentDiv.add(_map);
+			}
+
+			//Swap the current Greenstone content with the new Greenstone content
+			_gsPanel.setHTML(HTML.wrap(newContent).getHTML());
+		}
+
+		//If the page is a collection home page then add spatial search to the list of options
+		if (_currentURL.contains("sa=about"))
+		{
+			addSpatialSearchLinkToAboutPage();
+		}
+
+		//The page has now finished being updated
+		_statusBar.removeUpdate("LoadPage");
+
+		//Check the document's text for places
+		checkDocText();
+	}
+	
+	/**
+	 * Add a link to spatial searching on the current page
+	 */
+	private void addSpatialSearchLinkToAboutPage()
+	{
+		//Get the list to add the link to
+		Element list = getElementById("servicelist");
+
+		//If the list does not exist then stop
+		if(list == null)
+		{
+			return;
+		}
+		
+		//Create the div for the link and explanation
+		Element linkLabel = DOM.createDiv();
+		list.appendChild(linkLabel);
+		linkLabel.setClassName("paramLabel");
+
+		//Create the link and append it to the div
+		Element spatialLink = DOM.createAnchor();
+		linkLabel.appendChild(spatialLink);
+		spatialLink.setAttribute("href", "javascript:loadSpatialSearchPageJS()");
+		spatialLink.setInnerText("Spatial Search");
+
+		//Create the explanation div and add to the parent div
+		Element explainationDiv = DOM.createDiv();
+		list.appendChild(explainationDiv);
+		explainationDiv.setClassName("paramLabel");
+		explainationDiv.setInnerText("Search for documents mentioning places from a certain area");
+
+		Element br = DOM.createElement("br");
+		list.appendChild(br);
+		br.setClassName("clear");
+	}
+	
+	/**
+	 * Initialise the webpage elements for ATLAS
+	 */
+	public void initialiseATLAS()
+	{		
+		//Reload the page when the view type is changed
+		_textViewSelector.addChangeHandler(new ChangeHandler()
+		{
+			public void onChange(ChangeEvent event)
+			{
+				if (getDocTextElement() != null)
+				{
+					loadPageFromUrl(_currentURL);
+				}
+			}
+		});
+
+		//Set up content
+		_containerPanel.add(_headerDiv);
+		_containerPanel.add(_statusBar.getStatusBarDiv());
+		_containerPanel.add(_contentDiv);
+		_containerPanel.add(_footerDiv);
+
+		_viewPanel.add(new Label("View: "));
+		_viewPanel.add(_textViewSelector);
+		_viewPanel.setVisible(false);
+		
+		Element newPageElem = getElementById("newpage");
+		NodeList<com.google.gwt.dom.client.Element> divs = newPageElem.getElementsByTagName("div");
+		
+		Element headerElem = null;
+		Element bodyElem = null;
+		Element footerElem = null;
+		
+		for(int i = 0; i < divs.getLength(); i++)
+		{
+			if(divs.getItem(i).getId().equals("gs_banner"))
+			{
+				headerElem = (Element) divs.getItem(i);
+			}
+			else if(divs.getItem(i).getId().equals("gs_content"))
+			{
+				bodyElem = (Element) divs.getItem(i);
+			}
+			else if(divs.getItem(i).getId().equals("gs_footer"))
+			{
+				footerElem = (Element) divs.getItem(i);
+			}
+		}
+		
+		if(headerElem == null)
+		{
+			alert("header is null");
+		}
+		if(bodyElem == null)
+		{
+			alert("body is null");
+		}	
+		if(footerElem == null)
+		{
+			alert("footer is null");
+		}
+
+		// Add header
+		//Node newHeader = importNode(newPage, tempFrameGetElementById("banner"), true);
+		_headerDiv.add(HTML.wrap(headerElem));
+		_headerDiv.add(_viewPanel);
+		_headerDiv.setWidth("100%");
+		_headerDiv.getElement().setId("GSHeader");
+
+		_gsPanel.getElement().getStyle().setProperty("overflow", "auto");
+
+		_contentDiv.setWidth("100%");
+		_contentDiv.getElement().getStyle().setProperty("backgroundColor", "#FFFFFF");
+		_contentDiv.getElement().setId("GSContent");
+
+		// Add content
+		//Node newContent = importNode(Document.get(), tempFrameGetElementById("content"), true);
+		_gsPanel = HTML.wrap(bodyElem);
+		_contentDiv.add(_gsPanel);
+		_contentDiv.add(_map);
+
+		// Add footer
+		//Node newFooter = importNode(Document.get(), tempFrameGetElementById("footer"), true);
+		_footerDiv.add(HTML.wrap(footerElem));
+		_footerDiv.setWidth("100%");
+		_footerDiv.getElement().setId("GSFooter");
+
+		RootPanel.get("mainFrame").add(_containerPanel);
+
+		setUpBrowserResizeCheckTimer();
+
+		// Make the call to check if the Gazetteer is loaded
+		_findPlaceService.isGazetteerLoaded(setUpCheckGazetteerCallback());
+		_initialised = true;
+	}
+
+	/**
+	 * Loads a Greenstone search page from a url
+	 * @param url is the url to load
+	 */
+	public void loadSearchPageFromUrl(String url)
+	{
+		_searchResultsPage = true;
+
+		//Set up the request to get returned documents from the xml
+		RequestBuilder xmlRequest = new RequestBuilder(RequestBuilder.GET, url);
+		try
+		{
+			//Make the request
+			xmlRequest.sendRequest(null, new RequestCallback()
+			{
+				public void onError(Request request, Throwable exception)
+				{
+					logToConsole("xml Request Error -> " + exception.getMessage());
+				}
+
+				public void onResponseReceived(Request request, Response response)
+				{
+					processResponse(response);
+				}
+			});
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+	
+	public void processResponse(Response response)
+	{
+		//If the status code is not the page loaded code then stop
+		if(response.getStatusCode() != 200)
+		{
+			return;
+		}
+		
+		//Add the status update that indicates that the system is searching for places in the result document
+		_statusBar.addUpdate("Finding places in result documents", "SearchLoad");
+
+		//Get the response text
+		String text = response.getText();
+		
+		//Get the hash codes for the documents
+		final ArrayList<String> docURLs = getDocURLsFromResponse(text);		
+		final String[] texts = new String[docURLs.size()];
+		
+		for(int i = 0; i < texts.length; i++)
+		{
+			texts[i] = null;
+		}
+		
+		for(int i = 0; i < docURLs.size(); i++)
+		{
+			String url = docURLs.get(i);
+			RequestBuilder xmlRequest = new RequestBuilder(RequestBuilder.GET, _GREENSTONEURL + "/" + (url.contains("?") ? (url + "&excerptid=gs-document-text") : (url + "?excerptid=gs-document-text")));
+			try
+			{
+				final int index = i;
+				xmlRequest.sendRequest(null, new RequestCallback()
+				{
+					public void onResponseReceived(Request request, Response response)
+					{
+						if(response.getStatusCode() == Response.SC_OK)
+						{
+							texts[index] = response.getText();
+						}
+					}
+					
+					public void onError(Request request, Throwable exception)
+					{
+						alert("Exception requesting new page -> " + exception.getMessage());
+						texts[index] =  "";
+					}
+				});
+			}
+			catch(Exception ex)
+			{
+				alert("Exception requesting " + url + " -> " + ex.getMessage());
+			}
+		}
+		
+		Timer checkDoneTimer = new Timer()
+		{
+			int _count = 0;
+			
+			public void run()
+			{
+				boolean done = true;
+				if(_count < 20)
+				{
+					for(int i = 0; i < texts.length; i++)
+					{
+						if(texts[i] == null)
+						{
+							done = false;
+							break;
+						}
+					}
+				}
+				_count++;
+				
+				if(done)
+				{
+					this.cancel();
+					findPlacesInTexts(texts);
+				}
+			}
+		};
+		checkDoneTimer.scheduleRepeating(500);
+	}
+	
+	/**
+	 * Makes a call to the server to look through multiple places for places
+	 * @param texts is a list of texts from different documents
+	 */
+	private void findPlacesInTexts(String[] texts)
+	{
+		logToConsole("Finished gettings doc text and gazetteer loaded!");
+
+		//Make the call to the server that finds the places
+		_findPlaceService.findPlacesInMultipleTexts(texts, setUpFindMultiplePlacesCallback());
+
+		//Set up the map for viewing multiple places
+		_map.clearOverlays();
+		_mapVisible = true;
+		_map.setVisible(true);
+		_contentDiv.add(_mapLoadingLabel);
+
+		_mapLoadingLabel.getElement().getStyle().setProperty("left", ((int) (_headerDiv.getOffsetWidth() * (3.0 / 4.0) - _mapLoadingLabel.getOffsetWidth() / 2)) + "px");
+		_mapLoadingLabel.getElement().getStyle().setProperty("top", (_headerDiv.getOffsetHeight() + _contentDiv.getOffsetHeight() / 3) + "px");
+
+		_map.getElement().getStyle().setProperty("left", (Window.getClientWidth() / 2 + 8) + "px");
+		_map.getElement().getStyle().setProperty("top", (_headerDiv.getOffsetHeight() + 28 + Window.getScrollTop()) + "px");
+
+		_resize = true;
+	}
+	
+	/**
+	 * Takes Greenstone search response xml text and extracts the document hashes from it
+	 * @param text is the xml to use
+	 * @return a list of document hashes
+	 */
+	private ArrayList<String> getDocURLsFromResponse(String text)
+	{
+		ArrayList<String> docURLs = new ArrayList<String>();
+
+		int index = text.indexOf("<ul id=\"results\"");
+		int endIndex = text.indexOf("</ul>", index);
+		while ((index = text.indexOf("<a href=\"", index)) != -1 && index < endIndex)
+		{
+			int urlStart = index + "<a href=\"".length();
+			int urlEnd = text.indexOf("\"", urlStart + 1);
+
+			docURLs.add(text.substring(urlStart, urlEnd));
+			
+			index++;
+		}
+		
+		return docURLs;
+	}
+
+	/**
+	 * Creates and shows a spatial search page
+	 */
+	public void loadSpatialSearchPage()
+	{
+		//Clear the Greenstone panel
+		_gsPanel.setHTML((new HTML()).getHTML());
+
+		//Turn off the map move handler if it is on
+		if (_mapMoveHandlerOn)
+		{
+			_mapMoveHandlerOn = false;
+			_map.removeMapMoveHandler(_mapMoveHandler);
+		}
+
+		//Set up the page
+		_map.clearOverlays();
+		_mapVisible = true;
+		_gsVisible = false;
+		_map.setVisible(true);
+
+		//Create a new list that will store the points that the user clicks on
+		_areaPoints = new ArrayList<LatLng>();
+
+		//If the map does not currently have a spatial search handler
+		if (_spatialSearchClickHandler == null)
+		{
+			//Create a new map spatial search handler
+			_spatialSearchClickHandler = new MapClickHandler()
+			{
+				public void onClick(MapClickEvent event)
+				{
+					//Take the point that has been clicked and add it to the list
+					LatLng point = event.getLatLng();
+					_areaPoints.add(point);
+
+					//Create a marker at the clicked point
+					MarkerOptions mo = MarkerOptions.newInstance();
+					mo.setClickable(false);
+					Marker marker = new Marker(point, mo);
+					_map.addOverlay(marker);
+
+					//If this is not the first point then add a line between this and the previous point
+					if (_areaPoints.size() > 1)
+					{
+						PolylineOptions po = PolylineOptions.newInstance();
+						po.setClickable(false);
+
+						Polyline line = new Polyline(new LatLng[] { point, _areaPoints.get(_areaPoints.size() - 2) }, "#FFFFFF", 3, 1, po);
+						_map.addOverlay(line);
+					}
+				}
+			};
+		}
+		_map.addMapClickHandler(_spatialSearchClickHandler);
+
+		//If the map does not have search controls then add them
+		if (_searchControls == null)
+		{
+			_searchControls = new SpatialSearchControls(new ControlPosition(ControlAnchor.BOTTOM_RIGHT, 0, 0));
+		}
+		_map.addControl(_searchControls);
+
+		_resize = true;
+	}
+
+	/**
+	 * Takes an HTML form element and loads a page using its content
+	 * @param form is the form to use
+	 */
+	public void loadPageFromForm(Element form)
+	{
+		//Get the top level url
+		StringBuilder url = new StringBuilder(_GREENSTONEDEVURL);
+		
+		//Get the elements from the form
+		FormElement formElement = FormElement.as(form);
+		NodeCollection<com.google.gwt.dom.client.Element> elements = formElement.getElements();
+
+		//Go through each element
+		for (int i = 0; i < elements.getLength(); i++)
+		{
+			Element currentItem = (Element) elements.getItem(i);
+
+			//If the element is an input element
+			if (currentItem.getTagName().equals("INPUT"))
+			{
+				InputElement input = InputElement.as(currentItem);
+				
+				//If the element is a text or hidden element
+				if (input.getType().equals("text") || input.getType().equals("hidden"))
+				{
+					//Append the name and value of this element to the url
+					url.append("&" + input.getName() + "=" + input.getValue());
+				}
+				//If the element is a radio or checkbox element
+				else if (input.getType().equals("radio") || input.getType().equals("checkbox"))
+				{
+					//If the element has been checked
+					if (input.isChecked())
+					{
+						//Append the name and value of this element to the url
+						url.append("&" + input.getName() + "=" + input.getAttribute("value"));
+					}
+				}
+			}
+			//If the element is a select element (drop down box)
+			else if (currentItem.getTagName().equals("SELECT"))
+			{
+				SelectElement select = SelectElement.as(currentItem);
+				NodeList<OptionElement> options = select.getOptions();
+
+				//Append the name of the element and the value of the currently selected index to the url
+				url.append("&" + select.getName() + "=" + options.getItem(select.getSelectedIndex()).getAttribute("value"));
+			}
+			//If the element is a text area element 
+			else if (currentItem.getTagName().equals("TEXTAREA"))
+			{
+				TextAreaElement text = TextAreaElement.as(currentItem);
+				
+				//Append the name and value of the text area to the url
+				url.append("&" + text.getName() + "=" + text.getValue());
+			}
+		}
+		//Replace the first & with a ?
+		url.replace(url.indexOf("&"), url.indexOf("&") + 1, "?");
+		
+		//Load the page with the created url
+		loadPageFromUrl(url.toString());
+	}
+
+	/**
+	 * Remove old CSS links from the page and add new ones, the href attributes are also corrected
+	 */
+	public void changeCSSLinksAndScripts()
+	{		
+		//Get the head section of the HTML page
+		Element head = getDocumentHead();
+
+		//Get all of the <link> elements from the HTML header 
+		NodeList<com.google.gwt.dom.client.Element> currentLinks = head.getElementsByTagName("link");
+		
+		//Get all of the <script> elements from the HTML header 
+		NodeList<com.google.gwt.dom.client.Element> currentScripts = head.getElementsByTagName("script");
+
+		//Add the new links
+		com.google.gwt.xml.client.NodeList newLinks = _currentNewPageDoc.getElementsByTagName("link");	
+		for (int i = 0; i < newLinks.getLength(); i++)
+		{
+			com.google.gwt.xml.client.Element e = (com.google.gwt.xml.client.Element) newLinks.item(i);
+			boolean found = false;
+			for(int j = 0; j < currentLinks.getLength(); j++)
+			{
+				Element elem = (Element) currentLinks.getItem(j);
+				if(e.getAttribute("href").equals(elem.getAttribute("href"))
+				&& e.getAttribute("type").equals(elem.getAttribute("type"))
+				&& e.getAttribute("rel").equals(elem.getAttribute("rel")))
+				{
+					found = true;
+				}
+			}
+			
+			if(!found)
+			{
+				Element newElem = DOM.createElement("link");
+				
+				NamedNodeMap map = e.getAttributes();
+				for(int k = 0; k < e.getAttributes().getLength(); k++)
+				{
+					newElem.setAttribute(map.item(k).getNodeName(), map.item(k).getNodeValue());
+				}
+				
+				if(!newElem.getAttribute("href").startsWith("http://"))
+				{
+					newElem.setAttribute("href", _GREENSTONEURL + "/" + newElem.getAttribute("href"));	
+				}
+				head.appendChild(newElem);
+			}
+		}
+		
+		//Add the new scripts on the page
+		com.google.gwt.xml.client.NodeList newScripts = _currentNewPageDoc.getElementsByTagName("script");	
+		for (int i = 0; i < newScripts.getLength(); i++)
+		{
+			com.google.gwt.xml.client.Element e = (com.google.gwt.xml.client.Element) newScripts.item(i);
+			boolean found = false;
+			for(int j = 0; j < currentScripts.getLength(); j++)
+			{
+				Element elem = (Element) currentLinks.getItem(j);
+				if(e.getAttribute("src").equals(elem.getAttribute("src")) 
+				&& e.getAttribute("type").equals(elem.getAttribute("type"))
+				&& e.getAttribute("charset").equals(elem.getAttribute("charset")))
+				{
+					found = true;
+				}
+			}
+			
+			if(!found)
+			{
+				Element newElem = DOM.createElement("link");
+				
+				NamedNodeMap map = e.getAttributes();
+				for(int k = 0; k < e.getAttributes().getLength(); k++)
+				{
+					newElem.setAttribute(map.item(k).getNodeName(), map.item(k).getNodeValue());
+				}
+				
+				
+				if(!e.getAttribute("src").startsWith("http://"))
+				{
+					e.setAttribute("src", _GREENSTONEURL + "/" + e.getAttribute("src"));
+				}
+				head.appendChild(newElem);
+			}
+		}
+	}
+
+	/**
+	 * Go through each form on the page and add a javascript function in place of its action attribute
+	 */
+	public void fixForms()
+	{
+		NodeList<com.google.gwt.dom.client.Element> forms = getElementById("newpage").getElementsByTagName("form");
+		for (int i = 0; i < forms.getLength(); i++)
+		{
+			Element e = (Element)forms.getItem(i);
+			e.setAttribute("id", "form" + i);
+
+			if(e.getAttribute("action").isEmpty())
+			{
+				continue;
+			}
+			if (!e.getAttribute("action").startsWith("http://") && !e.getAttribute("action").startsWith("/"))
+			{
+				e.setAttribute("action", "javascript:loadPageFromFormJS(document.getElementById('form" + i + "'))");
+			}
+		}
+	}
+
+	/**
+	 * Go through each anchor on the page and add a javascript function in place of the href attribute
+	 */
+	public void fixAnchors()
+	{
+		NodeList<com.google.gwt.dom.client.Element> anchors = getElementById("newpage").getElementsByTagName("a");
+		for (int i = 0; i < anchors.getLength(); i++)
+		{
+			Element e = (Element)anchors.getItem(i);
+			if(e.getAttribute("href").isEmpty())
+			{
+				continue;
+			}
+			if (!e.getAttribute("href").startsWith("http://") && !e.getAttribute("href").startsWith("/"))
+			{
+				e.setAttribute("href", _GREENSTONEURL + "/" + e.getAttribute("href"));
+			}
+			//e.setAttribute("onClick", "loadPageFromUrlJS('" + e.getAttribute("href") + "'); return false;");
+			e.setAttribute("href", "javascript:loadPageFromUrlJS('" + e.getAttribute("href") + "');");
+		}
+	}
+
+	/**
+	 * Go through each image on the page and correct its src attribute
+	 */
+	public void fixImages()
+	{
+		NodeList<com.google.gwt.dom.client.Element> images = getElementById("newpage").getElementsByTagName("img");
+		for (int i = 0; i < images.getLength(); i++)
+		{
+			Element e = (Element)images.getItem(i);
+			
+			if(e.getAttribute("src").isEmpty())
+			{
+				continue;
+			}
+			
+			if (!e.getAttribute("src").startsWith("http://") && !e.getAttribute("src").startsWith("/"))
+			{
+				e.setAttribute("src", _GREENSTONEURL + "/" + e.getAttribute("src"));
+			}
+		}
+	}
+
+	/**
+	 * Makes calls to console.log harmless on browsers that do not have Firebug
+	 */
+	public static native void setUpConsole()
+	/*-{
+		if (! ("console" in $wnd) || !("firebug" in console)) 
+		{
+			var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
+			$wnd.console = {};
+			for (var i = 0; i < names.length; ++i) $wnd.console[names[i]] = function() {};
+		}
+	}-*/;
+
+	/**
+	 * Creates the javascript function that calls the loadPageFromForm GWT function 
+	 * @param ml is the instance of the GS3MapLibrary class that you wish to attach this function to
+	 */
+	public static native void setUpLoadPageFromForm(GS3MapLibrary ml)
+	/*-{
+		$wnd.loadPageFromFormJS = function(form)
+		{
+			ml.@org.greenstone.client.GS3MapLibrary::loadPageFromForm(Lcom/google/gwt/user/client/Element;) (form);
+		};
+	}-*/;
+
+	/**
+	 * Creates the javascript function that calls the loadPageFromURL GWT function 
+	 * @param ml is the instance of the GS3MapLibrary class that you wish to attach this function to
+	 */
+	public static native void setUpLoadPageFromUrl(GS3MapLibrary ml)
+	/*-{
+		$wnd.loadPageFromUrlJS = function(url)
+		{
+			ml.@org.greenstone.client.GS3MapLibrary::loadPageFromUrl(Ljava/lang/String;) (url);
+		};
+	}-*/;
+
+	/**
+	 * Creates the javascript function that calls the loadSpatialSearchPage GWT function 
+	 * @param ml is the instance of the GS3MapLibrary class that you wish to attach this function to
+	 */
+	public static native void setUpLoadSpatialSearchPage(GS3MapLibrary ml)
+	/*-{
+		$wnd.loadSpatialSearchPageJS = function()
+		{
+			ml.@org.greenstone.client.GS3MapLibrary::loadSpatialSearchPage() ();
+		};
+	}-*/;
+
+	/**
+	 * Gets all of the specified type of element in the given frame
+	 * @param frameIndex is the index of the frame to search in
+	 * @param tagname is the specific tag type to get
+	 * @return the list of elements with that tag name
+	 */
+	public static native JsArray<Element> tempFrameGetElementsByTagName(int frameIndex, String tagname)
+	/*-{
+		return $wnd.frames[frameIndex].document.getElementsByTagName(tagname);
+	}-*/;
+
+	/**
+	 * Gets a element specifed by a given id in the specified frame
+	 * @param frameIndex is the index of the frame to search in
+	 * @param id is the id of the element to find
+	 * @return the specified element
+	 */
+	public static native Element tempFrameGetElementById(int frameIndex, String id)
+	/*-{
+		return $wnd.frames[frameIndex].document.getElementById(id);
+	}-*/;
+
+	/**
+	 * Gets the total number of frames in the webpage
+	 * @return the total number of frames
+	 */
+	public static native int getNumberOfFrames()
+	/*-{
+		return $wnd.frames.length;
+	}-*/;
+
+	/**
+	 * Gets an element specified the given id in the intermediate frame
+	 * @param id is the id of the element to find
+	 * @return the specified element
+	 */
+	public static native Element tempFrameGetElementById(String id)
+	/*-{
+		return $wnd.frames[2].document.getElementById(id);
+	}-*/;
+
+	/**
+	 * Gets all of the specified type of element in the intermediate frame
+	 * @param tagname is the specific tag type to get
+	 * @return the list of elements with that tag name
+	 */
+	public static native JsArray<Element> tempFrameGetElementsByTagName(String tagname)
+	/*-{
+		return $wnd.frames[2].document.getElementsByTagName(tagname);
+	}-*/;
+
+	/**
+	 * Prints a given message to screen in a window
+	 * @param msg is the message to print
+	 */
+	public static native void alert(String msg)
+	/*-{
+		alert(msg);
+	}-*/;
+	
+	/**
+	 * Prints a given message to the Firebug console
+	 * @param msg is the message to print
+	 */
+	public static native void logToConsole(String msg)
+	/*-{
+		//alert(msg);
+		//console.log(msg);
+	}-*/;
+
+	/**
+	 * Gets the <head> element of the <html>
+	 * @return the head element
+	 */
+	public static native Element getDocumentHead()
+	/*-{
+		return $doc.getElementsByTagName("head")[0];
+	}-*/;
+	
+    /**
+     * Patch GWT Document.importNode, which fails to return the imported nodes
+     */
+    public static final native Node importNode(Document doc, Node node, boolean deep) /*-{
+            return doc.importNode(node, deep);
+    }-*/;
+
+
+	/**
+	 * Gets an element specified by the given id in the document
+	 * @param id the id of the element to find
+	 * @return the specified element
+	 */
+	public static native Element getElementById(String id)
+	/*-{
+		return $doc.getElementById(id);
+	}-*/;
+
+	/**
+	 * Gets all of the elements specified by the given tag name
+	 * @param tagName is the tag name of the elements to find
+	 * @return the list of elements specified by the given tag name
+	 */
+	public static native JsArray<Element> getElementsByTagName(String tagName)
+	/*-{
+		return $doc.getElementsByTagName(tagName);
+	}-*/;
+
+	/**
+	 * Calls the method the initialises the fisheye viewing
+	 */
+	public static native void setUpFishEye()
+	/*-{
+		console.log("Fish eye set up");
+		$wnd.fishEyeSetUp();
+	}-*/;
+
+	/**
+	 * Passes the mouse y coordinates from the GWT handler to the fisheye handler
+	 * @param y is the mouse y coordinate
+	 */
+	public static native void passMouseCoordsToFishEye(int y)
+	/*-{
+		$wnd.checkMousePos(y);
+	}-*/;
+
+	/**
+	 * Sets up the timer that checks periodically whether or not the elements in the page need to be resized
+	 */
+	public void setUpBrowserResizeCheckTimer()
+	{
+		_resizeCheckTimer = new Timer()
+		{
+			public void run()
+			{
+				if (_currentBrowserPixelHeight != Window.getClientHeight() || _currentBrowserPixelWidth != Window.getClientWidth() || _resize)
+				{
+					_resize = false;
+					resizeElements();
+					_currentBrowserPixelHeight = Window.getClientHeight();
+					_currentBrowserPixelWidth = Window.getClientWidth();
+				}
+			}
+		};
+		_resizeCheckTimer.scheduleRepeating(500);
+	}
+
+	/**
+	 * Resizes elements based on the current width and height of the browser window
+	 */
+	public void resizeElements()
+	{
+		//Get the width and height we want to use for the content div
+		int cwidth = Window.getClientWidth() - 45;
+		int cheight = (int) (Window.getClientHeight() - _headerDiv.getOffsetHeight() - _footerDiv.getOffsetHeight() - _statusBar.getStatusBarDiv().getOffsetHeight() - 50);
+
+		//If the height is less than 300 then set it to 300 
+		if (cheight < 200)
+		{
+			cheight = 200;
+		}
+
+		//Set the widths of the header and footer divs
+		_headerDiv.setWidth(cwidth + "px");
+		_footerDiv.setWidth(cwidth + "px");
+		
+		//Set the size of the content div
+		_contentDiv.setSize(cwidth + "px", cheight + "px");
+		
+		//Set up the style of the Greenstone panel
+		_gsPanel.getElement().getStyle().setProperty("position", "absolute");
+		_gsPanel.getElement().getStyle().setProperty("overflow", "auto");
+		
+		//Set up the style of the Map panel
+		_map.getElement().getStyle().setProperty("position", "absolute");
+		_map.getElement().getStyle().setProperty("top", _contentDiv.getAbsoluteTop() + "px");
+
+		//If the map is visible and the Greenstone panel is visible
+		if (_mapVisible && _gsVisible)
+		{
+			_map.setSize((cwidth / 2 - 60) + "px", (cheight - 25) + "px");
+			_map.getElement().getStyle().setProperty("left", (cwidth / 2 + 50) + "px");
+			_gsPanel.setSize((cwidth / 2) + "px", (cheight - 25) + "px");
+		}
+
+		//If the map is visible and the Greenstone panel is not visible
+		if (_mapVisible && !_gsVisible)
+		{
+			_gsPanel.setSize("0px", "0px");
+			_map.setSize((cwidth - 40) + "px", cheight + "px");
+			_map.getElement().getStyle().setProperty("left", _gsPanel.getAbsoluteLeft() + "px");
+		}
+
+		//If the map is not visible and the Greenstone panel is visible
+		if (!_mapVisible && _gsVisible)
+		{
+			_gsPanel.setSize(cwidth + "px", cheight + "px");
+			_map.setSize("0px", "0px");
+		}
+
+		//If neither panel is visible
+		if (!_mapVisible && !_gsVisible)
+		{
+			_gsPanel.setSize("0px", "0px");
+			_map.setSize("0px", "0px");
+		}
+	}
+
+	/**
+	 * Initialises the Google Map view
+	 */
+	public void setUpMap()
+	{
+		//Set up the map size, style, starting point and zoom level
+		LatLng startPoint = LatLng.newInstance(0, 0);
+		_map = new MapWidget(startPoint, _currentZoomLevel);
+		_map.setSize(Window.getClientWidth() / 2 + "px", "600px");
+		_map.getElement().getStyle().setProperty("margin", "5px");
+		_map.setStyleName("map");
+
+		//Store the default map move handler
+		_mapMoveHandler = new MapMoveHandler()
+		{
+			public void onMove(MapMoveEvent event)
+			{
+				if (_map.getZoomLevel() != _currentZoomLevel)
+				{
+					_currentZoomLevel = _map.getZoomLevel();
+					_map.clearOverlays();
+					
+					//If this is not a search page then there is only one place set
+					if(!_searchResultsPage)
+					{
+						addPlacesToMap(_currentPlaces, "#FFFFFF", "#00FF00", _searchResultsPage);
+					}
+					//If this is a search page then there are likely to be multiple place sets
+					else
+					{
+						displayMultiplePlaceSets();
+					}
+				}
+			}
+		};
+		_map.addMapMoveHandler(_mapMoveHandler);
+
+		// Add some controls for the zoom level
+		_map.addControl(new LargeMapControl());
+		_map.addControl(new MapTypeControl());
+
+		//Add the map loading label
+		_mapLoadingLabel.setStyleName("mapLoadingLabel");
+		_mapLoadingLabel.getElement().getStyle().setProperty("left", ((int) (_headerDiv.getOffsetWidth() * (3.0 / 4.0) - _mapLoadingLabel.getOffsetWidth() / 2)) + "px");
+		_mapLoadingLabel.getElement().getStyle().setProperty("top", (_headerDiv.getOffsetHeight() + _contentDiv.getOffsetHeight() / 3) + "px");
+
+		//Make the map initially invisible
+		_map.setVisible(false);
+	}
+
+	/**
+	 * Creates a marker at the given point
+	 * @param p is the place to create a marker for
+	 * @param point is the point to place the marker at (if this is null the place will be used to provide the coordinates)
+	 * @param borderColour is the colour of the border to use
+	 * @param fillColour is the colour to use to fill the marker
+	 * @param colourInfo stores whether or not to show a coloured rectangle in the information box that is shown when the marker is clicked
+	 * @return the marker
+	 */
+	public Polygon createMarkerAtPoint(Place p, LatLng point, String borderColour, String fillColour, Boolean colourInfo)
+	{
+		float lat = 0.0f;
+		float lng = 0.0f;
+
+		//Get the coordinates
+		if (point == null)
+		{
+			lat = p.getLatitude();
+			lng = p.getLongitude();
+		}
+		else
+		{
+			lat = (float) point.getLatitude();
+			lng = (float) point.getLongitude();
+		}
+
+		LatLng newPoint = ((point == null) ? LatLng.newInstance(lat, lng) : point);
+
+		//Work out the size of the new marker based on the current zoom level
+		double zoomLevel = _map.getZoomLevel();
+		double size = (8.0 * Math.pow(2.0, -zoomLevel)) + (0.05 * (zoomLevel / 20.0));
+		
+		//Work out the transparency of the new marker based on the current zoom level
+		double transparency = 0.8 * ((20.0 - zoomLevel) / 20.0);
+		
+		//Create the corner of the polygon
+		LatLng tl = LatLng.newInstance(lat - size, lng + size);
+		LatLng tr = LatLng.newInstance(lat + size, lng + size);
+		LatLng br = LatLng.newInstance(lat + size, lng - size);
+		LatLng bl = LatLng.newInstance(lat - size, lng - size);
+
+		//If the user has chosen to highlight this place then make the marker red
+		Polygon newMarker = null;
+		if (_highlightedPlaces.contains(p.getName()))
+		{
+			newMarker = new Polygon(new LatLng[] { tl, tr, br, bl, tl }, borderColour, 1, 1, "#FF7F27", transparency);
+		}
+		//Otherwise make it the normal colour
+		else
+		{
+			newMarker = new Polygon(new LatLng[] { tl, tr, br, bl, tl }, borderColour, 1, 1, fillColour, transparency);
+		}
+
+		// When the marker is clicked it will show its address in a info box
+		newMarker.addPolygonClickHandler(new MarkerClickHandler(fillColour, p, newPoint, colourInfo));
+
+		return newMarker;
+	}
+
+	/**
+	 * Shows an info box above the marker when the marker is clicked
+	 */
+	public class MarkerClickHandler implements PolygonClickHandler
+	{
+		String _fillColour = "";
+		Boolean _colourInfo = false;
+		LatLng _point = null;
+		Place _place = null;
+
+		public MarkerClickHandler(String fillColour, Place place, LatLng point, Boolean colourInfo)
+		{
+			_fillColour = fillColour;
+			_place = place;
+			_point = point;
+			_colourInfo = colourInfo;
+		}
+
+		public void onClick(PolygonClickEvent event)
+		{
+			InfoWindow info = _map.getInfoWindow();
+			if (_colourInfo)
+			{
+				info.open(_point, new InfoWindowContent(createPlaceInformationString(_place)));
+			}
+			else
+			{
+				info.open(_point, new InfoWindowContent("<span style=\"background: " + _fillColour + "; color: " + _fillColour + ";\">...</span> " + createPlaceInformationString(_place)));
+			}
+		}
+	}
+
+	/**
+	 * Takes a place object and creates a string with information about that place
+	 * @param p is the place to make an information string from
+	 * @return the information string
+	 */
+	public String createPlaceInformationString(Place p)
+	{
+		//If we do not have a valid place then stop
+		if(p == null)
+		{
+			return null;
+		}
+		
+		//Add the place name to the string
+		StringBuilder s = new StringBuilder(p.getName());
+		
+		//Add the full parent name to the string
+		if (p.getParentPlaceName() != null)
+		{
+			s.append(" in " + p.getParentPlaceName());
+		}
+
+		//If we have population information then add it to the string
+		if (p.getPopulation() > 0)
+		{
+			StringBuilder pop = new StringBuilder(Long.toString(p.getPopulation()));
+
+			//Insert commas into long numbers
+			for (int i = pop.length() - 3; i > 0; i -= 3)
+			{
+				pop.insert(i, ',');
+			}
+
+			s.append("<br/>Population: " + pop);
+		}
+
+		//If we have coordinates then add those to the string as well
+		if (p.getLongitude() != null && p.getLatitude() != null)
+		{
+			StringBuilder lat = new StringBuilder(Float.toString(p.getLatitude()));
+			StringBuilder lng = new StringBuilder(Float.toString(p.getLongitude()));
+
+			//Shorten coordinates to 3 decimal places
+			int i = 0;
+			while (lat.charAt(i++) != '.');
+			int j = 0;
+			while (lng.charAt(j++) != '.');
+
+			if ((i + 3) < lat.length())
+			{
+				lat.delete(i + 3, lat.length());
+			}
+
+			if ((j + 3) < lng.length())
+			{
+				lng.delete(j + 3, lng.length());
+			}
+
+			s.append("<br/>Coordinates: " + lat + ", " + lng);
+		}
+
+		return s.toString();
+	}
+
+	/**
+	 * Make a call to the server that gets places that match the given criteria
+	 */
+	public void updateCurrentPlaces()
+	{
+		_findPlaceService.getPlaces(0, 100, 0, true, true, true, _MAXVISIBLEMARKERS, setUpGetPlacesCallback());
+	}
+
+	/**
+	 * Displays all of the place sets created from a text search
+	 */
+	public void displayMultiplePlaceSets()
+	{
+		//Clear the map
+		_map.clearOverlays();
+
+		//Go through each place set and if it has been marked visible then add it
+		for (int j = 0; j < _currentMultiPlaces.size(); j++)
+		{
+			logToConsole("Adding set " + j + " to the map");
+			if (_visiblePlaceSets.get(j))
+			{
+				addPlacesToMap(_currentMultiPlaces.get(j), "#FFFFFF", numToColour(j, _currentMultiPlaces.size(), 100, 255), true);
+			}
+		}
+		_statusBar.removeUpdate("DisPlaces");
+	}
+
+	/**
+	 * Creates a colour from the given parameters
+	 * @param num is the specified index
+	 * @param max is the total number of indexes
+	 * @param cmin is the minimum colour
+	 * @param cmax is the maximum colour
+	 * @return the colour string (e.g. #F35A22)
+	 */
+	public String numToColour(int num, int max, int cmin, int cmax)
+	{
+		logToConsole("num = " + num + ", max = " + max + ", cmin = " + cmin + ", cmax = " + cmax);
+		//Make sure num and max are good numbers
+		if(num < 0){num = 0;}
+		if(max <= 0){return "#000000";}	
+		if(max > 255){max = 255;}
+		if(num > max){num = max;}
+		
+		//Work out where this colour will lie in the spectrum
+		int colourNum = (int) ((((float) num) / ((float) max)) * ((cmax - cmin) * 8));
+
+		String red = null;
+		String green = null;
+		String blue = null;
+
+		//If the colour is in the first section then make it a mix between red and green
+		if (colourNum <= (cmax - cmin) * 2)
+		{
+			if (colourNum <= (cmax - cmin))
+			{
+				red = Integer.toHexString(colourNum + cmin);
+				green = "00";
+				blue = "00";
+			}
+			else
+			{
+				colourNum -= (cmax - cmin);
+				red = Integer.toHexString(cmax);
+				green = Integer.toHexString(colourNum + cmin);
+				blue = "00";
+			}
+		}
+		//If the colour is in the second section then make it a mix between green and blue
+		else if (colourNum <= (cmax - cmin) * 4)
+		{
+			colourNum -= ((cmax - cmin) * 2);
+			if (colourNum <= (cmax - cmin))
+			{
+				red = "00";
+				green = Integer.toHexString(colourNum + cmin);
+				blue = "00";
+			}
+			else
+			{
+				colourNum -= (cmax - cmin);
+				red = "00";
+				green = Integer.toHexString(cmax);
+				blue = Integer.toHexString(colourNum + cmin);
+			}
+		}
+		//If the colour is in the third section then make it a mix between red and blue
+		else if (colourNum <= (cmax - cmin) * 6)
+		{
+			colourNum -= ((cmax - cmin) * 4);
+			if (colourNum <= (cmax - cmin))
+			{
+				red = "00";
+				green = "00";
+				blue = Integer.toHexString(colourNum + cmin);
+			}
+			else
+			{
+				colourNum -= (cmax - cmin);
+				red = Integer.toHexString(colourNum + cmin);
+				green = "00";
+				blue = Integer.toHexString(cmax);
+			}
+		}
+		//If the colour is in the fourth section then make it a mix between all colours
+		else
+		{
+			colourNum -= ((cmax - cmin) * 6);
+			colourNum /= 2;
+			
+			red = Integer.toHexString(colourNum + cmin);
+			green = Integer.toHexString(colourNum + cmin);
+			blue = Integer.toHexString(colourNum + cmin);
+		}
+
+		//If the hex strings are only one character long then add a zero the the front
+		if (red.length() < 2){red = "0" + red;}
+		if (green.length() < 2){green = "0" + green;}
+		if (blue.length() < 2){blue = "0" + blue;}
+
+		logToConsole("#" + red + green + blue);
+		return "#" + red + green + blue;
+	}
+
+	/**
+	 * Takes an array of places and displays them on the map
+	 * @param places is the list of places to display
+	 * @param borderColour is the colour of the marker borders
+	 * @param fillColour is the main colour of the marker
+	 * @param colourInfo is whether or not to display a coloured box in the information box of the marker
+	 */
+	public void addPlacesToMap(ArrayList<Place> places, String borderColour, String fillColour, boolean colourInfo)
+	{
+		//If we do not have a valid set of places then stop
+		if(places == null || places.size() == 0)
+		{
+			return;
+		}
+		
+		ArrayList<Place> placesSeen = new ArrayList<Place>();
+
+		// Make the best places visible
+		for (Place p : places)
+		{
+			if (!_showAllPlaces)
+			{
+				//If the place is not directly referenced then don't use it
+				if (!p.isDirectlyReferenced())
+				{
+					continue;
+				}
+
+				//If a different place with this name has been chosen then don't use it
+				if (_removedPlaces.contains(p.getName()) || (_chosenPlaces.containsKey(p.getName()) && !_chosenPlaces.get(p.getName()).equals(p)))
+				{
+					continue;
+				}
+
+				//Make sure we only add the place once
+				boolean found = false;
+				for (Place q : placesSeen)
+				{
+					if (p.getName().equals(q.getName()))
+					{
+						found = true;
+					}
+				}
+
+				if (found)
+				{
+					continue;
+				}
+				placesSeen.add(p);
+			}
+
+			// If there is no latitude and longitude provided for this place
+			if (p.getLatitude() == null)
+			{
+				// Use Google's geocoder to find the place
+				findAndMarkAddress(p, borderColour, fillColour, colourInfo);
+			}
+			// Latitude and longitude are provided in the address
+			else
+			{
+				Polygon marker = createMarkerAtPoint(p, null, borderColour, fillColour, colourInfo);
+				// Add the marker to the map
+				_map.addOverlay(marker);
+			}
+		}
+	}
+
+	/**
+	 * Finds and puts a marker at the given address using Google's geocoder service
+	 * @param address is the address to find and mark (e.g. Hamilton, Waikato, New Zealand)
+	 * @param index is where in the list of place locations this is to be added
+	 */
+	private void findAndMarkAddress(final Place place, final String borderColour, final String fillColour, final boolean colourInfo)
+	{
+		String address = null;
+
+		if (place.getParentPlaceName() != null)
+		{
+			address = place.getName() + ", " + place.getParentPlaceName();
+		}
+		else
+		{
+			address = place.getName();
+		}
+
+		// Make the call to the geocoder service
+		_geocoder.getLatLng(address, new LatLngCallback()
+		{
+			// Ignore the place if it is not valid
+			public void onFailure()
+			{
+				// LogToConsole("Failed");
+			}
+
+			// If the geocoder service found the place
+			public void onSuccess(final LatLng point)
+			{
+				// LogToConsole("Found");
+				Polygon marker = createMarkerAtPoint(place, point, borderColour, fillColour, colourInfo);
+
+				place.setLatitude((float) point.getLatitude());
+				place.setLongitude((float) point.getLongitude());
+
+				// Add the marker to the map
+				_map.addOverlay(marker);
+			}
+		});
+	}
+
+	public AsyncCallback<ArrayList<Place>> setUpGetPlacesCallback()
+	{
+		AsyncCallback<ArrayList<Place>> callback = new AsyncCallback<ArrayList<Place>>()
+		{
+			public void onFailure(Throwable caught)
+			{
+				logToConsole("Get Places FAIL" + caught.getMessage());
+			}
+
+			// This is called when a successful call to findAllPlaces() is
+			// completed
+			public void onSuccess(ArrayList<Place> places)
+			{
+				logToConsole("Get Places Success!");
+				_currentPlaces = places;
+
+				addPlacesToMap(places, "#FFFFFF", "#00FF00", _searchResultsPage);
+				_updatingMap = false;
+			}
+		};
+
+		return callback;
+	}
+
+	/**
+	 * Creates the callback object that is used when the call to the server returns
+	 * @return the callback object
+	 */
+	public AsyncCallback<Boolean> setUpFindPlacesCallback()
+	{
+		return new AsyncCallback<Boolean>()
+		{
+			/**
+			 * This is called when the call to the server fails
+			 */
+			public void onFailure(Throwable caught)
+			{
+				_statusBar.removeUpdate("TextSearch");
+				logToConsole("Find places fail" + caught.getMessage());
+			}
+
+			/**
+			 * This is called when the call to the server succeeds
+			 * @param result is a dummy variable that does not do anything
+			 */
+			public void onSuccess(Boolean result)
+			{
+				//Remove the "searching for places" status bar message
+				_statusBar.removeUpdate("TextSearch");
+
+				logToConsole("Find Places success!");
+				
+				//Get the marked up text from the server
+				_findPlaceService.getMarkedUpText(setUpGetMarkedUpTextCallback());
+				
+				//Get the found places from the server
+				updateCurrentPlaces();
+
+				//Clear the map
+				_map.clearOverlays();
+				
+				//Add the new places to the map
+				addPlacesToMap(_currentPlaces, "#FFFFFF", "#00FF00", _searchResultsPage);
+				
+				//Remove the "map loading" message from the map
+				_mapLoadingLabel.removeFromParent();
+			}
+		}; 
+	}
+
+	public AsyncCallback<ArrayList<ArrayList<Place>>> setUpFindMultiplePlacesCallback()
+	{
+		AsyncCallback<ArrayList<ArrayList<Place>>> callback = new AsyncCallback<ArrayList<ArrayList<Place>>>()
+		{
+			public void onFailure(Throwable caught)
+			{
+				alert("Find multi places fail" + caught.getMessage());
+			}
+
+			public void onSuccess(ArrayList<ArrayList<Place>> places)
+			{
+				_statusBar.removeUpdate("SearchLoad");
+				_statusBar.addUpdate("Displaying places", "DisPlaces");
+
+				_currentMultiPlaces = places;
+				_visiblePlaceSets = new ArrayList<Boolean>();
+				for (int i = 0; i < _currentMultiPlaces.size(); i++)
+				{
+					_visiblePlaceSets.add(true);
+				}
+
+				_mapLoadingLabel.removeFromParent();
+				displayMultiplePlaceSets();
+				addElementsToSearchResults(places);
+			}
+		};
+		return callback;
+	}
+
+	public AsyncCallback<Boolean> setUpCheckGazetteerCallback()
+	{
+		AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>()
+		{
+			public void onFailure(Throwable caught)
+			{
+				logToConsole("Check Gazetteer fail " + caught.getMessage());
+			}
+
+			public void onSuccess(Boolean result)
+			{
+				if (!result)
+				{
+					_statusBar.addUpdate("Server is loading gazetteer", "GazLoad");
+
+					_findPlaceService.loadGazetteer(setUpLoadGazetteerCallback());
+				}
+				else
+				{
+					logToConsole("Gazetteer Already Loaded!");
+					_gazetteerLoaded = true;
+				}
+			}
+		};
+		return callback;
+	}
+
+	/**
+	 * Checks to see if the current page has text from a document so that it can be checked for places
+	 */
+	public void checkDocText()
+	{
+		//Get the document text element if it exists
+		Element documentText = getDocTextElement();
+
+		//If it does not exist then stop
+		if (documentText == null)
+		{
+			logToConsole("document text div not found");
+			if (_mapVisible)
+			{
+				_map.setVisible(false);
+				_mapVisible = false;
+			}
+			_viewPanel.setVisible(false);
+			return;
+		}
+		
+		logToConsole("Document text found!");
+		
+		//Allow the user to change the method they wish to use view the text
+		_viewPanel.setVisible(true);
+
+		//If the view type is not set to normal then remove the TOC
+		if (_textViewSelector.getSelectedIndex() > 0)
+		{
+			HTML.wrap(getElementById("rightSidebar")).setVisible(false);
+		}
+
+		//Create the document menu if we have not already done so
+		if (_currentDocumentMenu == null)
+		{
+			createDocumentMenu();
+		}
+
+		//Create the timer that closes the document menu when it is not in focus
+		_menuOutTimer = new Timer()
+		{
+			public void run()
+			{
+				_currentDocumentMenu.hideMenu();
+			}
+		};
+
+		//Let the user know that the system is searching for places
+		_statusBar.addUpdate("Searching text for places", "TextSearch");
+
+		//Clear the map
+		_map.clearOverlays();
+		
+		//Make the map visible
+		_mapVisible = true;
+		_map.setVisible(true);
+		
+		//Add the "Loading" message to the map
+		_contentDiv.add(_mapLoadingLabel);
+
+		_mapLoadingLabel.getElement().getStyle().setProperty("left", ((int) (_headerDiv.getOffsetWidth() * (3.0 / 4.0) - _mapLoadingLabel.getOffsetWidth() / 2)) + "px");
+		_mapLoadingLabel.getElement().getStyle().setProperty("top", (_headerDiv.getOffsetHeight() + _contentDiv.getOffsetHeight() / 3) + "px");
+
+		//Resize the page elemens
+		_resize = true;
+
+		//Send the document text to the server to be checked for places
+		final Element finalDocumentText = documentText;
+		Timer checkLoadTimer = new Timer()
+		{
+			public void run()
+			{
+				if (_gazetteerLoaded)
+				{
+					this.cancel();
+					_findPlaceService.findPlacesInText(finalDocumentText.getInnerText(), finalDocumentText.getInnerHTML(), setUpFindPlacesCallback());
+				}
+			}
+		};
+		checkLoadTimer.scheduleRepeating(1000);
+	}
+
+	/**
+	 * Creates and returns the callback object used when the call to the server returns
+	 * @return the callback object
+	 */
+	public AsyncCallback<String> setUpGetMarkedUpTextCallback()
+	{
+		return new AsyncCallback<String>()
+		{
+			/**
+			 * This is called when the call to the server fails
+			 */
+			public void onFailure(Throwable caught)
+			{
+				logToConsole("Mark up text failed -> " + caught.getMessage());
+			}
+
+			/**
+			 * This is called when the call the to server succeeds
+			 * @param markedUpText is the text (marked up with place names) returned from the server
+			 */
+			public void onSuccess(String markedUpText)
+			{
+				logToConsole("Mark up text success");
+
+				//Get the unmodified document text element from the web page
+				Element documentText = getDocTextElement();
+
+				//Set the document text HTML to be the marked up text
+				documentText.setInnerHTML(markedUpText);
+
+				//Get the spans in the marked up text that are place names
+				JsArray<Element> textPlaces = getElementsByTagName("span");
+				ArrayList<Element> placeElements = new ArrayList<Element>();
+
+				//Add the place spans to the array
+				for (int j = 0; j < textPlaces.length(); j++)
+				{
+					if (textPlaces.get(j).getAttribute("class").equals("place"))
+					{
+						placeElements.add(textPlaces.get(j));
+					}
+				}
+
+				logToConsole("Adding events to " + placeElements.size() + " place spans");
+
+				//Add mouse over and mouse out handlers to each span
+				for (Element e : placeElements)
+				{
+					Label newLabel = Label.wrap(e);
+					newLabel.addMouseOverHandler(new PlaceMouseOverHandler());
+					newLabel.addMouseOutHandler(new PlaceMouseOutHandler());
+					newLabel.addMouseUpHandler(new PlaceMouseUpHandler());
+				}
+
+				//If Fisheye view is selected in the list box then use it
+				if (_textViewSelector.getSelectedIndex() == 1)
+				{
+					_statusBar.addUpdate("Loading fisheye view", "FishLoad");
+					setUpFishEye();
+					_statusBar.removeUpdate("FishLoad");
+					_fishEyeHandlerReg = _gsPanel.addMouseMoveHandler(new FishEyeMouseMoveHandler());
+				}
+
+				//If Column view is selected in the list box then use it
+				if (_textViewSelector.getSelectedIndex() == 2)
+				{
+					setUpColumnView(documentText);
+				}
+			}
+		};
+	}
+
+	public void setUpColumnView(Element documentText)
+	{
+		_statusBar.addUpdate("Loading column view", "ColLoad");
+
+		logToConsole("Wrapping doc text");
+		HTML docText = HTML.wrap(documentText);
+		int width = docText.getOffsetWidth();
+		String html = docText.getHTML();
+		ArrayList<String> words = new ArrayList<String>();
+
+		int inTag = 0;
+		boolean inWord = false;
+
+		StringBuilder currentWord = new StringBuilder();
+
+		for (int i = 0; i < html.length(); i++)
+		{
+			boolean add = true;
+			char c = html.charAt(i);
+
+			if (inTag > 0 && c == '>')
+			{
+				inTag--;
+
+				if (inTag == 0)
+				{
+					words.add(currentWord.toString() + ">");
+					currentWord = new StringBuilder();
+					add = false;
+				}
+			}
+			else if (c == '<')
+			{
+				inTag++;
+
+				if (inWord)
+				{
+					inWord = false;
+					words.add(currentWord.toString());
+					currentWord = new StringBuilder();
+				}
+			}
+			else if (("" + c).matches("\\s"))
+			{
+				if (inTag == 0)
+				{
+					add = false;
+				}
+
+				if (inWord)
+				{
+					inWord = false;
+					words.add(currentWord.toString());
+					currentWord = new StringBuilder();
+				}
+			}
+			else
+			{
+				if (inTag == 0 && !inWord)
+				{
+					inWord = true;
+				}
+			}
+
+			if (add)
+			{
+				currentWord.append(c);
+			}
+		}
+
+		docText.setHTML("");
+
+		HorizontalPanel columns = new HorizontalPanel();
+		ArrayList<Label> labels = new ArrayList<Label>();
+		logToConsole("Adding columns to panel");
+
+		if (_contentDiv.getWidget(0).getElement().getId() == "gs_content")
+		{
+			VerticalPanel newDocTextPanel = new VerticalPanel();
+			newDocTextPanel.add(columns);
+			newDocTextPanel.add(_contentDiv.getWidget(0));
+			newDocTextPanel.getElement().setId("columnBox");
+			_contentDiv.insert(newDocTextPanel, 0);
+		}
+		else
+		{
+			((VerticalPanel) (_contentDiv.getWidget(0))).remove(0);
+			((VerticalPanel) (_contentDiv.getWidget(0))).insert(columns, 0);
+		}
+
+		float fontSize = 12;
+		do
+		{
+			logToConsole("Looping");
+			Label l = new Label();
+			labels.add(l);
+			columns.add(l);
+
+			for (int j = 0; j < labels.size(); j++)
+			{
+				StringBuilder labelText = new StringBuilder();
+				for (int i = (words.size() / labels.size()) * j; i < ((j < (labels.size() - 1)) ? (((words.size() / labels.size()) * (j + 1)) - 1) : (words.size())); i++)
+				{
+					labelText.append(words.get(i) + " ");
+				}
+
+				labels.get(j).getElement().setInnerHTML(labelText.toString());
+				labels.get(j).setWidth((width / labels.size()) + "px");
+				labels.get(j).getElement().getStyle().setProperty("fontSize", fontSize + "px");
+			}
+			fontSize *= 0.85;
+
+			logToConsole("Font size = " + fontSize);
+		} while (columns.getOffsetHeight() > (Window.getClientHeight() - _headerDiv.getOffsetHeight() - _footerDiv.getOffsetHeight() - 70));
+
+		for (int i = 0; i < labels.size(); i++)
+		{
+			ArrayList<String> subList = new ArrayList<String>();
+			for (int j = (words.size() / labels.size()) * i; j < ((i < (labels.size() - 1)) ? (((words.size() / labels.size()) * (i + 1)) - 1) : (words.size())); j++)
+			{
+				subList.add(words.get(j));
+			}
+			labels.get(i).addMouseMoveHandler(new ColumnViewMouseMoveHandler(labels.get(i), subList));
+		}
+
+		columns.add(_columnViewBox);
+		_columnViewBox.getElement().getStyle().setProperty("position", "absolute");
+		_columnViewBox.getElement().getStyle().setProperty("border", "2px solid");
+		_columnViewBox.getElement().getStyle().setProperty("background", "#FFFFFF");
+		_columnViewBox.getElement().getStyle().setProperty("fontSize", "10px");
+		_columnViewBox.setPixelSize(250, 100);
+		_columnViewBox.setVisible(false);
+
+		_statusBar.removeUpdate("ColLoad");
+	}
+
+	class ColumnViewMouseMoveHandler implements MouseMoveHandler
+	{
+		Label _label = null;
+		ArrayList<String> _words = null;
+
+		public ColumnViewMouseMoveHandler(Label label, ArrayList<String> words)
+		{
+			_label = label;
+			_words = words;
+		}
+
+		public void onMouseOut(MouseOutEvent event)
+		{
+			_columnViewBox.setVisible(false);
+		}
+
+		public void onMouseOver(MouseOverEvent event)
+		{
+			_columnViewBox.setVisible(true);
+		}
+
+		public void onMouseMove(MouseMoveEvent event)
+		{
+			_columnViewBox.setVisible(true);
+
+			JsArray<Element> divs = getElementsByTagName("div");
+			Element documentText = null;
+			for (int i = 0; i < divs.length(); i++)
+			{
+				if (divs.get(i).getAttribute("class").equals("documenttext"))
+				{
+					documentText = divs.get(i);
+					break;
+				}
+			}
+
+			if (event.getY() < 125)
+			{
+				_columnViewBox.getElement().getStyle().setProperty("top", (event.getY() + _label.getAbsoluteTop() + 25) + "px");
+			}
+			else
+			{
+				_columnViewBox.getElement().getStyle().setProperty("top", (event.getY() + _label.getAbsoluteTop() - 125) + "px");
+			}
+
+			if (event.getX() + _label.getAbsoluteLeft() - 125 < documentText.getAbsoluteLeft())
+			{
+				_columnViewBox.getElement().getStyle().setProperty("left", documentText.getAbsoluteLeft() + "px");
+			}
+			else if (event.getX() + _label.getAbsoluteLeft() + 125 > documentText.getOffsetWidth() + documentText.getAbsoluteLeft())
+			{
+				_columnViewBox.getElement().getStyle().setProperty("left", documentText.getOffsetWidth() + documentText.getAbsoluteLeft() - 250 + "px");
+			}
+			else
+			{
+				_columnViewBox.getElement().getStyle().setProperty("left", (event.getX() + _label.getAbsoluteLeft() - 125) + "px");
+			}
+
+			int midWordIndex = (int) (((float) (event.getY()) / ((_label.getAbsoluteTop() + _label.getOffsetHeight()) - _label.getAbsoluteTop())) * _words.size());
+			int topWord = midWordIndex - 25;
+			int botWord = midWordIndex + 25;
+
+			if (_words.size() < 50)
+			{
+				topWord = 0;
+				botWord = _words.size();
+			}
+			else
+			{
+				if (topWord < 0)
+				{
+					topWord = 0;
+				}
+				else if (topWord > _words.size() - 50)
+				{
+					topWord = _words.size() - 50;
+				}
+
+				if (botWord > _words.size())
+				{
+					botWord = _words.size();
+				}
+				else if (botWord < 50)
+				{
+					botWord = 50;
+				}
+			}
+
+			StringBuilder s = new StringBuilder();
+
+			for (int i = topWord; i < botWord; i++)
+			{
+				s.append(_words.get(i) + " ");
+			}
+
+			_columnViewBox.getElement().setInnerHTML(s.toString());
+		}
+	}
+
+	public AsyncCallback<Boolean> setUpLoadGazetteerCallback()
+	{
+		AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>()
+		{
+			public void onFailure(Throwable caught)
+			{
+				logToConsole("Loading Gazetteer failed ->" + caught.getMessage());
+			}
+
+			public void onSuccess(Boolean success)
+			{
+				logToConsole("Loading Gazetteer success!");
+				_statusBar.removeUpdate("GazLoad");
+				_gazetteerLoaded = true;
+			}
+		};
+
+		return callback;
+	}
+
+	public void createDocumentMenu()
+	{
+		// Menu
+		final Menu documentMenu = new Menu(_footerDiv.getElement(), new MouseOverHandler()
+		{
+			public void onMouseOver(MouseOverEvent event)
+			{
+				if(_menuOverTimer != null)
+				{
+					_menuOverTimer.cancel();
+					_menuOverTimer = null;
+				}
+				Label item = (Label) event.getSource();
+				item.getElement().getStyle().setProperty("background", "#0000FF");
+				item.getElement().getStyle().setProperty("color", "#FFFFFF");
+				item.getElement().getStyle().setProperty("border", "1px solid");
+				if (_menuOutTimer != null)
+				{
+					_menuOutTimer.cancel();
+					_menuOutTimer = null;
+				}
+			}
+		}, new MouseOutHandler()
+		{
+			public void onMouseOut(MouseOutEvent event)
+			{
+				// logToConsole("MENU OUT!");
+				Label item = (Label) event.getSource();
+				item.getElement().getStyle().setProperty("background", "#FFFFFF");
+				item.getElement().getStyle().setProperty("color", "#000000");
+				item.getElement().getStyle().setProperty("border", "1px solid");
+
+				_menuOutTimer = new Timer()
+				{
+					public void run()
+					{
+						_currentDocumentMenu.hideMenu();
+					}
+				};
+
+				_menuOutTimer.schedule(500);
+			}
+		});
+
+		// Menu items
+		ArrayList<MenuItem> menuItems = new ArrayList<MenuItem>();
+		menuItems.add(new MenuItem("Centre this place on the map", new ClickHandler()
+		{
+			public void onClick(ClickEvent event)
+			{
+				Place place = null;
+
+				logToConsole("Center clicked, selected place name = " + _selectedPlaceName);
+
+				if ((place = _chosenPlaces.get(_selectedPlaceName)) == null)
+				{
+					for (int i = 0; i < _currentPlaces.size(); i++)
+					{
+						if (_currentPlaces.get(i).getName().equals(_selectedPlaceName))
+						{
+							place = _currentPlaces.get(i);
+							break;
+						}
+					}
+				}
+
+				if (place != null && place.getLatitude() != null && place.getLongitude() != null)
+				{
+					logToConsole("Centering map");
+					_map.setCenter(LatLng.newInstance(place.getLatitude(), place.getLongitude()));
+				}
+
+				_currentDocumentMenu.hideMenu();
+			}
+		}));
+
+		menuItems.add(new MenuItem("Highlight this place on the map", new ClickHandler()
+		{
+			public void onClick(ClickEvent event)
+			{
+				_highlightedPlaces.add(_selectedPlaceName);
+				_map.clearOverlays();
+				logToConsole("Overlays cleared");
+				addPlacesToMap(_currentPlaces, "#FFFFFF", "#00FF00", false);
+				logToConsole("Places added");
+
+				_currentDocumentMenu.hideMenu();
+			}
+		}));
+
+		menuItems.add(new MenuItem("Highlight this place in the text", new ClickHandler()
+		{
+			public void onClick(ClickEvent event)
+			{
+				_highlightedTextPlaces.add(_selectedPlaceName);
+
+				JsArray<Element> places = getElementsByTagName("span");
+
+				for (int i = 0; i < places.length(); i++)
+				{
+					Element e = places.get(i);
+
+					if (e.getClassName() == "place" && e.getInnerText() == _selectedPlaceName)
+					{
+						e.getStyle().setProperty("background", "#FF0000");
+					}
+				}
+
+				_currentDocumentMenu.hideMenu();
+			}
+		}));
+		
+		menuItems.add(new MenuItem("Remove all highlights", new ClickHandler()
+		{
+			public void onClick(ClickEvent event)
+			{
+				_highlightedPlaces.clear();
+
+				_map.clearOverlays();
+				addPlacesToMap(_currentPlaces, "#FFFFFF", "#00FF00", false);
+
+				JsArray<Element> places = getElementsByTagName("span");
+
+				for (int i = 0; i < places.length(); i++)
+				{
+					Element e = places.get(i);
+
+					if (e.getClassName() == "place" && _highlightedTextPlaces.contains(e.getInnerText()))
+					{
+						e.getStyle().setProperty("background", "#FFFF00");
+					}
+				}
+				_highlightedTextPlaces.clear();
+
+				_currentDocumentMenu.hideMenu();
+			}
+		}));
+		
+		final MenuItem prevMenuItem = new MenuItem("Remove this place", new ClickHandler()
+		{
+			public void onClick(ClickEvent event)
+			{
+				_removedPlaces.add(_selectedPlaceName);
+				_currentDocumentMenu.hideMenu();
+				
+				JsArray<Element> textPlaces = getElementsByTagName("span");
+
+				//Add the place spans to the array
+				for (int j = 0; j < textPlaces.length(); j++)
+				{
+					Element e = textPlaces.get(j);
+					if (e.getAttribute("class").equals("place") && e.getInnerText().equals(_selectedPlaceName))
+					{
+						e.getParentElement().insertBefore(e.getFirstChild(), e);
+					}
+				}
+				
+				_map.clearOverlays();
+				addPlacesToMap(_currentPlaces, "#FFFFFF", "#00FF00", false);
+			}
+		});
+		menuItems.add(prevMenuItem);
+
+		menuItems.add(new MenuItem("Choose correct place >", new ClickHandler()
+		{
+			public void onClick(ClickEvent event)
+			{
+				logToConsole("MARKER!");
+				ArrayList<MenuItem> places = new ArrayList<MenuItem>();
+
+				ArrayList<Place> matchingPlaces = new ArrayList<Place>();
+
+				for (Place p : _currentPlaces)
+				{
+					if (p.getName().equals(_selectedPlaceName))
+					{
+						matchingPlaces.add(p);
+					}
+				}
+				
+				for (final Place p : matchingPlaces)
+				{
+					places.add(new MenuItem(p.getName() + ((p.getParentPlaceName() != null) ? (", " + p.getParentPlaceName()) : (" (Country)")), new ClickHandler()
+					{
+						public void onClick(ClickEvent e)
+						{
+							_chosenPlaces.put(_selectedPlaceName, p);
+
+							_map.clearOverlays();
+							logToConsole("Chose a new place!!!");
+							addPlacesToMap(_currentPlaces, "#FFFFFF", "#00FF00", false);
+							logToConsole("Places added!!!");
+
+							_currentDocumentMenu.hideMenu();
+						}
+					}));
+				}
+
+				Menu placeMenu = new Menu(_footerDiv.getElement(), new MouseOverHandler()
+				{
+					public void onMouseOver(MouseOverEvent event)
+					{
+						Label item = (Label) event.getSource();
+						item.getElement().getStyle().setProperty("background", "#0000FF");
+						item.getElement().getStyle().setProperty("color", "#FFFFFF");
+						if (_menuOutTimer != null)
+						{
+							_menuOutTimer.cancel();
+							_menuOutTimer = null;
+						}
+					}
+				}, new MouseOutHandler()
+				{
+					public void onMouseOut(MouseOutEvent event)
+					{
+						Label item = (Label) event.getSource();
+						item.getElement().getStyle().setProperty("background", "#FFFFFF");
+						item.getElement().getStyle().setProperty("color", "#000000");
+
+						_menuOutTimer = new Timer()
+						{
+							public void run()
+							{
+								_currentDocumentMenu.hideMenu();
+							}
+						};
+
+						_menuOutTimer.schedule(500);
+					}
+				});
+
+				documentMenu.addChildMenu(placeMenu);
+				placeMenu.addMenuItems(places);
+				placeMenu.moveMenu(_currentDocumentMenu.getMenuDiv().getAbsoluteLeft() + _currentDocumentMenu.getMenuDiv().getOffsetWidth(), prevMenuItem.getMenuElement().getAbsoluteTop() + prevMenuItem.getMenuElement().getOffsetHeight() - 2);
+				placeMenu.showMenu();
+			}
+		}){
+			public boolean condition()
+			{
+				ArrayList<Place> matchingPlaces = new ArrayList<Place>();
+
+				if(_currentPlaces == null || _currentPlaces.size() == 0)
+				{
+					return false;
+				}
+				
+				for (Place p : _currentPlaces)
+				{
+					if (p.getName().equals(_selectedPlaceName))
+					{
+						matchingPlaces.add(p);
+					}
+				}
+				
+				if(matchingPlaces.size() > 1)
+				{
+					return true;
+				}
+				return false;
+			}
+		});
+
+		documentMenu.addMenuItems(menuItems);
+		documentMenu.hideMenu();
+		_currentDocumentMenu = documentMenu;
+	}
+
+	public void startDocumentMouseOverTimer(final int x, final int y)
+	{
+		_documentMouseOverTimer = new Timer()
+		{
+			public void run()
+			{
+				_currentDocumentMenu.moveMenu(x, y);
+				_currentDocumentMenu.showMenu();
+
+				_documentMouseOverTimer = null;
+			}
+		};
+
+		_documentMouseOverTimer.schedule(1000);
+	}
+
+	/**
+	 * Gets the document text element from the web page
+	 * @return the document text element
+	 */
+	public Element getDocTextElement()
+	{
+		JsArray<Element> divs = getElementsByTagName("div");
+
+		Element documentText = null;
+		for (int i = 0; i < divs.length(); i++)
+		{
+			if (divs.get(i).getAttribute("class").equals("documenttext"))
+			{
+				documentText = divs.get(i);
+				break;
+			}
+		}
+
+		return documentText;
+	}
+
+	public void addElementsToSearchResults(ArrayList<ArrayList<Place>> places)
+	{
+		JsArray<Element> elements = getElementsByTagName("li");
+		ArrayList<Element> listElements = new ArrayList<Element>();
+
+		for (int i = 0; i < elements.length(); i++)
+		{
+			if (elements.get(i).getAttribute("class").equals("document"))
+			{
+				listElements.add(elements.get(i));
+			}
+		}
+
+		for (int i = 0; i < listElements.size(); i++)
+		{
+			Element span = DOM.createSpan();
+			listElements.get(i).insertBefore(span, listElements.get(i).getFirstChild());
+			span.getStyle().setBackgroundColor(numToColour(i, listElements.size(), 100, 255));
+			span.getStyle().setColor(numToColour(i, listElements.size(), 100, 255));
+			span.setInnerText("...");
+
+			Element checkBoxSpan = DOM.createSpan();
+			listElements.get(i).insertBefore(checkBoxSpan, listElements.get(i).getFirstChild());
+			checkBoxSpan.setId("Result" + i);
+
+			CheckBox c = new CheckBox();
+			c.setValue(true);
+			c.addValueChangeHandler(new SearchResultsCheckBoxChangeHandler(i));
+			RootPanel.get("Result" + i).add(c);
+		}
+	}
+
+	public class SearchResultsCheckBoxChangeHandler implements ValueChangeHandler<Boolean>
+	{
+		int _index = -1;
+
+		public SearchResultsCheckBoxChangeHandler(int index)
+		{
+			_index = index;
+		}
+
+		public void onValueChange(ValueChangeEvent<Boolean> event)
+		{
+			if (event.getValue())
+			{
+				_visiblePlaceSets.set(_index, true);
+			}
+			else
+			{
+				_visiblePlaceSets.set(_index, false);
+			}
+
+			_map.clearOverlays();
+			displayMultiplePlaceSets();
+		}
+	}
+
+	public class DocumentOverHandler implements MouseOverHandler
+	{
+		int _index = -1;
+
+		public DocumentOverHandler(int index)
+		{
+			_index = index;
+		}
+
+		public void onMouseOver(MouseOverEvent event)
+		{
+			Label currentLabel = (Label) event.getSource();
+			currentLabel.setStyleName("placeOver");
+
+			startDocumentMouseOverTimer(event.getX() + currentLabel.getAbsoluteLeft(), event.getY() + currentLabel.getAbsoluteTop());
+		}
+	}
+
+	public class PlaceMouseOverHandler implements MouseOverHandler
+	{
+		public void onMouseOver(MouseOverEvent event)
+		{
+			Label currentLabel = (Label) event.getSource();
+			currentLabel.setStyleName("placeOver");
+			
+			_selectedPlaceName = currentLabel.getElement().getInnerText();
+
+			startPlaceMouseOverTimer(event.getX() + currentLabel.getAbsoluteLeft(), event.getY() + currentLabel.getAbsoluteTop());
+		}
+	}
+
+	public class PlaceMouseOutHandler implements MouseOutHandler
+	{
+		public void onMouseOut(MouseOutEvent event)
+		{
+			Label currentLabel = (Label) event.getSource();
+			currentLabel.setStyleName("place");
+
+			if (_placeMouseOverTimer != null)
+			{
+				_placeMouseOverTimer.cancel();
+				_placeMouseOverTimer = null;
+			}
+		}
+	}
+	
+	public class PlaceMouseUpHandler implements MouseUpHandler
+	{
+		public void onMouseUp(MouseUpEvent event)
+		{
+			Label currentLabel = (Label) event.getSource();
+			currentLabel.setStyleName("place");
+			if(event.getNativeButton() == NativeEvent.BUTTON_LEFT)
+			{
+				String placeName = currentLabel.getText();
+				Place place = _chosenPlaces.get(placeName);
+				
+				if(place == null)
+				{
+					for(Place p : _currentPlaces)
+					{
+						if(p.getName().equals(placeName))
+						{
+							place = p;
+							break;
+						}
+					}
+				}
+				
+				if(place != null && place.getLatitude() != null && place.getLongitude() != null)
+				{
+					_map.setCenter(LatLng.newInstance(place.getLatitude(), place.getLongitude()));
+				}
+			}
+			/*
+			else if(event.getNativeButton() == NativeEvent.BUTTON_RIGHT)
+			{
+				_currentDocumentMenu.moveMenu(event.getX() + currentLabel.getAbsoluteLeft(), event.getY() + currentLabel.getAbsoluteTop());
+				_currentDocumentMenu.showMenu();
+			}
+			*/
+		}
+	}
+	
+	public void startPlaceMouseOverTimer(final int x, final int y)
+	{
+		_placeMouseOverTimer = new Timer()
+		{
+			public void run()
+			{
+				_currentDocumentMenu.refreshMenu();
+				_currentDocumentMenu.moveMenu(x, y);
+				_currentDocumentMenu.showMenu();
+				
+				_menuOverTimer = new Timer()
+				{
+					public void run()
+					{
+						_menuOverTimer = null;
+						_currentDocumentMenu.hideMenu();
+					}
+				};
+				_menuOverTimer.schedule(4000);
+
+				_placeMouseOverTimer = null;
+			}
+		};
+
+		_placeMouseOverTimer.schedule(1000);
+	}
+	
+	public class FishEyeMouseMoveHandler implements MouseMoveHandler
+	{
+		public void onMouseMove(MouseMoveEvent e)
+		{
+			if (!_mouseEventPause)
+			{
+				_mouseEventPause = true;
+				passMouseCoordsToFishEye(e.getY());
+				Timer pauseTimer = new Timer()
+				{
+					public void run()
+					{
+						_mouseEventPause = false;
+					}
+				};
+				pauseTimer.schedule(50);
+			}
+		}
+	}
+
+	/**
+	 * This class contains methods that initialise and control the spatial search controls present on the map during a spatial search 
+	 */
+	public class SpatialSearchControls extends CustomControl
+	{
+		/**
+		 * Inherited parent constructor
+		 */
+		protected SpatialSearchControls(ControlPosition arg0)
+		{
+			super(arg0);
+		}
+
+		/**
+		 * Second inherited parent constructor
+		 */
+		protected SpatialSearchControls(ControlPosition arg0, boolean arg1, boolean arg2)
+		{
+			super(arg0, arg1, arg2);
+		}
+
+		/**
+		 * Initialises the control buttons
+		 */
+		protected Widget initialize(MapWidget map)
+		{
+			//Create the panel to store the buttons
+			HorizontalPanel panel = new HorizontalPanel();
+			
+			//Create the button that clears the user's currently created points
+			Button clearButton = new Button("Clear Points", new ClickHandler()
+			{
+				public void onClick(ClickEvent event)
+				{
+					_areaPoints.clear();
+					_map.clearOverlays();
+				}
+			});
+			
+			//Create the button the performs the spatial search
+			Button performSearch = new Button("Search Area", new ClickHandler()
+			{
+				public void onClick(ClickEvent event)
+				{
+					//If the users has marked less than 3 points on the map then we cannot perform a spatial search
+					if (_areaPoints.size() < 3)
+					{
+						//Show an error message on the status bar
+						_statusBar.removeUpdate("SpatialWarn");
+						_statusBar.addUpdate("A minimum of 3 points is needed on the map to perform a spatial search", "SpatialWarn");
+
+						//Remove the message after 5 seconds
+						Timer warningRemoveTimer = new Timer()
+						{
+							public void run()
+							{
+								_statusBar.removeUpdate("SpatialWarn");
+							}
+						};
+						warningRemoveTimer.schedule(5000);
+
+						return;
+					}
+
+					ArrayList<Float[]> points = new ArrayList<Float[]>();
+
+					//Turn the points into a list of float arrays
+					for (LatLng point : _areaPoints)
+					{
+						points.add(new Float[] { (float) point.getLatitude(), (float) point.getLongitude() });
+					}
+
+					//Ask the server to perform the spatial search
+					_findPlaceService.spatialSearch(points, getSpatialSearchCallback());
+				}
+			});
+
+			//Add the two buttons to the panel
+			panel.add(clearButton);
+			panel.add(performSearch);
+
+			return panel;
+		}
+		
+		/**
+		 * Creates and returns the callback necessary to perform a spatial search on the server
+		 * @return the callback object
+		 */
+		public AsyncCallback<ArrayList<String>> getSpatialSearchCallback()
+		{
+			return new AsyncCallback<ArrayList<String>>()
+			{
+				/**
+				 * If the call to the server failed then this is called
+				 * @see com.google.gwt.user.client.rpc.AsyncCallback#onFailure(java.lang.Throwable)
+				 */
+				public void onFailure(Throwable arg0)
+				{
+					logToConsole("Spatial search FAIL!");
+				}
+				
+				/**
+				 * If the call to the server succeeded then this is called
+				 * @param placeNames is the places found within the spatial search polygon
+				 */
+				public void onSuccess(ArrayList<String> placeNames)
+				{
+					logToConsole("Spatial search Success!");
+
+					//Create the query to pass to Greenstone
+					StringBuffer query = new StringBuffer();
+					for (int j = 0; j < placeNames.size() && j < 20; j++)
+					{
+						if (j == 0)
+						{
+							query.append(placeNames.get(j).replaceAll(" ", "+").replaceAll("-", "+"));
+						}
+						else
+						{
+							query.append("+" + placeNames.get(j).replaceAll(" ", "+").replaceAll("-", "+"));
+						}
+					}
+
+					logToConsole("Query -> " + query);
+					logToConsole("Collection -> " + _currentCollection);
+
+					//Create an invisible form
+					FormPanel form = new FormPanel();
+					form.setAction(_GREENSTONEDEVURL);
+					form.setMethod(FormPanel.METHOD_POST);
+					form.getElement().setAttribute("target", "_self");
+
+					VerticalPanel tempForm = new VerticalPanel();
+					form.setWidget(tempForm);
+
+					getElementsByTagName("body").get(0).appendChild(form.getElement());
+
+					logToConsole("Adding hidden form elements");
+
+					Hidden a = new Hidden("a", "q");
+					tempForm.add(a);
+					Hidden sa = new Hidden("sa");
+					tempForm.add(sa);
+					Hidden rt = new Hidden("rt", "rd");
+					tempForm.add(rt);
+					Hidden s = new Hidden("s", "TextQuery");
+					tempForm.add(s);
+					Hidden c = new Hidden("c", _currentCollection);
+					tempForm.add(c);
+					Hidden startPage = new Hidden("startPage", "1");
+					tempForm.add(startPage);
+					Hidden s1level = new Hidden("s1.level", "Sec");
+					tempForm.add(s1level);
+					Hidden s1case = new Hidden("s1.case", "1");
+					tempForm.add(s1case);
+					Hidden s1stem = new Hidden("s1.stem", "0");
+					tempForm.add(s1stem);
+					Hidden s1accent = new Hidden("s1.accent", "0");
+					tempForm.add(s1accent);
+					Hidden s1matchMode = new Hidden("s1.matchMode", "some");
+					tempForm.add(s1matchMode);
+					Hidden s1sort = new Hidden("s1.sortBy", "1");
+					tempForm.add(s1sort);
+					Hidden s1index = new Hidden("s1.index", "ZZ");
+					tempForm.add(s1index);
+					Hidden s1maxDocs = new Hidden("s1.maxDocs", "100");
+					tempForm.add(s1maxDocs);
+					Hidden s1query = new Hidden("s1.query", query.toString());
+					tempForm.add(s1query);
+
+					logToConsole("Submitting");
+
+					form.submit();
+
+					logToConsole("Loading results page");
+
+					loadPageWithoutUrl();
+				}
+			};
+		}
+
+		public boolean isSelectable()
+		{
+			return false;
+		}
+	}
+
+	public static FindPlaceServiceAsync getServerConnection()
+	{
+		return _findPlaceService;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/Menu.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/Menu.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/Menu.java	(revision 23906)
@@ -0,0 +1,132 @@
+package org.greenstone.client;
+
+import java.util.ArrayList;
+
+import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.event.dom.client.MouseOverHandler;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Label;
+
+public class Menu
+{
+	protected Element _parent = null;
+	protected ArrayList<Menu> _childMenus = new ArrayList<Menu>();
+	protected ArrayList<MenuItem> _menuItems = new ArrayList<MenuItem>();
+	protected HTML _menuDiv = new HTML();
+	protected MouseOverHandler _onMouseOver = null;
+	protected MouseOutHandler _onMouseOut = null;
+	
+	public Menu(Element parent, MouseOverHandler onMouseOver, MouseOutHandler onMouseOut)
+	{
+		_parent = parent;
+    	parent.appendChild(_menuDiv.getElement());
+    	_menuDiv.getElement().getStyle().setProperty("background", "#FFFFFF");
+    	_menuDiv.getElement().getStyle().setProperty("padding", "2px");
+    	_menuDiv.getElement().getStyle().setProperty("position", "absolute");
+    	
+    	_onMouseOver = onMouseOver;
+    	_onMouseOut = onMouseOut;
+	}
+	
+	public void addMenuItems(ArrayList<MenuItem> menuItems)
+	{
+		_menuItems = menuItems;
+		int maxWidth = -1;
+    	for(int i = 0; i < menuItems.size(); i++)
+    	{
+    		if(!menuItems.get(i).condition())
+    		{
+    			continue;
+    		}
+    		Element item = menuItems.get(i).getMenuElement();
+    		item.getStyle().setProperty("background", "#FFFFFF");
+			item.getStyle().setProperty("color", "#000000");
+			item.getStyle().setProperty("border", "1px solid");
+			item.getStyle().setProperty("padding", "2px");
+    		
+    		Label newLabel = Label.wrap(item);	
+    		newLabel.addMouseOverHandler(_onMouseOver);
+    		newLabel.addMouseOutHandler(_onMouseOut);
+    		newLabel.addClickHandler(menuItems.get(i).getClickHandler());
+    		
+    		_menuDiv.getElement().appendChild(item);
+    		
+    		if(item.getOffsetWidth() > maxWidth)
+    		{
+    			maxWidth = item.getOffsetWidth();
+    		}
+    	}
+    	
+    	for(int i = 0; i < menuItems.size(); i++)
+    	{
+    		menuItems.get(i).getMenuElement().getStyle().setProperty("width", maxWidth + "px");
+    	}
+	}
+	
+	public void moveMenu(final int x, final int y)
+	{
+		_menuDiv.setVisible(false);
+    	_menuDiv.getElement().getStyle().setProperty("left", x + "px");
+    	_menuDiv.getElement().getStyle().setProperty("top", y + "px");
+    	
+    	Timer pause = new Timer()
+    	{
+			public void run() 
+			{				
+				if (_menuDiv.getOffsetHeight() > GS3MapLibrary.getElementById("GSContent").getOffsetHeight())
+				{
+					_menuDiv.getElement().getStyle().setProperty("top", GS3MapLibrary.getElementById("GSContent").getAbsoluteTop() + "px");
+					_menuDiv.getElement().getStyle().setProperty("overflow", "auto");
+					_menuDiv.getElement().getStyle().setProperty("height", GS3MapLibrary.getElementById("GSContent").getOffsetHeight() + "px");
+				}
+				else if(y + _menuDiv.getOffsetHeight() > GS3MapLibrary.getElementById("GSFooter").getAbsoluteTop())
+		    	{
+		    		_menuDiv.getElement().getStyle().setProperty("top", (y - ( y + _menuDiv.getOffsetHeight() - GS3MapLibrary.getElementById("GSFooter").getAbsoluteTop()) + "px"));
+		    	}
+				_menuDiv.setVisible(true);
+			}
+    	};
+    	pause.schedule(100);
+	}
+	
+	public void addChildMenu(Menu menu)
+	{
+		_childMenus.add(menu);
+	}
+	
+	public void hideMenu()
+	{
+		_menuDiv.setVisible(false);
+		
+		for(Menu m : _childMenus)
+		{
+			if(m != null)
+			{
+				m.hideMenu();
+			}
+		}
+	}
+	
+	public void refreshMenu()
+	{
+		_parent.removeChild(_menuDiv.getElement());
+		_menuDiv = new HTML();
+    	_parent.appendChild(_menuDiv.getElement());
+    	_menuDiv.getElement().getStyle().setProperty("background", "#FFFFFF");
+    	_menuDiv.getElement().getStyle().setProperty("padding", "2px");
+    	_menuDiv.getElement().getStyle().setProperty("position", "absolute");
+    	addMenuItems(_menuItems);
+	}
+	
+	public void showMenu()
+	{
+		_menuDiv.setVisible(true);
+	}
+	
+	public HTML getMenuDiv()
+	{
+		return _menuDiv;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/MenuItem.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/MenuItem.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/MenuItem.java	(revision 23906)
@@ -0,0 +1,34 @@
+package org.greenstone.client;
+
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+
+public class MenuItem
+{
+	protected Element _menuElement = DOM.createDiv();
+	protected ClickHandler _onClick = null;
+	
+	public MenuItem(String text, ClickHandler onClick)
+	{
+		_menuElement.getStyle().setProperty("cursor", "pointer");
+		_menuElement.setInnerText(text);
+		
+		_onClick = onClick;
+	}
+	
+	public boolean condition()
+	{
+		return true;
+	}
+	
+	public ClickHandler getClickHandler()
+	{
+		return _onClick;
+	}
+	
+	public Element getMenuElement()
+	{
+		return _menuElement;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/Place.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/Place.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/Place.java	(revision 23906)
@@ -0,0 +1,449 @@
+package org.greenstone.client;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+public class Place implements Serializable
+{
+	private static final long serialVersionUID = 6106265145261780206L;
+
+	protected ArrayList<String> _childrenPlaceNames = new ArrayList<String>();
+
+	protected String _placeName = null;
+	protected String _parentPlaceName = null;
+	protected String _placeType = null;
+
+	protected Float _latitude = null;
+	protected Float _longitude = null;
+
+	protected long _population = 0;
+	protected long _id = -1;
+	protected int _score = 0;
+
+	protected boolean _directlyReferenced = false;
+	protected static boolean _loopDone = false;
+	
+	protected static Place _tempPlace = null;
+
+	public Place()
+	{
+
+	}
+
+	public Place(String placeName)
+	{
+		_placeName = placeName;
+	}
+
+	public void setParentPlaceName(String parentPlaceName)
+	{
+		_parentPlaceName = parentPlaceName;
+	}
+
+	public boolean addChildPlaceName(String placeName)
+	{
+		if (_childrenPlaceNames.contains(placeName))
+		{
+			return false;
+		}
+		else
+		{
+			_childrenPlaceNames.add(placeName);
+			return true;
+		}
+	}
+
+	public boolean equals(Object placeToCompare)
+	{
+		if (placeToCompare == null)
+		{
+			return false;
+		}
+		/*
+		if (placeToCompare instanceof ServerPlace)
+		{
+			ServerPlace otherPlace = (ServerPlace)(placeToCompare);
+			if(_placeName != null && otherPlace.getName() != null && _parentPlaceName != null && otherPlace.getParentPlaceName() != null && _placeName.equals(otherPlace.getName()))
+			{
+				String[] cols = _parentPlaceName.split(", ");
+				String[] otherCols = otherPlace.getParentPlaceName().split(", ");
+				
+				if(cols.length == 3 && otherCols.length == 2)
+				{
+					if(cols[0].equals(otherPlace.getName()) && cols[1].equals(otherCols[0]) && cols[2].equals(otherCols[1]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 2 && otherCols.length == 3)
+				{
+					if(otherCols[0].equals(_placeName) && otherCols[1].equals(cols[0]) && otherCols[2].equals(cols[1]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 2 && otherCols.length == 1)
+				{
+					if(cols[0].equals(otherPlace.getName()) && cols[1].equals(otherCols[0]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 1 && otherCols.length == 2)
+				{
+					if(otherCols[0].equals(_placeName) && otherCols[1].equals(cols[0]))
+					{
+						return true;
+					}
+				}
+			}
+			
+			if (_id != -1 && ((ServerPlace)(placeToCompare)).getId() != -1 && _id == otherPlace.getId())
+			{
+				return true;
+			}
+
+			String otherPlaceName = otherPlace.getName();
+			String otherParentName = otherPlace.getParentPlaceName();
+
+			if ((_placeName == null && otherPlaceName == null) && (_parentPlaceName == null && otherParentName == null))
+			{
+				return true;
+			}
+
+			if ((_placeName == null && otherPlaceName == null) && _parentPlaceName != null && otherParentName != null && _parentPlaceName.equals(otherParentName))
+			{
+				return true;
+			}
+
+			if ((_parentPlaceName == null && otherParentName == null) && _placeName != null && otherPlaceName != null && _placeName.equals(otherPlaceName))
+			{
+				return true;
+			}
+
+			if (_placeName != null && otherPlaceName != null && _parentPlaceName != null && otherParentName != null && _parentPlaceName.equals(otherPlace.getParentPlaceName()) && _placeName.equals(otherPlace.getName()))
+			{
+				return true;
+			}
+		}
+*/
+		if (placeToCompare instanceof Place)
+		{
+			Place otherPlace = (Place)(placeToCompare);
+			if(_placeName != null && otherPlace.getName() != null && _parentPlaceName != null && otherPlace.getParentPlaceName() != null && _placeName.equals(otherPlace.getName()))
+			{
+				String[] cols = _parentPlaceName.split(", ");
+				String[] otherCols = otherPlace.getParentPlaceName().split(", ");
+				
+				if(cols.length == 3 && otherCols.length == 2)
+				{
+					if(cols[0].equals(otherPlace.getName()) && cols[1].equals(otherCols[0]) && cols[2].equals(otherCols[1]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 2 && otherCols.length == 3)
+				{
+					if(otherCols[0].equals(_placeName) && otherCols[1].equals(cols[0]) && otherCols[2].equals(cols[1]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 2 && otherCols.length == 1)
+				{
+					if(cols[0].equals(otherPlace.getName()) && cols[1].equals(otherCols[0]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 1 && otherCols.length == 2)
+				{
+					if(otherCols[0].equals(_placeName) && otherCols[1].equals(cols[0]))
+					{
+						return true;
+					}
+				}
+			}
+			
+			if (_id != -1 && ((Place)(placeToCompare)).getId() != -1 && _id == ((Place)(placeToCompare)).getId())
+			{
+				return true;
+			}
+
+			String otherPlaceName = otherPlace.getName();
+			String otherParentName = otherPlace.getParentPlaceName();
+
+			if ((_placeName == null && otherPlaceName == null) && (_parentPlaceName == null && otherParentName == null))
+			{
+				return true;
+			}
+
+			if ((_placeName == null && otherPlaceName == null) && _parentPlaceName != null && otherParentName != null && _parentPlaceName.equals(otherParentName))
+			{
+				return true;
+			}
+
+			if ((_parentPlaceName == null && otherParentName == null) && _placeName != null && otherPlaceName != null && _placeName.equals(otherPlaceName))
+			{
+				return true;
+			}
+
+			if (_placeName != null && otherPlaceName != null && _parentPlaceName != null && otherParentName != null && _parentPlaceName.equals(otherPlace.getParentPlaceName()) && _placeName.equals(otherPlace.getName()))
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public String toString()
+	{
+		String info = "*************************";
+
+		info += "\nPlace name = " + _placeName + "\nParents = " + _parentPlaceName + "\nChildren = ";
+
+		for (String p : _childrenPlaceNames)
+		{
+			info += "->" + p;
+		}
+
+		info += "\nPlace type = " + _placeType;
+		info += "\nPopulation = " + _population;
+		info += "\nLatitude = " + _latitude + " Longitude = " + _longitude;
+		info += "\n*************************";
+
+		return info;
+	}
+
+	// Get Methods
+
+	public int getScore()
+	{
+		return _score;
+	}
+
+	public String getPlaceType()
+	{
+		return _placeType;
+	}
+
+	public String getParentPlaceName()
+	{
+		return _parentPlaceName;
+	}
+
+	public ArrayList<String> getChildrenPlaceNames()
+	{
+		return _childrenPlaceNames;
+	}
+
+	public String getName()
+	{
+		return _placeName;
+	}
+
+	public Float getLatitude()
+	{
+		return _latitude;
+	}
+
+	public Float getLongitude()
+	{
+		return _longitude;
+	}
+
+	public long getPopulation()
+	{
+		return _population;
+	}
+
+	public long getId()
+	{
+		return _id;
+	}
+
+	public boolean isDirectlyReferenced()
+	{
+		return _directlyReferenced;
+	}
+
+	// Set Methods
+
+	public void setScore(int score)
+	{
+		_score = score;
+	}
+
+	public void setPlaceType(String placeType)
+	{
+		_placeType = placeType;
+	}
+
+	public void setLatitude(Float latitude)
+	{
+		_latitude = latitude;
+	}
+
+	public void setLongitude(Float longitude)
+	{
+		_longitude = longitude;
+	}
+
+	public void setPopulation(long population)
+	{
+		_population = population;
+	}
+
+	public void setId(long id)
+	{
+		_id = id;
+	}
+
+	public void directReference()
+	{
+		_directlyReferenced = true;
+	}
+
+	/**
+	 * Figures out what sort of place the line in the file is refering to (e.g.
+	 * locality, region, etc.)
+	 * 
+	 * @param placeName
+	 *            is the name of the place
+	 * @param subRegionName
+	 *            is the possible sub region the place is in
+	 * @param regionName
+	 *            is the possible region the place is in
+	 * @param countryName
+	 *            is the country the place is in
+	 * @param placeType
+	 *            is the type of place
+	 * @return returns the place
+	 */
+	public static Place createPlaceFromInformation(long id, String placeName, String subRegionName, String regionName, String countryName, String placeType, Float longitude, Float latitude, long population)
+	{
+		Place newPlace = new Place(placeName);
+
+		String parentPlaceName = null;
+		if (!(subRegionName == null))
+		{
+			parentPlaceName = subRegionName;
+		}
+		if (!(regionName == null))
+		{
+			if (parentPlaceName == null)
+			{
+				parentPlaceName = regionName;
+			}
+			else
+			{
+				parentPlaceName += ", " + regionName;
+			}
+		}
+		if (!(countryName == null))
+		{
+			if (parentPlaceName == null)
+			{
+				parentPlaceName = countryName;
+			}
+			else
+			{
+				parentPlaceName += ", " + countryName;
+			}
+		}
+
+		if (parentPlaceName == null && countryName == null)
+		{
+			if (placeType.equals("country"))
+			{
+				countryName = placeName;
+			}
+			else
+			{
+				return null;
+			}
+		}
+		
+		newPlace.setId(id);
+		newPlace.setParentPlaceName(parentPlaceName);
+		newPlace.setLatitude(latitude);
+		newPlace.setLongitude(longitude);
+		newPlace.setPopulation(population);
+		newPlace.setPlaceType(placeType);
+		return newPlace;
+	}
+	
+	public boolean isIn(Place p)
+	{
+		if(_parentPlaceName == null)
+		{
+			return false;
+		}
+		
+		String[] cols = _parentPlaceName.split(", ");
+		
+		if(p.getParentPlaceName() == null)
+		{
+			if(cols[cols.length-1].equals(p.getName()))
+			{
+				return true;
+			}
+			return false;
+		}
+		String[] otherCols = p.getParentPlaceName().split(", ");
+		
+		if(cols.length <= otherCols.length)
+		{
+			return false;
+		}
+		
+		else if(otherCols.length == 1)
+		{
+			if(cols[cols.length-1].equals(otherCols[0]) && cols[cols.length-2].equals(p.getName()))
+			{
+				return true;
+			}
+		}
+		else if(otherCols.length == 2)
+		{
+			if(cols[cols.length-1].equals(otherCols[1]) && cols[cols.length-2].equals(otherCols[0]) && cols[cols.length-3].equals(p.getName()))
+			{
+				return true;
+			}
+		}
+		
+		return false;
+	}
+	
+	public static Place createPlaceFromId(long id)
+	{
+		_loopDone = false;
+		GS3MapLibrary.getServerConnection().getPlaceById(id, new AsyncCallback<Place>()
+		{
+			@Override
+			public void onSuccess(Place p)
+			{
+				_tempPlace = p;
+				_loopDone = true;
+			}
+
+			@Override
+			public void onFailure(Throwable arg0)
+			{
+				_tempPlace = null;
+				_loopDone = true;
+			}
+		});
+		
+		while(!_loopDone);
+		
+		return _tempPlace;
+	}
+
+	public void unDirectReference()
+	{
+		_directlyReferenced = false;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/StatusBar.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/StatusBar.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/StatusBar.java	(revision 23906)
@@ -0,0 +1,98 @@
+package org.greenstone.client;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import com.google.gwt.user.client.ui.HTML;
+
+public class StatusBar
+{
+	private HashMap<String, Integer> _ids = new HashMap<String, Integer>();
+	private HTML _statusBarDiv = new HTML();
+	private ArrayList<String> _statusUpdates = new ArrayList<String>();
+	
+	private GS3MapLibrary _atlasInstance = null;
+	
+	public StatusBar(GS3MapLibrary atlasInstance)
+	{
+		_statusBarDiv.setStyleName("statusBar");
+		_atlasInstance = atlasInstance;
+	}
+	
+	public void addUpdate(String update, String id)
+	{
+		GS3MapLibrary.logToConsole("Got update -> " + update + " - " + id);
+		
+		if(_ids.get(id) != null)
+		{
+			return;
+		}
+
+		for(int j = 0; j < _statusUpdates.size(); j++)
+		{
+			if(_statusUpdates.get(j) == null)
+			{
+
+				_statusUpdates.add(j, update);
+				_ids.put(id, j);
+				updateStatusBar();
+				return;
+			}
+		}
+		
+		_ids.put(id, _statusUpdates.size());
+		_statusUpdates.add(update);
+		updateStatusBar();
+		_atlasInstance.resizeElements();
+	}
+	
+	public boolean removeUpdate(String updateId)
+	{
+		Integer index = _ids.get(updateId);
+		if(index == null)
+		{
+			return false;
+		}
+		
+		_ids.remove(updateId);
+		_statusUpdates.set(index, null);
+		updateStatusBar();
+		_atlasInstance.resizeElements();
+		return true;
+	}
+	
+	private void updateStatusBar()
+	{	
+		String status = "";
+		boolean noStatusUpdate = true;
+		
+		for(int j = 0; j < _statusUpdates.size(); j++)
+		{
+			if(_statusUpdates.get(j) != null)
+			{
+				if(noStatusUpdate)
+				{
+					noStatusUpdate = false;
+				}
+				else
+				{
+					status += " <br/> ";
+				}
+				
+				status += _statusUpdates.get(j);
+			}
+		}
+		
+		if(!status.equals(""))
+		{
+			GS3MapLibrary.logToConsole("Adding progress image");
+			status += "<br/><img src=\"http://localhost:8080/ATLAS/images/progress.gif\"></img>"; 
+		}
+		_statusBarDiv.setHTML(status);
+	}
+	
+	public HTML getStatusBarDiv()
+	{
+		return _statusBarDiv;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/TemporaryFrame.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/TemporaryFrame.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/client/TemporaryFrame.java	(revision 23906)
@@ -0,0 +1,98 @@
+package org.greenstone.client;
+
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.ui.Frame;
+
+public class TemporaryFrame
+{
+	protected static int _totalIndex = 0;
+	
+	protected Frame _frame = null;
+	
+	protected String _url = null;
+	protected String _text = null;
+	
+	protected boolean _doneLoading = false;
+	
+	protected int _index = _totalIndex++;
+	
+	public TemporaryFrame(String url)
+	{
+		_frame = new Frame();
+		_frame.setSize("0px", "0px");
+		_frame.getElement().getStyle().setProperty("border", "0px");
+		
+		_url = url;
+	}
+	
+	public void loadPage()
+	{
+		_frame.setUrl(_url);
+		
+		Timer loadTimer = new Timer()
+		{
+			int timerCount = 0;
+
+			public void run()
+			{
+				if (timerCount++ > 10)
+				{
+					_doneLoading = true;
+					this.cancel();
+					return;
+				}
+
+				if (GS3MapLibrary.tempFrameGetElementById(_index, "banner") != null && GS3MapLibrary.tempFrameGetElementById(_index, "content") != null && GS3MapLibrary.tempFrameGetElementById(_index, "footer") != null)
+				{
+					this.cancel();
+
+					JsArray<Element> divs = GS3MapLibrary.tempFrameGetElementsByTagName(_index, "div");
+
+					Element documentTextDiv = null;
+					for (int i = 0; i < divs.length(); i++)
+					{
+						if (divs.get(i).getAttribute("class").equals("documenttext"))
+						{
+							documentTextDiv = divs.get(i);
+							break;
+						}
+					}
+
+					if (documentTextDiv != null)
+					{
+						_text = documentTextDiv.getInnerText();
+					}
+					_doneLoading = true;
+				}
+			}
+		};
+		loadTimer.scheduleRepeating(1000);
+	}
+	
+	public boolean isFinishedLoading()
+	{
+		return _doneLoading;
+	}
+	
+	public static void setCurrentFrameIndex(int index)
+	{
+		_totalIndex = index;
+	}
+	
+	public void removeFromParent()
+	{
+		_frame.getElement().getParentElement().removeChild(_frame.getElement());
+	}
+	
+	public Frame getFrame()
+	{
+		return _frame;
+	}
+	
+	public String getText()
+	{
+		return _text;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/FindPlaceServiceImpl.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/FindPlaceServiceImpl.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/FindPlaceServiceImpl.java	(revision 23906)
@@ -0,0 +1,298 @@
+package org.greenstone.server;
+
+import java.net.URLConnection;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ArrayList;
+
+import org.greenstone.client.FindPlaceService;
+import org.greenstone.client.Place;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+/**
+ * The server side implementation of the RPC service.
+ */
+@SuppressWarnings("serial")
+public class FindPlaceServiceImpl extends RemoteServiceServlet implements FindPlaceService 
+{
+	protected PageScanner _pageScanner = null;
+	protected Statement _database = null;
+	protected ArrayList<Place> _places = null;
+
+	public Boolean loadGazetteer()
+	{
+		System.out.print("Loading Gazetteer... ");
+		_pageScanner = new PageScanner(this.getServletContext().getRealPath(""));
+		System.out.println("Done!");
+		return true;
+	}
+	
+	public String getMarkedUpText()
+	{
+		return _pageScanner.getMarkedUpText();
+	}
+	
+	public Boolean findPlacesInText(String text, String html)
+	{
+		_pageScanner.examineTextWithGate(text, html);	
+		return true;
+	}
+
+	public ArrayList<Place> getPlaces(double maxScore, double minScore, long minPopulation, boolean locality, boolean region, boolean country, int numberOfPlacesToGet) 
+	{
+		return _pageScanner.getPlacesWithParams(minScore, maxScore, minPopulation, locality, region, country, numberOfPlacesToGet);
+	}
+	
+	public Boolean adjustScoringParams(double penalty, double parentBonus, double indirectReferencePercent) 
+	{
+		_pageScanner.setScoringParams(penalty, parentBonus, indirectReferencePercent);
+		return true;
+	}
+	
+	public Boolean isGazetteerLoaded() 
+	{
+		return _pageScanner != null && _pageScanner.isGazetteerLoaded();
+	}
+
+	public ArrayList<ArrayList<Place>> findPlacesInMultipleTexts(String[] texts) 
+	{
+		return _pageScanner.examineMultipleTexts(texts);
+	}
+
+	public ArrayList<Place> updateGazetteer(int start, int end) 
+	{
+		if(_places == null)
+		{
+			if(_database == null)
+			{
+				try 
+				{
+					Class.forName("org.postgresql.Driver");
+					Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/GS3MapDB", "sam","password"); 
+					_database = c.createStatement();
+				}
+				catch (Exception ex) 
+				{
+					ex.printStackTrace();
+				}
+				
+			}
+			
+			System.out.println("Got connection");
+	
+			try
+			{
+				ResultSet results = _database.executeQuery("SELECT * FROM places WHERE latitude is null;");
+				
+				System.out.println("Got results");
+				
+				_places = new ArrayList<Place>();
+
+				while(results.next())
+				{
+					_places.add(Place.createPlaceFromInformation(results.getLong("id"), results.getString("placename"), results.getString("subregion"), results.getString("region"), results.getString("country"), results.getString("placetype"), null, null, results.getLong("population")));	
+				}
+			}
+			catch(Exception ex)
+			{
+				ex.printStackTrace();
+			}
+		}
+		
+		System.out.println("Making subset");
+		
+		ArrayList<Place> placeSubset = new ArrayList<Place>();
+		for(int i = start; i <= end; i++)
+		{
+			if(i < _places.size())
+			{
+				System.out.println("Adding place to subset " + i);
+				placeSubset.add(_places.get(i));
+			}
+		}
+		
+		System.out.println("Done making subset, places size = " + placeSubset.size());
+		
+		return placeSubset;
+	}
+	
+	public Boolean addToDatabase(Place p, float lat, float lng)
+	{
+		if(_database == null)
+		{
+			try 
+			{
+				Class.forName("org.postgresql.Driver");
+				Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/GS3MapDB", "sam","password"); 
+				_database = c.createStatement();
+			}
+			catch (Exception ex) 
+			{
+				ex.printStackTrace();
+			}
+		}
+		
+		String placeName = p.getName();
+		String parentPlaceName = p.getParentPlaceName();
+		
+		String firstParent = null;
+		String secondParent = null;
+		String thirdParent = null;
+		
+		//Separate the parents
+		if(parentPlaceName != null)
+		{
+			String[] parents = parentPlaceName.split(", ");
+			
+			if(parents.length == 1)
+			{
+				firstParent = parents[0];
+			}
+			else if(parents.length == 2)
+			{
+				firstParent = parents[0];
+				secondParent = parents[1];
+			}
+			else
+			{
+				firstParent = parents[0];
+				secondParent = parents[1];
+				thirdParent = parents[2];
+			}
+		}
+		
+		try
+		{
+			//If the place does not have a parent
+			if(parentPlaceName == null)
+			{
+				 _database.executeQuery("UPDATE places SET latitude = " + lat + ", longitude = " + lng  + " WHERE placename = \'" + placeName.replaceAll("\'", "\\\\\'") + "\' " + 
+						"and subregion is null " +
+						"and region is null " +
+						"and country is null;");
+			}
+			//If the place has one parent
+			else if(secondParent == null)
+			{
+				_database.executeQuery("UPDATE places SET latitude = " + lat + ", longitude = " + lng  + " WHERE placename = \'" + placeName.replaceAll("\'", "\\\\\'") + "\' " +
+						"and subregion is null " +
+						"and region is null " +
+						"and country = \'" + firstParent.replaceAll("\'", "\\\\\'") + "\';");
+			}
+			//If the place has two parents
+			else if(thirdParent == null)
+			{
+				_database.executeQuery("UPDATE places SET latitude = " + lat + ", longitude = " + lng  + " WHERE placename = \'" + placeName.replaceAll("\'", "\\\\\'") + "\' " +
+						"and subregion is null " +
+						"and region = \'" + firstParent.replaceAll("\'", "\\\\\'") + "\' " +
+						"and country = \'" + secondParent.replaceAll("\'", "\\\\\'") + "\';");
+			}
+			//If the place has three parents
+			else
+			{
+				_database.executeQuery("UPDATE places SET latitude = " + lat + ", longitude = " + lng  + " WHERE placename = \'" + placeName.replaceAll("\'", "\\\\\'") + "\' " +
+						"and subregion = \'" + firstParent.replaceAll("\'", "\\\\\'") + "\' " +
+						"and region = \'" + secondParent.replaceAll("\'", "\\\\\'") + "\' " +
+						"and country = \'" + thirdParent.replaceAll("\'", "\\\\\'") + "\';");
+			}
+			_database.close();
+			_database = null;
+		}
+		catch(Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		
+		return true;
+	}
+
+	public ArrayList<String> spatialSearch(ArrayList<Float[]> points) 
+	{
+		try 
+		{
+			System.out.println("Connecting to database for spatial search");
+			if(_database == null)
+			{
+				Class.forName("org.postgresql.Driver");
+				Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/GS3MapDB", "sam","password"); 
+				_database = c.createStatement();
+			}	
+			System.out.println("Done connecting to database");
+			
+			StringBuilder polygonString = new StringBuilder("'POLYGON((");
+			
+			for(int j = 0; j < points.size(); j++)
+			{
+				Float[] current = points.get(j);
+				if(j != 0)
+				{
+					polygonString.append(", " + current[0] + " " + current[1]);
+				}
+				else
+				{
+					polygonString.append(current[0] + " " + current[1]);
+				}
+			}
+			
+			polygonString.append(", " + points.get(0)[0] + " " + points.get(0)[1]);
+			
+			polygonString.append("))'");
+			
+			System.out.println("Polystring = " + polygonString);
+			
+			ResultSet results = _database.executeQuery("select placename from places where id in (select id from placepoints where ST_Within(point, " + polygonString + "));");
+			
+			ArrayList<String> placeNames = new ArrayList<String>();
+			while(results.next())
+			{
+				placeNames.add(results.getString("placename"));
+				System.out.println(results.getString("placename"));
+			}
+			
+			_database.close();
+			_database = null;
+			
+			return placeNames;
+		}
+		catch (Exception ex) 
+		{
+			ex.printStackTrace();
+			return null;
+		}
+	}
+	
+	public Place getPlaceById(long id)
+	{
+		try
+		{
+			if(_database == null)
+			{
+				System.out.print("Loading postgresql driver... ");
+				Class.forName("org.postgresql.Driver");
+				Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/GS3MapDB", "sam", "password");//DriverManager.getConnection("jdbc:postgresql://localhost/test", "sjm84", "password");
+				System.out.println("Done!");
+				_database = c.createStatement();
+			}
+			
+			ResultSet results = _database.executeQuery("SELECT * FROM places WHERE id = " + id);
+			if(!results.next())
+			{
+				return null;
+			}
+			
+			Place p = Place.createPlaceFromInformation(results.getLong("id"), results.getString("placename"), results.getString("subregion"), results.getString("region"), results.getString("country"), results.getString("placetype"), (Float) (results.getObject("longitude")), (Float) (results.getObject("latitude")), results.getLong("population"));
+			
+			results.close();
+			
+			return p;
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+			return null;
+		}
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GateScanner.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GateScanner.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GateScanner.java	(revision 23906)
@@ -0,0 +1,119 @@
+package org.greenstone.server;
+
+import gate.Annotation;
+import gate.AnnotationSet;
+import gate.Corpus;
+import gate.Document;
+import gate.Factory;
+import gate.FeatureMap;
+import gate.Gate;
+import gate.GateConstants;
+import gate.creole.ANNIEConstants;
+import gate.creole.SerialAnalyserController;
+import gate.util.GateException;
+import gate.util.Out;
+import gate.util.persistence.PersistenceManager;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.log4j.BasicConfigurator;
+
+public class GateScanner 
+{
+	private SerialAnalyserController annieController;
+
+	public GateScanner()
+	{
+		try 
+		{
+			BasicConfigurator.configure();
+			Gate.setGateHome(new File("C:\\Users\\sjm84\\Desktop\\stuff\\Gate5.2"));
+			Gate.setPluginsHome(new File("C:\\Users\\sjm84\\Desktop\\stuff\\Gate5.2\\plugins"));
+			Gate.setUserConfigFile(new File("C:\\Users\\sjm84\\Desktop\\stuff\\Gate5.2\\gate.xml"));
+			Gate.init();
+			Gate.getCreoleRegister().registerDirectories(new File("C:\\Users\\sjm84\\Desktop\\stuff\\Gate5.2\\plugins\\ANNIE").toURI().toURL()); 
+			Gate.getUserConfig().put(GateConstants.DOCUMENT_ADD_SPACE_ON_UNPACK_FEATURE_NAME, new Boolean(false));
+			
+		    Out.prln("Initialising ANNIE...");
+		
+			annieController = (SerialAnalyserController) PersistenceManager.loadObjectFromFile(new File(new File(Gate.getPluginsHome(), ANNIEConstants.PLUGIN_DIR), ANNIEConstants.DEFAULT_FILE));
+		
+		    Out.prln("...ANNIE loaded");
+		} 
+		catch (Exception e) 
+		{
+			e.printStackTrace();
+		}
+	}
+	
+	/** Tell ANNIE's controller about the corpus you want to run on */
+	public void setCorpus(Corpus corpus) 
+	{
+		annieController.setCorpus(corpus);
+	} // setCorpus
+
+	/** Run ANNIE */
+	public void execute() throws GateException 
+	{
+	    Out.prln("Running ANNIE...");
+	    annieController.execute();
+	    Out.prln("...ANNIE complete");
+	} // execute()
+	
+	public HashMap<String, Word> classifyText(String originalText)
+	{
+		HashMap<String, Word> words = new HashMap<String, Word>();
+		try
+		{
+		    Corpus corpus = (Corpus) Factory.createResource("gate.corpora.CorpusImpl");
+		    FeatureMap params = Factory.newFeatureMap();
+		    params.put("stringContent", "<html><head></head><body>" + originalText + "</body></html>");
+		    params.put("preserveOriginalContent", new Boolean(true));
+		    params.put("collectRepositioningInfo", new Boolean(true));
+		    Document doc = (Document) Factory.createResource("gate.corpora.DocumentImpl", params);
+		    corpus.add(doc);
+	
+		    // tell the pipeline about the corpus and run it
+		    this.setCorpus(corpus);
+		    this.execute();	    
+		    
+		    AnnotationSet annotations = doc.getAnnotations();
+		    
+		    for(int j = 0; j < annotations.size(); j++)
+		    {
+		    	Annotation currentAnnotation = annotations.get(j);
+		    	
+		    	if(currentAnnotation == null)
+		    	{
+		    		continue;
+		    	}
+		    	if(currentAnnotation.getType().equals("Token"))
+		    	{
+		    		Word w = null;
+		    		if(words.containsKey((String)currentAnnotation.getFeatures().get("string")))
+		    		{
+		    			w = words.get((String)currentAnnotation.getFeatures().get("string"));
+		    			w.addClassification((String)currentAnnotation.getFeatures().get("category"));
+		    			w.setValue((String)currentAnnotation.getFeatures().get("string"));
+		    		}
+		    		else
+		    		{
+		    			w = new Word();
+		    			w.addClassification((String)currentAnnotation.getFeatures().get("category"));
+		    			w.setValue((String)currentAnnotation.getFeatures().get("string"));
+		    			words.put(w.getValue(), w);
+		    		}
+		    	}
+		    }
+		}
+		catch(Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		
+	    return words;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerHelper.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerHelper.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerHelper.java	(revision 23906)
@@ -0,0 +1,191 @@
+package org.greenstone.server;
+
+import java.util.ArrayList;
+
+public class GazetteerHelper 
+{
+	/**
+	 * Gets the population from the line in the gazetteer file
+	 * @param columns is the line in the file that has been split
+	 * @return the population (if available)
+	 */
+	public static int getPopulation(String[] columns)
+	{
+		// If the row has a sixth element it is the population of the place
+		if (columns.length > 5 && columns[5].length() > 0) 
+		{
+			return Integer.parseInt(columns[5]);
+		}
+		return -1;
+	}
+	
+	/**
+	 * Gets the longitude from the line in the gazetteer file
+	 * @param columns is the line in the file that has been split
+	 * @return the longitude (if available)
+	 */
+	public static Float getLongitude(String[] columns)
+	{
+		try
+		{
+			// If the row has a seventh element it is the longitude of the place
+			if (columns.length > 7 && columns[7].length() > 0) 
+			{
+				return (Float.parseFloat(columns[7]) / 100);
+			}
+			return null;
+		}
+		catch(Exception ex)
+		{
+			return null;
+		}
+	}
+	
+	/**
+	 * Gets the latitude from the line in the gazetteer file
+	 * @param columns is the line in the file that has been split
+	 * @return the latitude (if available)
+	 */
+	public static Float getLatitude(String[] columns)
+	{
+		try
+		{
+			// If the row has a eighth element it is the latitude of the place
+			if (columns.length > 6 && columns[6].length() > 0) 
+			{
+				return (Float.parseFloat(columns[6]) / 100);
+			}
+			return null;
+		}
+		catch(Exception ex)
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * Gets the country name from the line in the gazetteer file
+	 * @param columns is the line in the file that has been split
+	 * @return the country name
+	 */
+	public static String getCountryName(String[] columns)
+	{
+		// If the row has a ninth element it is the name of the country that
+		// this place is in
+		if (columns.length > 8 && columns[8].length() > 0) 
+		{
+			return columns[8];
+		}
+		return null;
+	}
+	
+	/**
+	 * Gets the region name from the line in the gazetteer file
+	 * @param columns is the line in the file that has been split
+	 * @return the region name
+	 */
+	public static String getRegionName(String[] columns)
+	{
+		// If the row has a tenth element it is the name of the region in
+		// the country that this place is in
+		if (columns.length > 9 && columns[9].length() > 0) 
+		{
+			return columns[9];
+		}
+		return null;
+	}
+	
+	/**
+	 * Gets the sub region name from the line in the gazetteer file
+	 * @param columns is the line in the file that has been split
+	 * @return the sub region name
+	 */
+	public static String getSubRegionName(String[] columns)
+	{
+		// If the row has an eleventh element is is the name of the
+		// sub-region in the country that this place is in
+		if (columns.length > 10 && columns[10].length() > 0) 
+		{
+			return columns[10];
+		}
+		return null;
+	}
+	
+	/**
+	 * Gets the place type (e.g. county, locality etc.) from the line in the gazetteer file
+	 * @param columns is the line in the file that has been split
+	 * @return the place type
+	 */
+	public static String getPlaceType(String[] columns)
+	{
+		if (columns.length > 4 && columns[4].length() > 0) 
+		{
+			return columns[4];
+		}
+		return null;
+	}
+
+	/**
+	 * Gets the place name that this place is most commonly known as
+	 * @param columns is a tab seperated line in the gazetteer file that has been split
+	 * @return the most common place name
+	 */
+	public static String getMainPlaceName(String[] columns)
+	{
+		if(columns.length > 1 && columns[1].length() > 0)
+		{
+			return columns[1];
+		}
+		return null;
+	}
+	
+	/**
+	 * Adds the alternative names for a place to the list of alternative place names that map back to the most common place name
+	 * @param columns is a tab seperated line in the gazetteer file that has been split
+	 */
+	protected static ArrayList<String> getAlternativePlaceNames(String[] columns)
+	{
+		ArrayList<String> placeNames = new ArrayList<String>();
+		
+		//If the row has a 3rd element it is a list of alternate names for this place
+		if(columns.length > 2 && columns[2].length() > 0)
+		{
+			String[] names = columns[2].split(", ");
+			
+			for(String name : names)
+			{
+				if(!placeNames.contains(name))
+				{
+					placeNames.add(name);
+				}
+			}
+		}
+		
+		//If the row has a 4th element it is the name of this place in it's home language
+		if(columns.length > 3 && columns[3].length() > 0)
+		{
+			if(!placeNames.contains(columns[3]))
+			{
+				placeNames.add(columns[3]);
+			}
+		}
+		
+		return placeNames;
+	}
+	
+	protected static Integer getID(String[] columns)
+	{
+		try
+		{
+			if(columns.length > 0 && columns[0].length() > 0)
+			{
+				return Integer.parseInt(columns[0]);
+			}
+			return null;
+		}
+		catch(Exception ex)
+		{
+			return null;
+		}
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieFullNode.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieFullNode.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieFullNode.java	(revision 23906)
@@ -0,0 +1,104 @@
+package org.greenstone.server;
+
+import java.io.Serializable;
+
+public class GazetteerTrieFullNode implements Serializable
+{
+	private static final long serialVersionUID = 1L;
+	protected boolean _nameEnd = false;
+	protected GazetteerTrieFullNode[] _children = new GazetteerTrieFullNode[65000];
+	
+	/**
+	 * Basic constructor for a non-unicode trie node
+	 * @param nameEnd defines whether the new node represents the end of a place name
+	 */
+	protected GazetteerTrieFullNode(boolean nameEnd)
+	{
+		for(int i = 0; i < 65000; i++)
+		{
+			_children[i] = null;
+		}
+		_nameEnd = nameEnd;
+	}
+	
+	/**
+	 * Add a child to the current node
+	 * @param c is the character used to decide where to add the child node
+	 * @param nameEnd is whether or not this character represents the end of a place name
+	 * @return true if a child was added and false if one already existed
+	 */
+	protected boolean addChild(char c, boolean nameEnd)
+	{
+		if(c >= 65 && c <= 90)
+		{
+			c += 32;
+		}
+		//Add the ascii character if it either does not already exist or it signifies the end of a place name
+		if(_children[(int)c] != null)
+		{
+			if(_children[(int)c].isNameEnd() || nameEnd == false)
+			{
+				return false;
+			}
+			else
+			{
+				_children[(int)c].setNameEnd(true);
+				return true;
+			}
+		}
+		else
+		{
+			_children[(int)c] = new GazetteerTrieFullNode(nameEnd);
+			return true;
+		}
+	}
+	
+	/**
+	 * @return whether or not this node represents the end of a place name
+	 */
+	protected boolean isNameEnd()
+	{
+		return _nameEnd;
+	}
+	
+	/**
+	 * Set whether or not this node represents the end of a place name
+	 * @param nameEnd is the value to set it to
+	 */
+	protected void setNameEnd(boolean nameEnd)
+	{
+		_nameEnd = nameEnd;
+	}
+	
+	/**
+	 * Returns the child at the given index
+	 * @param index is the index of the child to return
+	 * @return the child at the given index
+	 */
+	protected GazetteerTrieFullNode getChild(char c)
+	{
+		if(c >= 65 && c <= 90)
+		{
+			c += 32;
+		}
+		return _children[(int)c];
+	}
+	
+	public void output()
+	{
+		/*
+		if(_unicodeCharacters != null && _unicodeCharacters.size() >= 10)
+		{
+			System.out.println(_unicodeCharacters.size());
+		}
+		
+		for(GazetteerTrieFullNode g : _children)
+		{
+			if(g != null)
+			{
+				g.output();
+			}
+		}
+		*/
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieNode.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieNode.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieNode.java	(revision 23906)
@@ -0,0 +1,156 @@
+package org.greenstone.server;
+
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+public class GazetteerTrieNode implements Serializable
+{
+	private static final long serialVersionUID = 8121262849365540679L;
+	protected boolean _nameEnd = false;
+	protected GazetteerTrieNode[] _children = new GazetteerTrieNode[26];
+	protected HashMap<Character,GazetteerTrieNode> _unicodeCharacters = null;
+	
+	/**
+	 * Basic constructor for a non-unicode trie node
+	 * @param nameEnd defines whether the new node represents the end of a place name
+	 */
+	protected GazetteerTrieNode(boolean nameEnd)
+	{
+		for(int i = 0; i < 26; i++)
+		{
+			_children[i] = null;
+		}
+		_nameEnd = nameEnd;
+	}
+	
+	/**
+	 * Add a child to the current node
+	 * @param c is the character used to decide where to add the child node
+	 * @param nameEnd is whether or not this character represents the end of a place name
+	 * @return true if a child was added and false if one already existed
+	 */
+	protected boolean addChild(char c, boolean nameEnd)
+	{
+		//If the characters is an ASCII letter
+		int index;
+		if(c >= 'A' && c <= 'Z')
+		{
+			index = c - 65;
+		}
+		else if(c >= 'a' && c <= 'z')
+		{
+			index = c - 97; 
+		}
+		//If the character is not an ASCII letter
+		else
+		{		
+			//If the node does not currently have a unicode character map then make one
+			if(_unicodeCharacters == null)
+			{
+				_unicodeCharacters = new HashMap<Character, GazetteerTrieNode>();
+				_unicodeCharacters.put(c, new GazetteerTrieNode(nameEnd));
+				return true;
+			}
+			
+			//Add the unicode character to the map only if it either does not exist already or signifies the end of a place name
+			GazetteerTrieNode value;
+			if((value = _unicodeCharacters.get(c)) != null)
+			{
+				if(value.isNameEnd())
+				{
+					return false;
+				}
+				else
+				{
+					value.setNameEnd(nameEnd);
+				}
+			}
+			else
+			{
+				_unicodeCharacters.put(c, new GazetteerTrieNode(nameEnd));
+			}
+			
+			return true;
+		}
+		
+		//Add the ascii character if it either does not already exist or it signifies the end of a place name
+		if(_children[index] != null)
+		{
+			if(_children[index].isNameEnd() || nameEnd == false)
+			{
+				return false;
+			}
+			else
+			{
+				_children[index].setNameEnd(true);
+				return true;
+			}
+		}
+		else
+		{
+			_children[index] = new GazetteerTrieNode(nameEnd);
+			return true;
+		}
+	}
+	
+	/**
+	 * @return whether or not this node represents the end of a place name
+	 */
+	protected boolean isNameEnd()
+	{
+		return _nameEnd;
+	}
+	
+	/**
+	 * Set whether or not this node represents the end of a place name
+	 * @param nameEnd is the value to set it to
+	 */
+	protected void setNameEnd(boolean nameEnd)
+	{
+		_nameEnd = nameEnd;
+	}
+	
+	/**
+	 * Returns the child at the given index
+	 * @param index is the index of the child to return
+	 * @return the child at the given index
+	 */
+	protected GazetteerTrieNode getChild(char c)
+	{
+		int index = -1;
+		if(c >= 'A' && c <= 'Z')
+		{
+			index = c - 65;
+		}
+		else if(c >= 'a' && c <= 'z')
+		{
+			index = c - 97; 
+		}
+		if(index == -1)
+		{
+			if(_unicodeCharacters == null)
+			{
+				return null;
+			}
+			return _unicodeCharacters.get(c);
+		}
+		return _children[index];
+	}
+	
+	public void output()
+	{
+		if(_unicodeCharacters != null && _unicodeCharacters.size() >= 10)
+		{
+			System.out.println(_unicodeCharacters.size());
+		}
+		
+		for(GazetteerTrieNode g : _children)
+		{
+			if(g != null)
+			{
+				g.output();
+			}
+		}
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieSmallNode.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieSmallNode.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieSmallNode.java	(revision 23906)
@@ -0,0 +1,117 @@
+package org.greenstone.server;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+public class GazetteerTrieSmallNode implements Serializable
+{
+	private static final long serialVersionUID = 8121262849365540679L;
+	protected boolean _nameEnd = false;
+	// protected GazetteerTrieSmallNode[] _children = new
+	// GazetteerTrieSmallNode[26];
+	protected HashMap<Character, GazetteerTrieSmallNode> _unicodeCharacters = null;
+
+	/**
+	 * Basic constructor for a non-unicode trie node
+	 * 
+	 * @param nameEnd
+	 *            defines whether the new node represents the end of a place
+	 *            name
+	 */
+	protected GazetteerTrieSmallNode(boolean nameEnd)
+	{
+		_nameEnd = nameEnd;
+	}
+
+	/**
+	 * Add a child to the current node
+	 * 
+	 * @param c
+	 *            is the character used to decide where to add the child node
+	 * @param nameEnd
+	 *            is whether or not this character represents the end of a place
+	 *            name
+	 * @return true if a child was added and false if one already existed
+	 */
+	protected boolean addChild(char c, boolean nameEnd)
+	{
+		if (c >= 65 && c <= 90)
+		{
+			c += 32;
+		}
+		// If the node does not currently have a unicode character map then make
+		// one
+		if (_unicodeCharacters == null)
+		{
+			_unicodeCharacters = new HashMap<Character, GazetteerTrieSmallNode>();
+			_unicodeCharacters.put(c, new GazetteerTrieSmallNode(nameEnd));
+			return true;
+		}
+
+		// Add the unicode character to the map only if it either does not exist
+		// already or signifies the end of a place name
+		GazetteerTrieSmallNode value;
+		if ((value = _unicodeCharacters.get(c)) != null)
+		{
+			if (value.isNameEnd())
+			{
+				return false;
+			}
+			else
+			{
+				value.setNameEnd(nameEnd);
+			}
+		}
+		else
+		{
+			_unicodeCharacters.put(c, new GazetteerTrieSmallNode(nameEnd));
+		}
+
+		return true;
+	}
+
+	/**
+	 * @return whether or not this node represents the end of a place name
+	 */
+	protected boolean isNameEnd()
+	{
+		return _nameEnd;
+	}
+
+	/**
+	 * Set whether or not this node represents the end of a place name
+	 * 
+	 * @param nameEnd
+	 *            is the value to set it to
+	 */
+	protected void setNameEnd(boolean nameEnd)
+	{
+		_nameEnd = nameEnd;
+	}
+
+	/**
+	 * Returns the child at the given index
+	 * 
+	 * @param index
+	 *            is the index of the child to return
+	 * @return the child at the given index
+	 */
+	protected GazetteerTrieSmallNode getChild(char c)
+	{
+		if (c >= 65 && c <= 90)
+		{
+			c += 32;
+		}
+
+		if (_unicodeCharacters == null)
+		{
+			return null;
+		}
+		return _unicodeCharacters.get(c);
+	}
+
+	public void output()
+	{
+		System.out.println("Please implement a output method for GazetteerTrieSmallNode");
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieTopLevelNode.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieTopLevelNode.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieTopLevelNode.java	(revision 23906)
@@ -0,0 +1,104 @@
+package org.greenstone.server;
+
+import java.io.Serializable;
+
+public class GazetteerTrieTopLevelNode implements Serializable
+{
+	private static final long serialVersionUID = 1L;
+	protected boolean _nameEnd = false;
+	protected GazetteerTrieNode[] _children = new GazetteerTrieNode[65000];
+	
+	/**
+	 * Basic constructor for a non-unicode trie node
+	 * @param nameEnd defines whether the new node represents the end of a place name
+	 */
+	protected GazetteerTrieTopLevelNode(boolean nameEnd)
+	{
+		for(int i = 0; i < 65000; i++)
+		{
+			_children[i] = null;
+		}
+		_nameEnd = nameEnd;
+	}
+	
+	/**
+	 * Add a child to the current node
+	 * @param c is the character used to decide where to add the child node
+	 * @param nameEnd is whether or not this character represents the end of a place name
+	 * @return true if a child was added and false if one already existed
+	 */
+	protected boolean addChild(char c, boolean nameEnd)
+	{
+		if(c >= 65 && c <= 90)
+		{
+			c += 32;
+		}
+		//Add the ascii character if it either does not already exist or it signifies the end of a place name
+		if(_children[(int)c] != null)
+		{
+			if(_children[(int)c].isNameEnd() || nameEnd == false)
+			{
+				return false;
+			}
+			else
+			{
+				_children[(int)c].setNameEnd(true);
+				return true;
+			}
+		}
+		else
+		{
+			_children[(int)c] = new GazetteerTrieNode(nameEnd);
+			return true;
+		}
+	}
+	
+	/**
+	 * @return whether or not this node represents the end of a place name
+	 */
+	protected boolean isNameEnd()
+	{
+		return _nameEnd;
+	}
+	
+	/**
+	 * Set whether or not this node represents the end of a place name
+	 * @param nameEnd is the value to set it to
+	 */
+	protected void setNameEnd(boolean nameEnd)
+	{
+		_nameEnd = nameEnd;
+	}
+	
+	/**
+	 * Returns the child at the given index
+	 * @param index is the index of the child to return
+	 * @return the child at the given index
+	 */
+	protected GazetteerTrieNode getChild(char c)
+	{
+		if(c >= 65 && c <= 90)
+		{
+			c += 32;
+		}
+		return _children[(int)c];
+	}
+	
+	public void output()
+	{
+		/*
+		if(_unicodeCharacters != null && _unicodeCharacters.size() >= 10)
+		{
+			System.out.println(_unicodeCharacters.size());
+		}
+		
+		for(GazetteerTrieNode g : _children)
+		{
+			if(g != null)
+			{
+				g.output();
+			}
+		}
+		*/
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType1.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType1.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType1.java	(revision 23906)
@@ -0,0 +1,316 @@
+package org.greenstone.server;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.Serializable;
+import java.sql.Statement;
+import java.util.ArrayList;
+
+public class GazetteerTrieType1 implements Serializable
+{
+	private static final long serialVersionUID = -959184305931535981L;
+
+	int _nameCount = 0;
+
+	//GazetteerTrieNode _topLevelNode = new GazetteerTrieNode(false);
+	GazetteerTrieFullNode _topLevelNode = new GazetteerTrieFullNode(false);
+
+	Statement _database = null;
+
+	/**
+	 * Default constructor
+	 */
+	public GazetteerTrieType1()
+	{
+	}
+
+	/**
+	 * Contructor that takes a filename to generate the gazetteer
+	 * 
+	 * @param filename
+	 *            is the name of the file to use to generate the gazetteer with
+	 */
+	public GazetteerTrieType1(String filename)
+	{
+		System.out.println("Loading " + filename + " as gazetteer");
+		try
+		{
+			BufferedReader gazetteerFile = new BufferedReader(new FileReader(filename));
+			String line = "";
+
+			ArrayList<String> allPlaceNames = new ArrayList<String>();
+
+			int count = 0;
+			while ((line = gazetteerFile.readLine()) != null)
+			{
+				if(count++ % 10000 == 0){System.out.println(count + " entries loaded");}
+				// The file is tab seperated so split it by tabs
+				String[] columns = line.split("\t");
+
+				ArrayList<String> placeNames = new ArrayList<String>();
+
+				String mainPlaceName = GazetteerHelper.getMainPlaceName(columns);
+				if (mainPlaceName != null)
+				{
+					placeNames.add(mainPlaceName);
+				}
+
+				ArrayList<String> alternatePlaceNames = GazetteerHelper.getAlternativePlaceNames(columns);
+				if (alternatePlaceNames != null)
+				{
+					placeNames.addAll(alternatePlaceNames);
+				}
+
+				allPlaceNames.addAll(placeNames);
+
+				// Add the place names to the trie
+				for (String placeName : placeNames)
+				{
+					this.addPlaceName(placeName);
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		removeAmbiguousPlaceNames();
+	}
+
+	/**
+	 * Removes a place name from the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to be removed
+	 * @return true if sucessful and false if the place name did not exist
+	 */
+
+	public boolean removePlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			_topLevelNode.setNameEnd(false);
+			return true;
+		}
+		if (placeName.length() == 1)
+		{
+			_topLevelNode.getChild(placeName.charAt(0)).setNameEnd(false);
+			return true;
+		}
+		GazetteerTrieFullNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		if(currentNode == null)
+		{
+			System.out.println(placeName.charAt(0) + " = null?");
+			return false;
+		}
+		
+		for (int i = 1; i < placeName.length(); i++)
+		{
+			currentNode = currentNode.getChild(placeName.charAt(i));
+			if (currentNode == null)
+			{
+				return false;
+			}
+		}
+
+		currentNode.setNameEnd(false);
+		return true;
+	}
+
+	/**
+	 * Adds a place name to the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to add
+	 */
+	public void addPlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			return;
+		}
+
+		if (placeName.length() == 1)
+		{
+			_topLevelNode.addChild(placeName.charAt(0), true);
+			return;
+		}
+
+		_topLevelNode.addChild(placeName.charAt(0), false);
+		GazetteerTrieFullNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		for (int i = 1; i < placeName.length() - 1; i++)
+		{
+			currentNode.addChild(placeName.charAt(i), false);
+			currentNode = currentNode.getChild(placeName.charAt(i));
+		}
+
+		currentNode.addChild(placeName.charAt(placeName.length() - 1), true);
+	}
+
+	/**
+	 * Checks to see if a place name exists in the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to check
+	 * @return 1 if the place name exists 0 if the place name does not exist but
+	 *         there might be a match further down the trie -1 if the place name
+	 *         does not exist and the trie is at a dead end
+	 */
+	public int checkPlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			return 0;
+		}
+
+		if (placeName.length() == 1)
+		{
+			GazetteerTrieFullNode node = _topLevelNode.getChild(placeName.charAt(0));
+			if (node == null)
+			{
+				return -1;
+			}
+
+			return node.isNameEnd() ? 1 : 0;
+		}
+
+		GazetteerTrieFullNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		if (currentNode == null)
+		{
+			return -1;
+		}
+		for (int i = 1; i < placeName.length(); i++)
+		{
+			currentNode = currentNode.getChild(placeName.charAt(i));
+			if (currentNode == null)
+			{
+				return -1;
+			}
+		}
+
+		if (currentNode.isNameEnd())
+		{
+			return 1;
+		}
+		else
+		{
+			return 0;
+		}
+	}
+	
+	/**
+	 * Removes place names that are unlikely to be meant as place names in a given text
+	 * @param gazetteer is the gazetteer to remove the place names from
+	 */
+	public void removeAmbiguousPlaceNames()
+	{
+		removePlaceName("are");				removePlaceName("is");
+		removePlaceName("over");			removePlaceName("at");
+		removePlaceName("of");				removePlaceName("to");
+		removePlaceName("rule");			removePlaceName("time");
+		removePlaceName("real");			removePlaceName("national");
+		removePlaceName("early");			removePlaceName("by");
+		removePlaceName("as");				removePlaceName("eastern");
+		removePlaceName("western");			removePlaceName("southern");
+		removePlaceName("northern");		removePlaceName("east");
+		removePlaceName("west");			removePlaceName("south");
+		removePlaceName("north");			removePlaceName("this");
+		removePlaceName("between");			removePlaceName("many");
+		removePlaceName("strong");			removePlaceName("economy");
+		removePlaceName("mall");			removePlaceName("they");
+		removePlaceName("do");				removePlaceName("image");
+		removePlaceName("republic");		removePlaceName("section");
+		removePlaceName("dollar");			removePlaceName("index");
+		removePlaceName("day");				removePlaceName("council");
+		removePlaceName("use");				removePlaceName("log");
+		removePlaceName("logo");			removePlaceName("best");
+		removePlaceName("go");				removePlaceName("portal");
+		removePlaceName("list");			removePlaceName("english");
+		removePlaceName("page");			removePlaceName("see");
+		removePlaceName("ocean");			removePlaceName("island");
+		removePlaceName("x");				removePlaceName("country");
+		removePlaceName("colony");			removePlaceName("christian");
+		removePlaceName("black");			removePlaceName("independence");
+		removePlaceName("war");				removePlaceName("no");
+		removePlaceName("continental");		removePlaceName("continental");
+		removePlaceName("force");			removePlaceName("reform");
+		removePlaceName("rush");			removePlaceName("read");
+		removePlaceName("none");			removePlaceName("justice");
+		removePlaceName("font");			removePlaceName("u");
+		removePlaceName("y");				removePlaceName("normal");
+		removePlaceName("center");			removePlaceName("date");
+		removePlaceName("story");			removePlaceName("union");
+		removePlaceName("supreme");			removePlaceName("house");
+		removePlaceName("court");			removePlaceName("data");
+		removePlaceName("energy");			removePlaceName("white");
+		removePlaceName("universal");		removePlaceName("protection");
+		removePlaceName("great");			removePlaceName("star");
+		removePlaceName("banner");			removePlaceName("capital");
+		removePlaceName("much");			removePlaceName("sidney");
+		removePlaceName("media");			removePlaceName("protection");
+		
+		addPlaceName("United States");
+	}
+}
+
+// TreeMap<Character, Integer> charCount = new TreeMap<Character, Integer>();
+// int upperCount = 0;
+// int lowerCount = 0;
+// int numbers = 0;
+// int arabicCharacters = 0;
+// int chineseCharacters = 0;
+
+// if (!charCount.containsKey(placeName.charAt(placeName.length() - 1)))
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1),
+// charCount.get(placeName.charAt(placeName.length() - 1)) + 1);
+// }
+
+// for (Character c : charCount.keySet())
+// {
+// System.out.println(c + " (" + (int) c + ") -> " + charCount.get(c));
+// }
+//
+// System.out.println("Upper -> " + upperCount);
+// System.out.println("Lower -> " + lowerCount);
+// System.out.println("Number -> " + numbers);
+// System.out.println("Arabic -> " + arabicCharacters);
+// System.out.println("Chinese -> " + chineseCharacters);
+
+// if (placeName.charAt(i) >= 'A' && placeName.charAt(i) <= 'Z')
+// {
+// upperCount++;
+// }
+//
+// if (placeName.charAt(i) >= 'a' && placeName.charAt(i) <= 'z')
+// {
+// lowerCount++;
+// }
+//
+// if (placeName.charAt(i) >= '0' && placeName.charAt(i) <= '9')
+// {
+// numbers++;
+// }
+//
+// if (placeName.charAt(i) >= 1569 && placeName.charAt(i) <= 1711)
+// {
+// arabicCharacters++;
+// }
+//
+// if (placeName.charAt(i) >= 12293 && placeName.charAt(i) <= 64016)
+// {
+// chineseCharacters++;
+// }
+//
+// if (!charCount.containsKey(placeName.charAt(i)))
+// {
+// charCount.put(placeName.charAt(i), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(i), charCount.get(placeName.charAt(i)) + 1);
+// }
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType2.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType2.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType2.java	(revision 23906)
@@ -0,0 +1,316 @@
+package org.greenstone.server;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.Serializable;
+import java.sql.Statement;
+import java.util.ArrayList;
+
+public class GazetteerTrieType2 implements Serializable
+{
+	private static final long serialVersionUID = -959184305931535981L;
+
+	int _nameCount = 0;
+
+	GazetteerTrieNode _topLevelNode = new GazetteerTrieNode(false);
+	//GazetteerTrieTopLevelNode _topLevelNode = new GazetteerTrieTopLevelNode(false);
+
+	Statement _database = null;
+
+	/**
+	 * Default constructor
+	 */
+	public GazetteerTrieType2()
+	{
+	}
+
+	/**
+	 * Contructor that takes a filename to generate the gazetteer
+	 * 
+	 * @param filename
+	 *            is the name of the file to use to generate the gazetteer with
+	 */
+	public GazetteerTrieType2(String filename)
+	{
+		System.out.println("Loading " + filename + " as gazetteer");
+		try
+		{
+			BufferedReader gazetteerFile = new BufferedReader(new FileReader(filename));
+			String line = "";
+
+			ArrayList<String> allPlaceNames = new ArrayList<String>();
+
+			int count = 0;
+			while ((line = gazetteerFile.readLine()) != null)
+			{
+				if(count++ % 10000 == 0){System.out.println(count + " entries loaded");}
+				// The file is tab seperated so split it by tabs
+				String[] columns = line.split("\t");
+
+				ArrayList<String> placeNames = new ArrayList<String>();
+
+				String mainPlaceName = GazetteerHelper.getMainPlaceName(columns);
+				if (mainPlaceName != null)
+				{
+					placeNames.add(mainPlaceName);
+				}
+
+				ArrayList<String> alternatePlaceNames = GazetteerHelper.getAlternativePlaceNames(columns);
+				if (alternatePlaceNames != null)
+				{
+					placeNames.addAll(alternatePlaceNames);
+				}
+
+				allPlaceNames.addAll(placeNames);
+
+				// Add the place names to the trie
+				for (String placeName : placeNames)
+				{
+					this.addPlaceName(placeName);
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		removeAmbiguousPlaceNames();
+	}
+
+	/**
+	 * Removes a place name from the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to be removed
+	 * @return true if sucessful and false if the place name did not exist
+	 */
+
+	public boolean removePlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			_topLevelNode.setNameEnd(false);
+			return true;
+		}
+		if (placeName.length() == 1)
+		{
+			_topLevelNode.getChild(placeName.charAt(0)).setNameEnd(false);
+			return true;
+		}
+		GazetteerTrieNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		if(currentNode == null)
+		{
+			System.out.println(placeName.charAt(0) + " = null?");
+			return false;
+		}
+		
+		for (int i = 1; i < placeName.length(); i++)
+		{
+			currentNode = currentNode.getChild(placeName.charAt(i));
+			if (currentNode == null)
+			{
+				return false;
+			}
+		}
+
+		currentNode.setNameEnd(false);
+		return true;
+	}
+
+	/**
+	 * Adds a place name to the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to add
+	 */
+	public void addPlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			return;
+		}
+
+		if (placeName.length() == 1)
+		{
+			_topLevelNode.addChild(placeName.charAt(0), true);
+			return;
+		}
+
+		_topLevelNode.addChild(placeName.charAt(0), false);
+		GazetteerTrieNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		for (int i = 1; i < placeName.length() - 1; i++)
+		{
+			currentNode.addChild(placeName.charAt(i), false);
+			currentNode = currentNode.getChild(placeName.charAt(i));
+		}
+
+		currentNode.addChild(placeName.charAt(placeName.length() - 1), true);
+	}
+
+	/**
+	 * Checks to see if a place name exists in the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to check
+	 * @return 1 if the place name exists 0 if the place name does not exist but
+	 *         there might be a match further down the trie -1 if the place name
+	 *         does not exist and the trie is at a dead end
+	 */
+	public int checkPlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			return 0;
+		}
+
+		if (placeName.length() == 1)
+		{
+			GazetteerTrieNode node = _topLevelNode.getChild(placeName.charAt(0));
+			if (node == null)
+			{
+				return -1;
+			}
+
+			return node.isNameEnd() ? 1 : 0;
+		}
+
+		GazetteerTrieNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		if (currentNode == null)
+		{
+			return -1;
+		}
+		for (int i = 1; i < placeName.length(); i++)
+		{
+			currentNode = currentNode.getChild(placeName.charAt(i));
+			if (currentNode == null)
+			{
+				return -1;
+			}
+		}
+
+		if (currentNode.isNameEnd())
+		{
+			return 1;
+		}
+		else
+		{
+			return 0;
+		}
+	}
+	
+	/**
+	 * Removes place names that are unlikely to be meant as place names in a given text
+	 * @param gazetteer is the gazetteer to remove the place names from
+	 */
+	public void removeAmbiguousPlaceNames()
+	{
+		removePlaceName("are");				removePlaceName("is");
+		removePlaceName("over");			removePlaceName("at");
+		removePlaceName("of");				removePlaceName("to");
+		removePlaceName("rule");			removePlaceName("time");
+		removePlaceName("real");			removePlaceName("national");
+		removePlaceName("early");			removePlaceName("by");
+		removePlaceName("as");				removePlaceName("eastern");
+		removePlaceName("western");			removePlaceName("southern");
+		removePlaceName("northern");		removePlaceName("east");
+		removePlaceName("west");			removePlaceName("south");
+		removePlaceName("north");			removePlaceName("this");
+		removePlaceName("between");			removePlaceName("many");
+		removePlaceName("strong");			removePlaceName("economy");
+		removePlaceName("mall");			removePlaceName("they");
+		removePlaceName("do");				removePlaceName("image");
+		removePlaceName("republic");		removePlaceName("section");
+		removePlaceName("dollar");			removePlaceName("index");
+		removePlaceName("day");				removePlaceName("council");
+		removePlaceName("use");				removePlaceName("log");
+		removePlaceName("logo");			removePlaceName("best");
+		removePlaceName("go");				removePlaceName("portal");
+		removePlaceName("list");			removePlaceName("english");
+		removePlaceName("page");			removePlaceName("see");
+		removePlaceName("ocean");			removePlaceName("island");
+		removePlaceName("x");				removePlaceName("country");
+		removePlaceName("colony");			removePlaceName("christian");
+		removePlaceName("black");			removePlaceName("independence");
+		removePlaceName("war");				removePlaceName("no");
+		removePlaceName("continental");		removePlaceName("continental");
+		removePlaceName("force");			removePlaceName("reform");
+		removePlaceName("rush");			removePlaceName("read");
+		removePlaceName("none");			removePlaceName("justice");
+		removePlaceName("font");			removePlaceName("u");
+		removePlaceName("y");				removePlaceName("normal");
+		removePlaceName("center");			removePlaceName("date");
+		removePlaceName("story");			removePlaceName("union");
+		removePlaceName("supreme");			removePlaceName("house");
+		removePlaceName("court");			removePlaceName("data");
+		removePlaceName("energy");			removePlaceName("white");
+		removePlaceName("universal");		removePlaceName("protection");
+		removePlaceName("great");			removePlaceName("star");
+		removePlaceName("banner");			removePlaceName("capital");
+		removePlaceName("much");			removePlaceName("sidney");
+		removePlaceName("media");			removePlaceName("protection");
+		
+		addPlaceName("United States");
+	}
+}
+
+// TreeMap<Character, Integer> charCount = new TreeMap<Character, Integer>();
+// int upperCount = 0;
+// int lowerCount = 0;
+// int numbers = 0;
+// int arabicCharacters = 0;
+// int chineseCharacters = 0;
+
+// if (!charCount.containsKey(placeName.charAt(placeName.length() - 1)))
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1),
+// charCount.get(placeName.charAt(placeName.length() - 1)) + 1);
+// }
+
+// for (Character c : charCount.keySet())
+// {
+// System.out.println(c + " (" + (int) c + ") -> " + charCount.get(c));
+// }
+//
+// System.out.println("Upper -> " + upperCount);
+// System.out.println("Lower -> " + lowerCount);
+// System.out.println("Number -> " + numbers);
+// System.out.println("Arabic -> " + arabicCharacters);
+// System.out.println("Chinese -> " + chineseCharacters);
+
+// if (placeName.charAt(i) >= 'A' && placeName.charAt(i) <= 'Z')
+// {
+// upperCount++;
+// }
+//
+// if (placeName.charAt(i) >= 'a' && placeName.charAt(i) <= 'z')
+// {
+// lowerCount++;
+// }
+//
+// if (placeName.charAt(i) >= '0' && placeName.charAt(i) <= '9')
+// {
+// numbers++;
+// }
+//
+// if (placeName.charAt(i) >= 1569 && placeName.charAt(i) <= 1711)
+// {
+// arabicCharacters++;
+// }
+//
+// if (placeName.charAt(i) >= 12293 && placeName.charAt(i) <= 64016)
+// {
+// chineseCharacters++;
+// }
+//
+// if (!charCount.containsKey(placeName.charAt(i)))
+// {
+// charCount.put(placeName.charAt(i), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(i), charCount.get(placeName.charAt(i)) + 1);
+// }
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType3.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType3.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType3.java	(revision 23906)
@@ -0,0 +1,316 @@
+package org.greenstone.server;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.Serializable;
+import java.sql.Statement;
+import java.util.ArrayList;
+
+public class GazetteerTrieType3 implements Serializable
+{
+	private static final long serialVersionUID = -959184305931535981L;
+
+	int _nameCount = 0;
+
+	//GazetteerTrieNode _topLevelNode = new GazetteerTrieNode(false);
+	GazetteerTrieTopLevelNode _topLevelNode = new GazetteerTrieTopLevelNode(false);
+
+	Statement _database = null;
+
+	/**
+	 * Default constructor
+	 */
+	public GazetteerTrieType3()
+	{
+	}
+
+	/**
+	 * Contructor that takes a filename to generate the gazetteer
+	 * 
+	 * @param filename
+	 *            is the name of the file to use to generate the gazetteer with
+	 */
+	public GazetteerTrieType3(String filename)
+	{
+		System.out.println("Loading " + filename + " as gazetteer");
+		try
+		{
+			BufferedReader gazetteerFile = new BufferedReader(new FileReader(filename));
+			String line = "";
+
+			ArrayList<String> allPlaceNames = new ArrayList<String>();
+
+			int count = 0;
+			while ((line = gazetteerFile.readLine()) != null)
+			{
+				if(count++ % 10000 == 0){System.out.println(count + " entries loaded");}
+				// The file is tab seperated so split it by tabs
+				String[] columns = line.split("\t");
+
+				ArrayList<String> placeNames = new ArrayList<String>();
+
+				String mainPlaceName = GazetteerHelper.getMainPlaceName(columns);
+				if (mainPlaceName != null)
+				{
+					placeNames.add(mainPlaceName);
+				}
+
+				ArrayList<String> alternatePlaceNames = GazetteerHelper.getAlternativePlaceNames(columns);
+				if (alternatePlaceNames != null)
+				{
+					placeNames.addAll(alternatePlaceNames);
+				}
+
+				allPlaceNames.addAll(placeNames);
+
+				// Add the place names to the trie
+				for (String placeName : placeNames)
+				{
+					this.addPlaceName(placeName);
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		removeAmbiguousPlaceNames();
+	}
+
+	/**
+	 * Removes a place name from the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to be removed
+	 * @return true if sucessful and false if the place name did not exist
+	 */
+
+	public boolean removePlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			_topLevelNode.setNameEnd(false);
+			return true;
+		}
+		if (placeName.length() == 1)
+		{
+			_topLevelNode.getChild(placeName.charAt(0)).setNameEnd(false);
+			return true;
+		}
+		GazetteerTrieNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		if(currentNode == null)
+		{
+			System.out.println(placeName.charAt(0) + " = null?");
+			return false;
+		}
+		
+		for (int i = 1; i < placeName.length(); i++)
+		{
+			currentNode = currentNode.getChild(placeName.charAt(i));
+			if (currentNode == null)
+			{
+				return false;
+			}
+		}
+
+		currentNode.setNameEnd(false);
+		return true;
+	}
+
+	/**
+	 * Adds a place name to the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to add
+	 */
+	public void addPlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			return;
+		}
+
+		if (placeName.length() == 1)
+		{
+			_topLevelNode.addChild(placeName.charAt(0), true);
+			return;
+		}
+
+		_topLevelNode.addChild(placeName.charAt(0), false);
+		GazetteerTrieNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		for (int i = 1; i < placeName.length() - 1; i++)
+		{
+			currentNode.addChild(placeName.charAt(i), false);
+			currentNode = currentNode.getChild(placeName.charAt(i));
+		}
+
+		currentNode.addChild(placeName.charAt(placeName.length() - 1), true);
+	}
+
+	/**
+	 * Checks to see if a place name exists in the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to check
+	 * @return 1 if the place name exists 0 if the place name does not exist but
+	 *         there might be a match further down the trie -1 if the place name
+	 *         does not exist and the trie is at a dead end
+	 */
+	public int checkPlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			return 0;
+		}
+
+		if (placeName.length() == 1)
+		{
+			GazetteerTrieNode node = _topLevelNode.getChild(placeName.charAt(0));
+			if (node == null)
+			{
+				return -1;
+			}
+
+			return node.isNameEnd() ? 1 : 0;
+		}
+
+		GazetteerTrieNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		if (currentNode == null)
+		{
+			return -1;
+		}
+		for (int i = 1; i < placeName.length(); i++)
+		{
+			currentNode = currentNode.getChild(placeName.charAt(i));
+			if (currentNode == null)
+			{
+				return -1;
+			}
+		}
+
+		if (currentNode.isNameEnd())
+		{
+			return 1;
+		}
+		else
+		{
+			return 0;
+		}
+	}
+	
+	/**
+	 * Removes place names that are unlikely to be meant as place names in a given text
+	 * @param gazetteer is the gazetteer to remove the place names from
+	 */
+	public void removeAmbiguousPlaceNames()
+	{
+		removePlaceName("are");				removePlaceName("is");
+		removePlaceName("over");			removePlaceName("at");
+		removePlaceName("of");				removePlaceName("to");
+		removePlaceName("rule");			removePlaceName("time");
+		removePlaceName("real");			removePlaceName("national");
+		removePlaceName("early");			removePlaceName("by");
+		removePlaceName("as");				removePlaceName("eastern");
+		removePlaceName("western");			removePlaceName("southern");
+		removePlaceName("northern");		removePlaceName("east");
+		removePlaceName("west");			removePlaceName("south");
+		removePlaceName("north");			removePlaceName("this");
+		removePlaceName("between");			removePlaceName("many");
+		removePlaceName("strong");			removePlaceName("economy");
+		removePlaceName("mall");			removePlaceName("they");
+		removePlaceName("do");				removePlaceName("image");
+		removePlaceName("republic");		removePlaceName("section");
+		removePlaceName("dollar");			removePlaceName("index");
+		removePlaceName("day");				removePlaceName("council");
+		removePlaceName("use");				removePlaceName("log");
+		removePlaceName("logo");			removePlaceName("best");
+		removePlaceName("go");				removePlaceName("portal");
+		removePlaceName("list");			removePlaceName("english");
+		removePlaceName("page");			removePlaceName("see");
+		removePlaceName("ocean");			removePlaceName("island");
+		removePlaceName("x");				removePlaceName("country");
+		removePlaceName("colony");			removePlaceName("christian");
+		removePlaceName("black");			removePlaceName("independence");
+		removePlaceName("war");				removePlaceName("no");
+		removePlaceName("continental");		removePlaceName("continental");
+		removePlaceName("force");			removePlaceName("reform");
+		removePlaceName("rush");			removePlaceName("read");
+		removePlaceName("none");			removePlaceName("justice");
+		removePlaceName("font");			removePlaceName("u");
+		removePlaceName("y");				removePlaceName("normal");
+		removePlaceName("center");			removePlaceName("date");
+		removePlaceName("story");			removePlaceName("union");
+		removePlaceName("supreme");			removePlaceName("house");
+		removePlaceName("court");			removePlaceName("data");
+		removePlaceName("energy");			removePlaceName("white");
+		removePlaceName("universal");		removePlaceName("protection");
+		removePlaceName("great");			removePlaceName("star");
+		removePlaceName("banner");			removePlaceName("capital");
+		removePlaceName("much");			removePlaceName("sidney");
+		removePlaceName("media");			removePlaceName("protection");
+		
+		addPlaceName("United States");
+	}
+}
+
+// TreeMap<Character, Integer> charCount = new TreeMap<Character, Integer>();
+// int upperCount = 0;
+// int lowerCount = 0;
+// int numbers = 0;
+// int arabicCharacters = 0;
+// int chineseCharacters = 0;
+
+// if (!charCount.containsKey(placeName.charAt(placeName.length() - 1)))
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1),
+// charCount.get(placeName.charAt(placeName.length() - 1)) + 1);
+// }
+
+// for (Character c : charCount.keySet())
+// {
+// System.out.println(c + " (" + (int) c + ") -> " + charCount.get(c));
+// }
+//
+// System.out.println("Upper -> " + upperCount);
+// System.out.println("Lower -> " + lowerCount);
+// System.out.println("Number -> " + numbers);
+// System.out.println("Arabic -> " + arabicCharacters);
+// System.out.println("Chinese -> " + chineseCharacters);
+
+// if (placeName.charAt(i) >= 'A' && placeName.charAt(i) <= 'Z')
+// {
+// upperCount++;
+// }
+//
+// if (placeName.charAt(i) >= 'a' && placeName.charAt(i) <= 'z')
+// {
+// lowerCount++;
+// }
+//
+// if (placeName.charAt(i) >= '0' && placeName.charAt(i) <= '9')
+// {
+// numbers++;
+// }
+//
+// if (placeName.charAt(i) >= 1569 && placeName.charAt(i) <= 1711)
+// {
+// arabicCharacters++;
+// }
+//
+// if (placeName.charAt(i) >= 12293 && placeName.charAt(i) <= 64016)
+// {
+// chineseCharacters++;
+// }
+//
+// if (!charCount.containsKey(placeName.charAt(i)))
+// {
+// charCount.put(placeName.charAt(i), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(i), charCount.get(placeName.charAt(i)) + 1);
+// }
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType4.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType4.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType4.java	(revision 23906)
@@ -0,0 +1,316 @@
+package org.greenstone.server;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.Serializable;
+import java.sql.Statement;
+import java.util.ArrayList;
+
+public class GazetteerTrieType4 implements Serializable
+{
+	private static final long serialVersionUID = -959184305931535981L;
+
+	int _nameCount = 0;
+
+	//GazetteerTrieNode _topLevelNode = new GazetteerTrieNode(false);
+	GazetteerTrieSmallNode _topLevelNode = new GazetteerTrieSmallNode(false);
+
+	Statement _database = null;
+
+	/**
+	 * Default constructor
+	 */
+	public GazetteerTrieType4()
+	{
+	}
+
+	/**
+	 * Contructor that takes a filename to generate the gazetteer
+	 * 
+	 * @param filename
+	 *            is the name of the file to use to generate the gazetteer with
+	 */
+	public GazetteerTrieType4(String filename)
+	{
+		System.out.println("Loading " + filename + " as gazetteer");
+		try
+		{
+			BufferedReader gazetteerFile = new BufferedReader(new FileReader(filename));
+			String line = "";
+
+			ArrayList<String> allPlaceNames = new ArrayList<String>();
+
+			int count = 0;
+			while ((line = gazetteerFile.readLine()) != null)
+			{
+				if(count++ % 10000 == 0){System.out.println(count + " entries loaded");}
+				// The file is tab seperated so split it by tabs
+				String[] columns = line.split("\t");
+
+				ArrayList<String> placeNames = new ArrayList<String>();
+
+				String mainPlaceName = GazetteerHelper.getMainPlaceName(columns);
+				if (mainPlaceName != null)
+				{
+					placeNames.add(mainPlaceName);
+				}
+
+				ArrayList<String> alternatePlaceNames = GazetteerHelper.getAlternativePlaceNames(columns);
+				if (alternatePlaceNames != null)
+				{
+					placeNames.addAll(alternatePlaceNames);
+				}
+
+				allPlaceNames.addAll(placeNames);
+
+				// Add the place names to the trie
+				for (String placeName : placeNames)
+				{
+					this.addPlaceName(placeName);
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		removeAmbiguousPlaceNames();
+	}
+
+	/**
+	 * Removes a place name from the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to be removed
+	 * @return true if sucessful and false if the place name did not exist
+	 */
+
+	public boolean removePlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			_topLevelNode.setNameEnd(false);
+			return true;
+		}
+		if (placeName.length() == 1)
+		{
+			_topLevelNode.getChild(placeName.charAt(0)).setNameEnd(false);
+			return true;
+		}
+		GazetteerTrieSmallNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		if(currentNode == null)
+		{
+			System.out.println(placeName.charAt(0) + " = null?");
+			return false;
+		}
+		
+		for (int i = 1; i < placeName.length(); i++)
+		{
+			currentNode = currentNode.getChild(placeName.charAt(i));
+			if (currentNode == null)
+			{
+				return false;
+			}
+		}
+
+		currentNode.setNameEnd(false);
+		return true;
+	}
+
+	/**
+	 * Adds a place name to the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to add
+	 */
+	public void addPlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			return;
+		}
+
+		if (placeName.length() == 1)
+		{
+			_topLevelNode.addChild(placeName.charAt(0), true);
+			return;
+		}
+
+		_topLevelNode.addChild(placeName.charAt(0), false);
+		GazetteerTrieSmallNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		for (int i = 1; i < placeName.length() - 1; i++)
+		{
+			currentNode.addChild(placeName.charAt(i), false);
+			currentNode = currentNode.getChild(placeName.charAt(i));
+		}
+
+		currentNode.addChild(placeName.charAt(placeName.length() - 1), true);
+	}
+
+	/**
+	 * Checks to see if a place name exists in the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to check
+	 * @return 1 if the place name exists 0 if the place name does not exist but
+	 *         there might be a match further down the trie -1 if the place name
+	 *         does not exist and the trie is at a dead end
+	 */
+	public int checkPlaceName(String placeName)
+	{
+		if (placeName.length() == 0)
+		{
+			return 0;
+		}
+
+		if (placeName.length() == 1)
+		{
+			GazetteerTrieSmallNode node = _topLevelNode.getChild(placeName.charAt(0));
+			if (node == null)
+			{
+				return -1;
+			}
+
+			return node.isNameEnd() ? 1 : 0;
+		}
+
+		GazetteerTrieSmallNode currentNode = _topLevelNode.getChild(placeName.charAt(0));
+		if (currentNode == null)
+		{
+			return -1;
+		}
+		for (int i = 1; i < placeName.length(); i++)
+		{
+			currentNode = currentNode.getChild(placeName.charAt(i));
+			if (currentNode == null)
+			{
+				return -1;
+			}
+		}
+
+		if (currentNode.isNameEnd())
+		{
+			return 1;
+		}
+		else
+		{
+			return 0;
+		}
+	}
+	
+	/**
+	 * Removes place names that are unlikely to be meant as place names in a given text
+	 * @param gazetteer is the gazetteer to remove the place names from
+	 */
+	public void removeAmbiguousPlaceNames()
+	{
+		removePlaceName("are");				removePlaceName("is");
+		removePlaceName("over");			removePlaceName("at");
+		removePlaceName("of");				removePlaceName("to");
+		removePlaceName("rule");			removePlaceName("time");
+		removePlaceName("real");			removePlaceName("national");
+		removePlaceName("early");			removePlaceName("by");
+		removePlaceName("as");				removePlaceName("eastern");
+		removePlaceName("western");			removePlaceName("southern");
+		removePlaceName("northern");		removePlaceName("east");
+		removePlaceName("west");			removePlaceName("south");
+		removePlaceName("north");			removePlaceName("this");
+		removePlaceName("between");			removePlaceName("many");
+		removePlaceName("strong");			removePlaceName("economy");
+		removePlaceName("mall");			removePlaceName("they");
+		removePlaceName("do");				removePlaceName("image");
+		removePlaceName("republic");		removePlaceName("section");
+		removePlaceName("dollar");			removePlaceName("index");
+		removePlaceName("day");				removePlaceName("council");
+		removePlaceName("use");				removePlaceName("log");
+		removePlaceName("logo");			removePlaceName("best");
+		removePlaceName("go");				removePlaceName("portal");
+		removePlaceName("list");			removePlaceName("english");
+		removePlaceName("page");			removePlaceName("see");
+		removePlaceName("ocean");			removePlaceName("island");
+		removePlaceName("x");				removePlaceName("country");
+		removePlaceName("colony");			removePlaceName("christian");
+		removePlaceName("black");			removePlaceName("independence");
+		removePlaceName("war");				removePlaceName("no");
+		removePlaceName("continental");		removePlaceName("continental");
+		removePlaceName("force");			removePlaceName("reform");
+		removePlaceName("rush");			removePlaceName("read");
+		removePlaceName("none");			removePlaceName("justice");
+		removePlaceName("font");			removePlaceName("u");
+		removePlaceName("y");				removePlaceName("normal");
+		removePlaceName("center");			removePlaceName("date");
+		removePlaceName("story");			removePlaceName("union");
+		removePlaceName("supreme");			removePlaceName("house");
+		removePlaceName("court");			removePlaceName("data");
+		removePlaceName("energy");			removePlaceName("white");
+		removePlaceName("universal");		removePlaceName("protection");
+		removePlaceName("great");			removePlaceName("star");
+		removePlaceName("banner");			removePlaceName("capital");
+		removePlaceName("much");			removePlaceName("sidney");
+		removePlaceName("media");			removePlaceName("protection");
+		
+		addPlaceName("United States");
+	}
+}
+
+// TreeMap<Character, Integer> charCount = new TreeMap<Character, Integer>();
+// int upperCount = 0;
+// int lowerCount = 0;
+// int numbers = 0;
+// int arabicCharacters = 0;
+// int chineseCharacters = 0;
+
+// if (!charCount.containsKey(placeName.charAt(placeName.length() - 1)))
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1),
+// charCount.get(placeName.charAt(placeName.length() - 1)) + 1);
+// }
+
+// for (Character c : charCount.keySet())
+// {
+// System.out.println(c + " (" + (int) c + ") -> " + charCount.get(c));
+// }
+//
+// System.out.println("Upper -> " + upperCount);
+// System.out.println("Lower -> " + lowerCount);
+// System.out.println("Number -> " + numbers);
+// System.out.println("Arabic -> " + arabicCharacters);
+// System.out.println("Chinese -> " + chineseCharacters);
+
+// if (placeName.charAt(i) >= 'A' && placeName.charAt(i) <= 'Z')
+// {
+// upperCount++;
+// }
+//
+// if (placeName.charAt(i) >= 'a' && placeName.charAt(i) <= 'z')
+// {
+// lowerCount++;
+// }
+//
+// if (placeName.charAt(i) >= '0' && placeName.charAt(i) <= '9')
+// {
+// numbers++;
+// }
+//
+// if (placeName.charAt(i) >= 1569 && placeName.charAt(i) <= 1711)
+// {
+// arabicCharacters++;
+// }
+//
+// if (placeName.charAt(i) >= 12293 && placeName.charAt(i) <= 64016)
+// {
+// chineseCharacters++;
+// }
+//
+// if (!charCount.containsKey(placeName.charAt(i)))
+// {
+// charCount.put(placeName.charAt(i), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(i), charCount.get(placeName.charAt(i)) + 1);
+// }
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType5.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType5.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/GazetteerTrieType5.java	(revision 23906)
@@ -0,0 +1,302 @@
+package org.greenstone.server;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class GazetteerTrieType5 implements Serializable
+{
+	private static final long serialVersionUID = -959184305931535981L;
+
+	int _nameCount = 0;
+
+	HashMap<String, Integer> _gazetteer = new HashMap<String, Integer>();
+
+	/**
+	 * Default constructor
+	 */
+	public GazetteerTrieType5()
+	{
+	}
+
+	/**
+	 * Contructor that takes a filename to generate the gazetteer
+	 * 
+	 * @param filename
+	 *            is the name of the file to use to generate the gazetteer with
+	 */
+	public GazetteerTrieType5(String filename)
+	{
+		System.out.println("Loading " + filename + " as gazetteer");
+		try
+		{
+			BufferedReader gazetteerFile = new BufferedReader(new FileReader(filename));
+			String line = "";
+
+			ArrayList<String> allPlaceNames = new ArrayList<String>();
+
+			int count = 0;
+			while ((line = gazetteerFile.readLine()) != null)
+			{
+				if (count++ % 10000 == 0)
+				{
+					System.out.println(count + " entries loaded");
+				}
+				// The file is tab seperated so split it by tabs
+				String[] columns = line.split("\t");
+
+				ArrayList<String> placeNames = new ArrayList<String>();
+
+				String mainPlaceName = GazetteerHelper.getMainPlaceName(columns);
+				if (mainPlaceName != null)
+				{
+					placeNames.add(mainPlaceName);
+				}
+
+				ArrayList<String> alternatePlaceNames = GazetteerHelper.getAlternativePlaceNames(columns);
+				if (alternatePlaceNames != null)
+				{
+					placeNames.addAll(alternatePlaceNames);
+				}
+
+				allPlaceNames.addAll(placeNames);
+
+				// Add the place names to the trie
+				for (String placeName : placeNames)
+				{
+					this.addPlaceName(placeName);
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		removeAmbiguousPlaceNames();
+	}
+
+	/**
+	 * Removes a place name from the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to be removed
+	 * @return true if sucessful and false if the place name did not exist
+	 */
+
+	public void removePlaceName(String placeName)
+	{
+		_gazetteer.remove(placeName);
+	}
+
+	/**
+	 * Adds a place name to the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to add
+	 */
+	public void addPlaceName(String placeName)
+	{
+		String[] words = placeName.split(" ");
+
+		for (int i = 0; i < (words.length - 1); i++)
+		{
+			_gazetteer.put(words[i], 0);
+		}
+		_gazetteer.put(words[words.length-1], 1);
+	}
+
+	/**
+	 * Checks to see if a place name exists in the trie
+	 * 
+	 * @param placeName
+	 *            is the place name to check
+	 * @return 1 if the place name exists 0 if the place name does not exist but
+	 *         there might be a match further down the trie -1 if the place name
+	 *         does not exist and the trie is at a dead end
+	 */
+	public int checkPlaceName(String placeName)
+	{
+		String[] words = placeName.split(" ");
+		
+		for(int i = 0; i < (words.length - 1); i++)
+		{
+			if(_gazetteer.get(words[i]) == null)
+			{
+				return -1;
+			}
+		}
+		
+		if (_gazetteer.get(words[words.length-1]) == null)
+		{
+			return -1;
+		}
+		else
+		{
+			return _gazetteer.get(words[words.length-1]) ;
+		}
+	}
+
+	/**
+	 * Removes place names that are unlikely to be meant as place names in a
+	 * given text
+	 * 
+	 * @param gazetteer
+	 *            is the gazetteer to remove the place names from
+	 */
+	public void removeAmbiguousPlaceNames()
+	{
+		removePlaceName("are");
+		removePlaceName("is");
+		removePlaceName("over");
+		removePlaceName("at");
+		removePlaceName("of");
+		removePlaceName("to");
+		removePlaceName("rule");
+		removePlaceName("time");
+		removePlaceName("real");
+		removePlaceName("national");
+		removePlaceName("early");
+		removePlaceName("by");
+		removePlaceName("as");
+		removePlaceName("eastern");
+		removePlaceName("western");
+		removePlaceName("southern");
+		removePlaceName("northern");
+		removePlaceName("east");
+		removePlaceName("west");
+		removePlaceName("south");
+		removePlaceName("north");
+		removePlaceName("this");
+		removePlaceName("between");
+		removePlaceName("many");
+		removePlaceName("strong");
+		removePlaceName("economy");
+		removePlaceName("mall");
+		removePlaceName("they");
+		removePlaceName("do");
+		removePlaceName("image");
+		removePlaceName("republic");
+		removePlaceName("section");
+		removePlaceName("dollar");
+		removePlaceName("index");
+		removePlaceName("day");
+		removePlaceName("council");
+		removePlaceName("use");
+		removePlaceName("log");
+		removePlaceName("logo");
+		removePlaceName("best");
+		removePlaceName("go");
+		removePlaceName("portal");
+		removePlaceName("list");
+		removePlaceName("english");
+		removePlaceName("page");
+		removePlaceName("see");
+		removePlaceName("ocean");
+		removePlaceName("island");
+		removePlaceName("x");
+		removePlaceName("country");
+		removePlaceName("colony");
+		removePlaceName("christian");
+		removePlaceName("black");
+		removePlaceName("independence");
+		removePlaceName("war");
+		removePlaceName("no");
+		removePlaceName("continental");
+		removePlaceName("force");
+		removePlaceName("reform");
+		removePlaceName("rush");
+		removePlaceName("read");
+		removePlaceName("none");
+		removePlaceName("justice");
+		removePlaceName("font");
+		removePlaceName("u");
+		removePlaceName("y");
+		removePlaceName("normal");
+		removePlaceName("center");
+		removePlaceName("date");
+		removePlaceName("story");
+		removePlaceName("union");
+		removePlaceName("supreme");
+		removePlaceName("house");
+		removePlaceName("court");
+		removePlaceName("data");
+		removePlaceName("energy");
+		removePlaceName("white");
+		removePlaceName("universal");
+		removePlaceName("protection");
+		removePlaceName("great");
+		removePlaceName("star");
+		removePlaceName("banner");
+		removePlaceName("capital");
+		removePlaceName("much");
+		removePlaceName("sidney");
+		removePlaceName("media");
+		removePlaceName("protection");
+
+		addPlaceName("United States");
+	}
+}
+
+// TreeMap<Character, Integer> charCount = new TreeMap<Character, Integer>();
+// int upperCount = 0;
+// int lowerCount = 0;
+// int numbers = 0;
+// int arabicCharacters = 0;
+// int chineseCharacters = 0;
+
+// if (!charCount.containsKey(placeName.charAt(placeName.length() - 1)))
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(placeName.length() - 1),
+// charCount.get(placeName.charAt(placeName.length() - 1)) + 1);
+// }
+
+// for (Character c : charCount.keySet())
+// {
+// System.out.println(c + " (" + (int) c + ") -> " + charCount.get(c));
+// }
+//
+// System.out.println("Upper -> " + upperCount);
+// System.out.println("Lower -> " + lowerCount);
+// System.out.println("Number -> " + numbers);
+// System.out.println("Arabic -> " + arabicCharacters);
+// System.out.println("Chinese -> " + chineseCharacters);
+
+// if (placeName.charAt(i) >= 'A' && placeName.charAt(i) <= 'Z')
+// {
+// upperCount++;
+// }
+//
+// if (placeName.charAt(i) >= 'a' && placeName.charAt(i) <= 'z')
+// {
+// lowerCount++;
+// }
+//
+// if (placeName.charAt(i) >= '0' && placeName.charAt(i) <= '9')
+// {
+// numbers++;
+// }
+//
+// if (placeName.charAt(i) >= 1569 && placeName.charAt(i) <= 1711)
+// {
+// arabicCharacters++;
+// }
+//
+// if (placeName.charAt(i) >= 12293 && placeName.charAt(i) <= 64016)
+// {
+// chineseCharacters++;
+// }
+//
+// if (!charCount.containsKey(placeName.charAt(i)))
+// {
+// charCount.put(placeName.charAt(i), 1);
+// }
+// else
+// {
+// charCount.put(placeName.charAt(i), charCount.get(placeName.charAt(i)) + 1);
+// }
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/MarkupService.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/MarkupService.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/MarkupService.java	(revision 23906)
@@ -0,0 +1,285 @@
+package org.greenstone.server;
+
+
+import java.util.HashMap;
+import java.util.ArrayList;
+
+public class MarkupService 
+{
+	/**
+	 * Takes the given text and searches it for place names and then marks up any matches
+	 * @param originalText is the given text
+	 * @return a marked up version of the text
+	 */
+	public String getMarkedUpText(String originalText)
+	{
+		//Get the gazetteer
+		GazetteerTrieType2 gazetteer = new GazetteerTrieType2("/research/sjm84/Msc/Downloads/dataen.txt");
+		
+		removeAmbiguousPlaceNames(gazetteer);
+		
+		//Find the words in the text
+		ArrayList<String> words = findWords(originalText);
+		
+		//Find the matches
+		HashMap<Integer, Integer> matches = findMatches(gazetteer, words);
+		
+		//Return the marked up text
+		return createMarkedUpText(words, matches, originalText);
+	}
+	
+	/**
+	 * Takes the given text, list of words and list of matches to create marked up text
+	 * @param words is the list of words in the text
+	 * @param matches is the list of gazetteer matches in the text
+	 * @param originalText is the orignal text that is to be marked up
+	 * @return the marked up text
+	 */
+	protected String createMarkedUpText(ArrayList<String> words, HashMap<Integer, Integer> matches, String originalText)
+	{
+		StringBuilder markedUpText = new StringBuilder();
+		
+		//Used to count what word in words is being used
+		int wordCount = 0;
+		
+		//Go through each character 
+		int i = 0;
+		while(i < originalText.length())
+		{
+			//If the current character in the text is a letter
+			if(Character.isLetter(originalText.charAt(i)))
+			{
+				Integer numOfWordsInPlaceName = matches.get(wordCount);
+				
+				//If the word is a place name
+				if(numOfWordsInPlaceName != null)
+				{
+					int endingCharacters = 0;
+					StringBuilder placeName = new StringBuilder();
+					
+					//Get each word in the place name
+					for(int j = 0; j < numOfWordsInPlaceName; j++)
+					{
+						//Add the word to the place name
+						placeName.append(words.get(wordCount));
+						
+						//Move the corresponding amount of characters ahead 
+						i += words.get(wordCount).length();
+						wordCount++;
+						
+						Character c = null;
+						endingCharacters = 0;
+						
+						//Add any characters between the words in the place name
+						while(i < originalText.length() && !Character.isLetter(c = originalText.charAt(i)))
+						{
+							placeName.append(c);
+							i++;
+							endingCharacters++;
+						}
+					}	
+					
+					//Mark up the place name
+					markedUpText.append("<span style=\"background-color:yellow\">"
+							+ placeName.substring(0, placeName.length() - endingCharacters) 
+							+ "</span>" 
+							+ placeName.substring(placeName.length() - endingCharacters, placeName.length()));
+				}
+				//If the word is not a place name
+				else
+				{	
+					//Add it to the marked up text as is
+					String word = words.get(wordCount++);
+					markedUpText.append(word);
+					i += word.length();
+				}
+			}
+			//If the current character in the text is not a letter
+			else
+			{
+				//Add it to the marked up text as is
+				markedUpText.append(originalText.charAt(i));
+				i++;
+			}
+		}
+		
+		return markedUpText.toString();
+	}
+	
+	/**
+	 * Searches the words of a text for matches in the gazetteer
+	 * @param gaz is the gazetter (in trie form) to use
+	 * @param text is the list of words to search
+	 * @return a list of matches in the format <[first word index], [number of words in match]>
+	 */
+	protected HashMap<Integer, Integer> findMatches(GazetteerTrieType2 gaz, ArrayList<String> words)
+	{
+		HashMap<Integer, Integer> matches = new HashMap<Integer, Integer>();
+		
+		//Go through every word in the list of words
+		for(int i = 0; i < words.size(); i++)
+		{
+			int j = i;
+			int result = 0;
+			StringBuilder currentWord = new StringBuilder();
+			
+			//Until a dead end in the trie is reached or there is no more words to append
+			while(j < words.size())
+			{
+				if(j == i && Character.isLowerCase(words.get(j).charAt(0)))
+				{
+					break;
+				}
+				
+				currentWord = currentWord.append(words.get(j));
+				
+				//Check the words in the gazetteer to see if there is a match
+				result = gaz.checkPlaceName(currentWord.toString());
+				
+				//If there is a match
+				if(result == 1)
+				{
+					//Store the match in the form <[first word index], [number of words in match]>
+					//Because shorter words are checked first longer matches will overwrite shorter matches
+					matches.put(i, (j-i) + 1);
+				}
+				else if(result == -1)
+				{
+					break;
+				}
+				j++;
+				currentWord = currentWord.append(" ");
+			}
+		}
+		
+		//Remove any overlapping matches and return
+		return correctMatches(matches);
+	}
+	
+	/**
+	 * Takes an array of matches and removes any that overlap
+	 * @param matches is the array of matches
+	 * @return the corrected array of matches
+	 */
+	protected HashMap<Integer, Integer> correctMatches(HashMap<Integer, Integer> matches)
+	{
+		Integer[] keys = matches.keySet().toArray(new Integer[0]);
+		
+		//Go through all the keys in the hash map
+		for(int i = 0; i < keys.length-1; i++)
+		{
+			//Ignore keys that have been removed
+			if(matches.get(keys[i]) == null)
+			{
+				continue;
+			}
+			
+			//Check and see if the current key overlaps any other keys
+			for(int j = keys[i] + 1; j < keys[i] + matches.get(keys[i]); j++)
+			{
+				//If so, remove the keys it overlaps
+				if(matches.get(j) != null)
+				{
+					matches.remove(j);
+				}
+			}
+		}
+		
+		return matches;
+	}
+	
+	/**
+	 * Divides the given text into words and returns the list of words
+	 * @param text is the text to divide
+	 * @return the divided text
+	 */
+	protected static ArrayList<String> findWords(String text)
+	{
+		ArrayList<String> words = new ArrayList<String>();		
+		StringBuilder currentWord = new StringBuilder();
+		
+		//Go through each character in the text
+		for(int i = 0; i < text.length(); i++)
+		{
+			//If it is a letter then add it to the current word
+			if(Character.isLetter(text.charAt(i)) || text.charAt(i) == '-')
+			{
+				currentWord.append(text.charAt(i));
+			}
+			//If it is not a letter
+			else
+			{
+				//And a word currently is currently being created
+				if(currentWord.length() > 0 && text.charAt(i - currentWord.length() + 1) != '\'')
+				{
+					//Add the current word to the list of words
+					words.add(currentWord.toString());	
+					
+					//Delete the current words
+					currentWord.delete(0, currentWord.length());
+				}
+			}
+		}
+		
+		//Add the final word of the text
+		if(currentWord.length() > 0)
+		{
+			words.add(currentWord.toString());
+		}
+		
+		return words;
+	}
+	
+	/**
+	 * Removes place names that are unlikely to be meant as place names in a given text
+	 * @param gazetteer is the gazetteer to remove the place names from
+	 */
+	public void removeAmbiguousPlaceNames(GazetteerTrieType2 gazetteer)
+	{
+		gazetteer.removePlaceName("are");			gazetteer.removePlaceName("is");
+		gazetteer.removePlaceName("over");			gazetteer.removePlaceName("at");
+		gazetteer.removePlaceName("of");			gazetteer.removePlaceName("to");
+		gazetteer.removePlaceName("rule");			gazetteer.removePlaceName("time");
+		gazetteer.removePlaceName("real");			gazetteer.removePlaceName("national");
+		gazetteer.removePlaceName("early");			gazetteer.removePlaceName("by");
+		gazetteer.removePlaceName("as");			gazetteer.removePlaceName("eastern");
+		gazetteer.removePlaceName("western");		gazetteer.removePlaceName("southern");
+		gazetteer.removePlaceName("northern");		gazetteer.removePlaceName("east");
+		gazetteer.removePlaceName("west");			gazetteer.removePlaceName("south");
+		gazetteer.removePlaceName("north");			gazetteer.removePlaceName("this");
+		gazetteer.removePlaceName("between");		gazetteer.removePlaceName("many");
+		gazetteer.removePlaceName("strong");		gazetteer.removePlaceName("economy");
+		gazetteer.removePlaceName("mall");			gazetteer.removePlaceName("they");
+		gazetteer.removePlaceName("do");			gazetteer.removePlaceName("image");
+		gazetteer.removePlaceName("republic");		gazetteer.removePlaceName("section");
+		gazetteer.removePlaceName("dollar");		gazetteer.removePlaceName("index");
+		gazetteer.removePlaceName("day");			gazetteer.removePlaceName("council");
+		gazetteer.removePlaceName("use");			gazetteer.removePlaceName("log");
+		gazetteer.removePlaceName("logo");			gazetteer.removePlaceName("best");
+		gazetteer.removePlaceName("go");			gazetteer.removePlaceName("portal");
+		gazetteer.removePlaceName("list");			gazetteer.removePlaceName("english");
+		gazetteer.removePlaceName("page");			gazetteer.removePlaceName("see");
+		gazetteer.removePlaceName("ocean");			gazetteer.removePlaceName("island");
+		gazetteer.removePlaceName("x");				gazetteer.removePlaceName("country");
+		gazetteer.removePlaceName("colony");		gazetteer.removePlaceName("christian");
+		gazetteer.removePlaceName("black");			gazetteer.removePlaceName("independence");
+		gazetteer.removePlaceName("war");			gazetteer.removePlaceName("no");
+		gazetteer.removePlaceName("continental");	gazetteer.removePlaceName("continental");
+		gazetteer.removePlaceName("force");			gazetteer.removePlaceName("reform");
+		gazetteer.removePlaceName("rush");			gazetteer.removePlaceName("read");
+		gazetteer.removePlaceName("none");			gazetteer.removePlaceName("justice");
+		gazetteer.removePlaceName("font");			gazetteer.removePlaceName("u");
+		gazetteer.removePlaceName("y");				gazetteer.removePlaceName("normal");
+		gazetteer.removePlaceName("center");		gazetteer.removePlaceName("date");
+		gazetteer.removePlaceName("story");			gazetteer.removePlaceName("union");
+		gazetteer.removePlaceName("supreme");		gazetteer.removePlaceName("house");
+		gazetteer.removePlaceName("court");			gazetteer.removePlaceName("data");
+		gazetteer.removePlaceName("energy");		gazetteer.removePlaceName("white");
+		gazetteer.removePlaceName("universal");		gazetteer.removePlaceName("protection");
+		gazetteer.removePlaceName("great");			gazetteer.removePlaceName("star");
+		gazetteer.removePlaceName("banner");		gazetteer.removePlaceName("capital");
+		gazetteer.removePlaceName("much");
+		
+		gazetteer.addPlaceName("United States");
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/PageScanner.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/PageScanner.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/PageScanner.java	(revision 23906)
@@ -0,0 +1,1036 @@
+package org.greenstone.server;
+
+import gate.util.Out;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.greenstone.client.Place;
+
+public class PageScanner
+{
+	// Stores the gazeteer structure (used to verify if words are a place name
+	// or not)
+	protected GazetteerTrieType2 _gazetteer = null;
+
+	// Stores all of the places in the page that is being examined
+	protected ArrayList<Place> _places = new ArrayList<Place>();
+	protected HashMap<String, ArrayList<Place>> _placeNameMap = new HashMap<String, ArrayList<Place>>();
+	protected String _markedUpText = null;
+	protected HashMap<String, ArrayList<Place>> _placeCache = new HashMap<String, ArrayList<Place>>();
+
+	protected GateScanner _gateScanner = new GateScanner();
+
+	// Parameters for score calculations
+	// *********************************
+	protected double _penaltyPercentage = 0.5;
+	protected double _parentBonusPercentage = 0.25;
+	protected double _indirectReferencePenaltyPercentage = 0.25;
+	protected double _parentLimitPercentage = 0.05;
+
+	protected ArrayList<String> _prevDoc = null;
+	protected String _prevFileName = null;
+
+	/**
+	 * Default constructer. It creates the place data structure and gazetteer
+	 * trie structure
+	 */
+	public PageScanner(String path)
+	{
+		System.out.println("Loading path = " + path);
+		try
+		{
+			PlaceInformation.init();
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		System.out.println("Starting loading gazetteer");
+		_gazetteer = new GazetteerTrieType2(path + "/dataen.txt");
+	}
+
+	/**
+	 * Examines the given text to find place names and score them
+	 * 
+	 * @param text
+	 *            is the text to examine
+	 */
+	public ArrayList<Place> examineTextWithGate(String text, String htmlString)
+	{
+		_places.clear();
+		
+		if(htmlString != null && text == null)
+		{
+			text = HTMLParser.removeTags(htmlString);
+		}
+
+		if (text == null || text.length() < 1 || htmlString == null || htmlString.length() < 1)
+		{
+			return new ArrayList<Place>();
+		}
+
+		HTMLParser htmlParser = new HTMLParser(htmlString);
+
+		ArrayList<String> betweenChars = htmlParser.getFullBetweenWordList();
+		ArrayList<String> htmlWords = htmlParser.getFullHTMLWordList();
+		
+		HashMap<Integer, Integer> placeNameIndexesAndLength = new HashMap<Integer, Integer>();
+		
+		try
+		{
+			// Classify all the text with Gate
+			HashMap<String, Word> classifiedWords = _gateScanner.classifyText(text);
+
+			// Stores the current words being examined
+			StringBuilder currentPotentialPlaceNames = new StringBuilder();
+			
+			// Get the next word from the HTML
+			for (int i = 0; i < htmlWords.size(); i++)
+			{		
+				String currentWord = htmlWords.get(i);
+				
+				Word classifiedWord = classifiedWords.get(currentWord);
+				if (classifiedWord == null)
+				{
+					continue;
+				}
+				
+				String classification = classifiedWord.getNextClassification();
+
+				// If the word does not begin with an uppercase letter then ignore it
+				if (classification == null || Character.isLowerCase(currentWord.charAt(0)) || !(classification.equals("NNP")))
+				{
+					continue;
+				}
+
+				// Add the first word to the list of words to be examined
+				currentPotentialPlaceNames.append(currentWord);
+
+				// While the gazetteer does not reach a dead end, add another word and examine them
+				int count = 1;
+				String lastGoodPlaceName = null;
+				while (_gazetteer.checkPlaceName(currentPotentialPlaceNames.toString()) != -1)
+				{
+					// If the words are a place name then store it
+					if (_gazetteer.checkPlaceName(currentPotentialPlaceNames.toString()) == 1)
+					{
+						lastGoodPlaceName = currentPotentialPlaceNames.toString();
+					}
+
+					// If it is not the end of the words the add the next word
+					if (i + count < htmlWords.size())
+					{
+						currentPotentialPlaceNames.append(betweenChars.get(i + count) + htmlWords.get(i + count++));
+					}
+					else
+					{
+						break;
+					}
+				}
+
+				// If there was a place name found then find its information and score it
+				if (lastGoodPlaceName != null)
+				{
+					placeNameIndexesAndLength.put(i, count);
+
+					ArrayList<Place> placeList = PlaceInformation.getPlaces(lastGoodPlaceName);
+
+					if (placeList == null)
+					{
+						continue;
+					}
+
+					if (_placeNameMap.containsKey(lastGoodPlaceName))
+					{
+						for (Place p : _placeNameMap.get(lastGoodPlaceName))
+						{
+							p.directReference();
+						}
+					}
+
+					for (Place p : placeList)
+					{
+						p.directReference();
+						addScore(p, 256);
+					}
+				}
+
+				currentPotentialPlaceNames = new StringBuilder();
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+			
+
+		// Tidy up the places found by removing overlapping places
+		for(int i = 0; i < htmlWords.size(); i++)
+		{
+			if(placeNameIndexesAndLength.get(i) != null)
+			{
+				int length = placeNameIndexesAndLength.get(i);
+				for(int j = 1; j < length; j++)
+				{
+					if(placeNameIndexesAndLength.get(i + j) != null)
+					{
+						placeNameIndexesAndLength.remove(i + j);
+					}
+				}
+			}
+		}
+
+		StringBuilder htmlBuilder = new StringBuilder();
+
+		int placeEnd = -1;
+		for(int i = 0; i < htmlWords.size(); i++)
+		{
+			if(i == placeEnd)
+			{
+				htmlBuilder.append("</span>");
+			}		
+			
+			if(i < betweenChars.size() && betweenChars.get(i) != null)
+			{
+				htmlBuilder.append(betweenChars.get(i));
+			}
+			
+			if(placeNameIndexesAndLength.get(i) != null)
+			{
+				htmlBuilder.append("<span class=\"place\">");
+				placeEnd = i + (placeNameIndexesAndLength.get(i) - 1);
+			}
+			htmlBuilder.append(htmlWords.get(i));
+		}
+
+		_markedUpText = htmlBuilder.toString();
+		
+		if (_places.size() > 0)
+		{
+			adjustScores();
+		}
+
+		return _places;
+	}
+
+	public ArrayList<Place> examineTextWithoutGate(ArrayList<ArrayList<String>> lines)
+	{
+		_places.clear();
+		try
+		{
+			// Stores the current words being examined
+			StringBuilder currentWords = new StringBuilder();
+
+			// Read each line from the file
+			for (ArrayList<String> words : lines)
+			{
+				// Examine the words
+				for (int j = 0; j < words.size(); j++)
+				{
+					// If the word does not begin with an uppercase letter then
+					// ignore it
+					if (Character.isLowerCase(words.get(j).charAt(0)) || !(words.get(j).length() > 1 && Character.isLowerCase(words.get(j).charAt(1))))
+					{
+						continue;
+					}
+
+					// Used to store a good place name
+					String lastGoodPlaceName = null;
+
+					// Add the first word to the list of words to be examined
+					currentWords.append(words.get(j));
+
+					// While the gazetteer does not reach a dead end, add
+					// another word and examine them
+					int count = 1;
+					while (_gazetteer.checkPlaceName(currentWords.toString()) != -1)
+					{
+						// If the words are a place name then store it
+						if (_gazetteer.checkPlaceName(currentWords.toString()) == 1)
+						{
+							lastGoodPlaceName = currentWords.toString();
+							// System.out.println("Current place name part => "
+							// + lastGoodPlaceName);
+						}
+
+						// If it is not the end of the words the add the next
+						// word
+						if (j + count < words.size())
+						{
+							currentWords.append(" " + words.get(j + count++));
+						}
+						else
+						{
+							break;
+						}
+					}
+
+					// If there was a place name found then find its information
+					// and score it
+					if (lastGoodPlaceName != null)
+					{
+						ArrayList<Place> placeList = PlaceInformation.getPlaces(lastGoodPlaceName);
+
+						if (placeList == null)
+						{
+							continue;
+						}
+
+						if (_placeNameMap.containsKey(lastGoodPlaceName))
+						{
+							for (Place p : _placeNameMap.get(lastGoodPlaceName))
+							{
+								p.directReference();
+							}
+						}
+
+						for (Place p : placeList)
+						{
+							p.directReference();
+							addScore(p, 256);
+						}
+					}
+					currentWords = new StringBuilder();
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+
+		adjustScores();
+
+		return _places;
+	}
+
+	public ArrayList<String> getPlaceNames(String text)
+	{
+		ArrayList<String> placeNames = new ArrayList<String>();
+		_places.clear();
+		try
+		{
+			// The file to read from
+			BufferedReader file = new BufferedReader(new StringReader(text));
+
+			// Stores the current words being examined
+			StringBuilder currentWords = new StringBuilder();
+
+			// Stores the current line being examined
+			String currentLine = null;
+
+			// The list of words in the line
+			ArrayList<String> words = new ArrayList<String>();
+
+			// Read each line from the file
+			while ((currentLine = file.readLine()) != null)
+			{
+				words.clear();
+
+				// Find the words in those lines and add them to the list
+				words.addAll(MarkupService.findWords(currentLine));
+
+				// Examine the words
+				for (int j = 0; j < words.size(); j++)
+				{
+					// If the word does not begin with an uppercase letter then
+					// ignore it
+					if (Character.isLowerCase(words.get(j).charAt(0)) || !(words.get(j).length() > 1 && Character.isLowerCase(words.get(j).charAt(1))))
+					{
+						continue;
+					}
+
+					// Used to store a good place name
+					String lastGoodPlaceName = null;
+
+					// Add the first word to the list of words to be examined
+					currentWords.append(words.get(j));
+
+					// While the gazetteer does not reach a dead end, add
+					// another word and examine them
+					int count = 1;
+					while (_gazetteer.checkPlaceName(currentWords.toString()) != -1)
+					{
+						// If the words are a place name then store it
+						if (_gazetteer.checkPlaceName(currentWords.toString()) == 1)
+						{
+							lastGoodPlaceName = currentWords.toString();
+							// System.out.println("Current place name part => "
+							// + lastGoodPlaceName);
+						}
+
+						// If it is not the end of the words the add the next
+						// word
+						if (j + count < words.size())
+						{
+							currentWords.append(" " + words.get(j + count++));
+						}
+						else
+						{
+							break;
+						}
+					}
+
+					// If there was a place name found then find its information
+					// and score it
+					if (lastGoodPlaceName != null)
+					{
+						placeNames.add(lastGoodPlaceName);
+					}
+					currentWords = new StringBuilder();
+				}
+			}
+			file.close();
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+
+		return placeNames;
+	}
+
+	public ArrayList<Place> examineArrayOfStrings(ArrayList<String> placeNames)
+	{
+		_places.clear();
+		for (int i = 0; i < placeNames.size(); i++)
+		{
+			String currentPlaceName = placeNames.get(i);
+
+			ArrayList<Place> placeList = null;
+			if (_placeCache.containsKey(currentPlaceName))
+			{
+				placeList = _placeCache.get(currentPlaceName);
+				for (Place p : placeList)
+				{
+					p.unDirectReference();
+				}
+			}
+			else
+			{
+				placeList = PlaceInformation.getPlaces(currentPlaceName);
+				_placeCache.put(currentPlaceName, placeList);
+			}
+
+			if (placeList == null)
+			{
+				continue;
+			}
+
+			if (_placeNameMap.containsKey(currentPlaceName))
+			{
+				for (Place p : _placeNameMap.get(currentPlaceName))
+				{
+					p.directReference();
+				}
+			}
+
+			for (Place p : placeList)
+			{
+				p.directReference();
+				addScore(p, 256);
+			}
+		}
+
+		// System.out.println("Done!");
+		// System.out.print("Adjusting Scores... ");
+
+		adjustScores();
+
+		// System.out.println("Done!");
+		// System.out.print("Sorting Scores... ");
+
+		// System.out.println("Done!");
+
+		// System.out.println("Places found = " + _places);
+
+		return _places;
+	}
+
+	public ArrayList<Place> examineTextWithoutGate(String text)
+	{
+		_places.clear();
+		try
+		{
+			// The file to read from
+			BufferedReader file = new BufferedReader(new StringReader(text));
+
+			// Stores the current words being examined
+			StringBuilder currentWords = new StringBuilder();
+
+			// Stores the current line being examined
+			String currentLine = null;
+
+			// The list of words in the line
+			ArrayList<String> words = new ArrayList<String>();
+
+			// Read each line from the file
+			while ((currentLine = file.readLine()) != null)
+			{
+				words.clear();
+
+				// Find the words in those lines and add them to the list
+				words.addAll(MarkupService.findWords(currentLine));
+
+				// Examine the words
+				for (int j = 0; j < words.size(); j++)
+				{
+					// If the word does not begin with an uppercase letter then
+					// ignore it
+					if (Character.isLowerCase(words.get(j).charAt(0)) || !(words.get(j).length() > 1 && Character.isLowerCase(words.get(j).charAt(1))))
+					{
+						continue;
+					}
+
+					// Used to store a good place name
+					String lastGoodPlaceName = null;
+
+					// Add the first word to the list of words to be examined
+					currentWords.append(words.get(j));
+
+					// While the gazetteer does not reach a dead end, add
+					// another word and examine them
+					int count = 1;
+					while (_gazetteer.checkPlaceName(currentWords.toString()) != -1)
+					{
+						// If the words are a place name then store it
+						if (_gazetteer.checkPlaceName(currentWords.toString()) == 1)
+						{
+							lastGoodPlaceName = currentWords.toString();
+							// System.out.println("Current place name part => "
+							// + lastGoodPlaceName);
+						}
+
+						// If it is not the end of the words the add the next
+						// word
+						if (j + count < words.size())
+						{
+							currentWords.append(" " + words.get(j + count++));
+						}
+						else
+						{
+							break;
+						}
+					}
+
+					// If there was a place name found then find its information
+					// and score it
+					if (lastGoodPlaceName != null)
+					{
+						ArrayList<Place> placeList = PlaceInformation.getPlaces(lastGoodPlaceName);
+
+						if (placeList == null)
+						{
+							continue;
+						}
+
+						if (_placeNameMap.containsKey(lastGoodPlaceName))
+						{
+							for (Place p : _placeNameMap.get(lastGoodPlaceName))
+							{
+								p.directReference();
+							}
+						}
+
+						for (Place p : placeList)
+						{
+							p.directReference();
+							addScore(p, 256);
+						}
+					}
+					currentWords = new StringBuilder();
+				}
+			}
+			file.close();
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+
+		// System.out.println("Done!");
+		// System.out.print("Adjusting Scores... ");
+
+		adjustScores();
+
+		// System.out.println("Done!");
+		// System.out.print("Sorting Scores... ");
+		// System.out.println("Done!");
+		// System.out.println("Places found = " + _places);
+
+		return _places;
+	}
+
+	public String getMarkedUpText()
+	{
+		return _markedUpText;
+	}
+
+	public Place getTopScorePlace()
+	{
+		Place p = _places.get(0);
+
+		for (Place pp : _places)
+		{
+			if (pp.getScore() > p.getScore())
+			{
+				p = pp;
+			}
+		}
+		return p;
+	}
+
+	/**
+	 * Used to make the original place scores more accurate by using other place
+	 * information
+	 */
+	public void adjustScores()
+	{
+		for (Place p : _places)
+		{
+			if (!p.isDirectlyReferenced())
+			{
+				p.setScore((int) (p.getScore() * _penaltyPercentage));
+			}
+		}
+
+		Place topScore = getTopScorePlace();
+
+		for (Place p : _places)
+		{
+			if (p.getParentPlaceName() == null)
+			{
+				continue;
+			}
+
+			for (Place pp : _places)
+			{
+				if (p.isIn(pp) && (topScore.getScore() - pp.getScore()) <= (int) (topScore.getScore() * 0.1 * _parentLimitPercentage))
+				{
+					p.setScore((int) (p.getScore() + (pp.getScore() * _parentBonusPercentage)));
+				}
+			}
+		}
+	}
+
+	/**
+	 * Adds one to the score of the given place
+	 * 
+	 * @param p
+	 *            is the place to add one to the score of
+	 */
+	public void addScore(Place p, Integer scoreToAdd)
+	{
+		if (p == null)
+		{
+			return;
+		}
+
+		// If there is already a score for this key the increase it by one
+		if (_places.contains(p))
+		{
+			Place place = _places.get(_places.indexOf(p));
+			place.setScore(place.getScore() + scoreToAdd);
+		}
+		// If there is no score for this key then make one
+		else
+		{
+			p.setScore(scoreToAdd);
+			_places.add(p);
+
+			if (_placeNameMap.containsKey(p.getName()))
+			{
+				_placeNameMap.get(p.getName()).add(p);
+			}
+			else
+			{
+				ArrayList<Place> placeList = new ArrayList<Place>();
+				_placeNameMap.put(p.getName(), placeList);
+			}
+		}
+
+		// Add to the parent's score too (if there is one)
+		if (!(p.getParentPlaceName() == null))
+		{
+			// If there is an ancestor then add to it's score as well
+			if (p.getParentPlaceName().contains(", "))
+			{
+				String[] places = p.getParentPlaceName().split(", ");
+
+				ArrayList<Place> specificPlaces = null;
+
+				if (places.length == 3)
+				{
+					specificPlaces = PlaceInformation.getSpecificPlace(places[0], places[1] + ", " + places[2]);
+				}
+				else
+				{
+					specificPlaces = PlaceInformation.getSpecificPlace(places[0], places[1]);
+				}
+
+				if (specificPlaces == null)
+				{
+					return;
+				}
+
+				for (Place pp : specificPlaces)
+				{
+					addScore(pp, (int) (scoreToAdd * _indirectReferencePenaltyPercentage));
+				}
+			}
+			// Otherwise just add the parent
+			else
+			{
+				ArrayList<Place> specificPlaces = PlaceInformation.getSpecificPlace(p.getParentPlaceName(), null);
+
+				if (specificPlaces == null)
+				{
+					return;
+				}
+
+				for (Place pp : specificPlaces)
+				{
+					addScore(pp, (int) (scoreToAdd * _indirectReferencePenaltyPercentage));
+				}
+			}
+		}
+	}
+
+	public void sortScores()
+	{
+		ArrayList<Place> sortedPlaces = new ArrayList<Place>();
+
+		while (_places.size() > 0)
+		{
+			int index = -1;
+			for (int j = 0; j < _places.size(); j++)
+			{
+				if (index == -1 || _places.get(j).getScore() > _places.get(index).getScore())
+				{
+					index = j;
+				}
+			}
+
+			sortedPlaces.add(_places.remove(index));
+		}
+
+		_places = sortedPlaces;
+	}
+
+	public ArrayList<Place> getPlacesWithParams(double maxScorePercentage, double minScorePercentage, long minPopulation, boolean locality, boolean region, boolean country, int numberOfPlacesToGet)
+	{
+		// System.out.println("Getting places with params");
+
+		int topScore = _places.get(0).getScore();
+		int minScore = (int) (topScore * minScorePercentage);
+		int maxScore = (int) (topScore * maxScorePercentage);
+
+		// System.out.println("minScore = " + minScore);
+		// System.out.println("maxScore = " + maxScore);
+
+		ArrayList<Place> matchingPlaces = new ArrayList<Place>();
+
+		// Go through all the markers
+		for (Place p : _places)
+		{
+			// System.out.println("Testing place -> " + p.getName());
+			if (p == null)
+			{
+				// System.out.println("P is null?");
+				continue;
+			}
+
+			// If the place meets the criteria
+			if (p.getScore() > minScore && p.getScore() <= maxScore && p.getPopulation() > minPopulation && ((locality && p.getPlaceType().equals("locality")) || (region && p.getPlaceType().equals("region")) || (country && p.getPlaceType().equals("country"))))
+			{
+				// System.out.println("MATCH!");
+				// If there is not already the maximum amount of visible places
+				// then add this
+				if (matchingPlaces.size() < numberOfPlacesToGet)
+				{
+					matchingPlaces.add(p);
+				}
+				// If there is already MAXVISIBLE visible places then see if
+				// this place should replace one
+				else
+				{
+					Place minScorePlace = null;
+					for (Place q : matchingPlaces)
+					{
+						if (minScorePlace == null)
+						{
+							minScorePlace = q;
+						}
+						if (q.getScore() < minScorePlace.getScore())
+						{
+							minScorePlace = q;
+						}
+					}
+
+					if (p.getScore() > minScorePlace.getScore())
+					{
+						matchingPlaces.remove(minScorePlace);
+						matchingPlaces.add(p);
+					}
+				}
+			}
+			else
+			{
+				// System.out.println("NOT A MATCH");
+			}
+		}
+		return matchingPlaces;
+	}
+
+	public void setScoringParams(double penaltyPercentage, double parentBonusPercentage, double indirectReferencePenaltyPercentage)
+	{
+		_penaltyPercentage = penaltyPercentage;
+		_parentBonusPercentage = parentBonusPercentage;
+		_indirectReferencePenaltyPercentage = indirectReferencePenaltyPercentage;
+	}
+
+	public void setScoringParams(ScanConfiguration config)
+	{
+		_penaltyPercentage = config.getPenalty();
+		_parentBonusPercentage = config.getParentBonus();
+		_indirectReferencePenaltyPercentage = config.getIndirectReferencePenalty();
+		_parentLimitPercentage = config.getParentLimit();
+	}
+
+	// public HashMap<String, Integer> wordCount(String fileName)
+	// {
+	// HashMap<String, Integer> wordCountMap = new HashMap<String, Integer>();
+	//
+	// try
+	// {
+	// BufferedReader file = new BufferedReader(new FileReader(fileName));
+	//
+	// StringBuilder currentWord = new StringBuilder();
+	//
+	// String line = "";
+	//
+	// ArrayList<String> words = new ArrayList<String>();
+	//
+	// //System.out.print("Finding words... ");
+	// while((line = file.readLine()) != null)
+	// {
+	// words.addAll(MarkupService.findWords(line));
+	// }
+	// //System.out.println("Done!");
+	//
+	// //System.out.print("Adding up scores... ");
+	// for(int j = 0; j < words.size(); j++)
+	// {
+	// if(Character.isLowerCase(words.get(j).charAt(0)))
+	// {
+	// //continue;
+	// }
+	//
+	// currentWord.append(words.get(j));
+	//
+	// int count = 1;
+	// while(_gazetteer.checkPlaceName(currentWord.toString()) != -1)
+	// {
+	// if(_gazetteer.checkPlaceName(currentWord.toString()) == 1)
+	// {
+	// if(wordCountMap.containsKey(currentWord.toString()))
+	// {
+	// Integer i = wordCountMap.get(currentWord.toString());
+	// wordCountMap.put(currentWord.toString(), ++i);
+	// }
+	// else
+	// {
+	// wordCountMap.put(currentWord.toString(), 1);
+	// }
+	// }
+	// currentWord.append(" " + words.get(j + count++));
+	// }
+	//
+	// currentWord.delete(0, currentWord.length());
+	// }
+	// }
+	// catch(Exception ex)
+	// {
+	// ex.printStackTrace();
+	// }
+	//
+	// return wordCountMap;
+	// }
+
+	public boolean isGazetteerLoaded()
+	{
+		return _gazetteer != null;
+	}
+
+	public ArrayList<ArrayList<Place>> examineMultipleTexts(String[] texts)
+	{
+		ArrayList<ArrayList<Place>> multipleResults = new ArrayList<ArrayList<Place>>();
+		for (String text : texts)
+		{
+			if (text != null)
+			{
+				this.examineTextWithGate(null, text);
+
+				if (_places.size() > 0)
+				{
+					multipleResults.add(new ArrayList<Place>(_places));
+				}
+				else
+				{
+					multipleResults.add(null);
+				}
+			}
+			else
+			{
+				multipleResults.add(null);
+			}
+		}
+
+		return multipleResults;
+	}
+
+	public ArrayList<Place> getPlaces()
+	{
+		return _places;
+	}
+
+	public void clearPlaces()
+	{
+		_places.clear();
+	}
+}
+
+/*
+ * 
+ * // Halve the scores for places that do not have their parents // mentioned //
+ * ********************************************************************
+ * 
+ * String[] parentPlaceNames = p.getParentPlaceName().split(", "); if
+ * (parentPlaceNames.length == 1) { ArrayList<Place> parentList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[0], null); Place parent =
+ * null;
+ * 
+ * if (parentList != null && parentList.size() > 0) { parent =
+ * parentList.get(0);
+ * 
+ * if (!_places.contains(parent) ||
+ * !_places.get(_places.indexOf(parent)).isDirectlyReferenced()) {
+ * p.setScore((int) (p.getScore() * (1 - _penaltyPercentage))); } } } else if
+ * (parentPlaceNames.length == 2) { ArrayList<Place> parentList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[0], parentPlaceNames[1]);
+ * ArrayList<Place> ancestorList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[1], null);
+ * 
+ * Place parent = null; Place ancestor = null;
+ * 
+ * if (parentList != null && parentList.size() > 0) { parent =
+ * parentList.get(0);
+ * 
+ * if (!_places.contains(parent) ||
+ * !_places.get(_places.indexOf(parent)).isDirectlyReferenced()) {
+ * p.setScore((int) (p.getScore() * (1 - _penaltyPercentage))); } }
+ * 
+ * if (ancestorList != null && ancestorList.size() > 0) { ancestor =
+ * ancestorList.get(0);
+ * 
+ * if (!_places.contains(ancestor) ||
+ * !_places.get(_places.indexOf(ancestor)).isDirectlyReferenced()) {
+ * p.setScore((int) (p.getScore() * (1 - _penaltyPercentage))); } } } else if
+ * (parentPlaceNames.length == 3) { ArrayList<Place> parentList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[0], parentPlaceNames[1] +
+ * ", " + parentPlaceNames[2]); ArrayList<Place> firstAncestorList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[1], parentPlaceNames[2]);
+ * ArrayList<Place> secondAncestorList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[2], null);
+ * 
+ * Place parent = null; Place firstAncestor = null; Place secondAncestor = null;
+ * 
+ * if (parentList != null && parentList.size() > 0) { parent =
+ * parentList.get(0);
+ * 
+ * if (!_places.contains(parent) ||
+ * !_places.get(_places.indexOf(parent)).isDirectlyReferenced()) {
+ * p.setScore((int) (p.getScore() * (1 - _penaltyPercentage))); } }
+ * 
+ * if (firstAncestorList != null && firstAncestorList.size() > 0) {
+ * firstAncestor = firstAncestorList.get(0);
+ * 
+ * if (!_places.contains(firstAncestor) ||
+ * !_places.get(_places.indexOf(firstAncestor)).isDirectlyReferenced()) {
+ * p.setScore((int) (p.getScore() * (1 - _penaltyPercentage))); } }
+ * 
+ * if (secondAncestorList != null && secondAncestorList.size() > 0) {
+ * secondAncestor = secondAncestorList.get(0);
+ * 
+ * if (!_places.contains(secondAncestor) ||
+ * !_places.get(_places.indexOf(secondAncestor)).isDirectlyReferenced()) {
+ * p.setScore((int) (p.getScore() * (1 - _penaltyPercentage))); } } }
+ */
+// Add part of the parent's score to the child
+// *******************************************
+/*
+ * if (parentPlaceNames.length == 1) { ArrayList<Place> parentList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[0], null);
+ * 
+ * Place parent = null;
+ * 
+ * if (parentList != null && parentList.size() > 0) { parent =
+ * parentList.get(0);
+ * 
+ * if (_places.contains(parent)) { p.setScore(p.getScore() + (int)
+ * (_places.get(_places.indexOf(parent)).getScore() * _parentBonusPercentage));
+ * } } } else if (parentPlaceNames.length == 2) { ArrayList<Place> parentList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[0], parentPlaceNames[1]);
+ * ArrayList<Place> ancestorList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[1], null);
+ * 
+ * Place parent = null; Place ancestor = null;
+ * 
+ * if (parentList != null && parentList.size() > 0) { parent =
+ * parentList.get(0);
+ * 
+ * if (_places.contains(parent)) { p.setScore(p.getScore() + (int)
+ * (_places.get(_places.indexOf(parent)).getScore() * _parentBonusPercentage));
+ * } }
+ * 
+ * if (ancestorList != null && ancestorList.size() > 0) { ancestor =
+ * ancestorList.get(0);
+ * 
+ * if (_places.contains(ancestor)) { p.setScore(p.getScore() + (int)
+ * (_places.get(_places.indexOf(ancestor)).getScore() *
+ * _parentBonusPercentage)); } } } else if (parentPlaceNames.length == 3) {
+ * ArrayList<Place> parentList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[0], parentPlaceNames[1] +
+ * ", " + parentPlaceNames[2]); ArrayList<Place> firstAncestorList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[1], parentPlaceNames[2]);
+ * ArrayList<Place> secondAncestorList =
+ * PlaceInformation.getSpecificPlace(parentPlaceNames[2], null);
+ * 
+ * Place parent = null; Place firstAncestor = null; Place secondAncestor = null;
+ * 
+ * if (parentList != null && parentList.size() > 0) { parent =
+ * parentList.get(0);
+ * 
+ * if (_places.contains(parent)) { p.setScore(p.getScore() + (int)
+ * (_places.get(_places.indexOf(parent)).getScore() * _parentBonusPercentage));
+ * } }
+ * 
+ * if (firstAncestorList != null && firstAncestorList.size() > 0) {
+ * firstAncestor = firstAncestorList.get(0);
+ * 
+ * if (_places.contains(firstAncestor)) { p.setScore(p.getScore() + (int)
+ * (_places.get(_places.indexOf(firstAncestor)).getScore() *
+ * _parentBonusPercentage)); } }
+ * 
+ * if (secondAncestorList != null && secondAncestorList.size() > 0) {
+ * secondAncestor = secondAncestorList.get(0);
+ * 
+ * if (_places.contains(secondAncestor)) { p.setScore(p.getScore() + (int)
+ * (_places.get(_places.indexOf(secondAncestor)).getScore() *
+ * _parentBonusPercentage)); } } }
+ */
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/PlaceInformation.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/PlaceInformation.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/PlaceInformation.java	(revision 23906)
@@ -0,0 +1,251 @@
+package org.greenstone.server;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ArrayList;
+import org.greenstone.client.Place;
+import java.util.HashMap;
+
+public class PlaceInformation
+{
+	static Statement _database = null;
+	static HashMap<String, ArrayList<Place>> _knownPlaces = new HashMap<String, ArrayList<Place>>();
+
+	/**
+	 * Default constructor Creates a connection to the database
+	 */
+	public static void init()
+	{
+		try
+		{
+			System.out.print("Loading postgresql driver... ");
+			Class.forName("org.postgresql.Driver");
+			Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/GS3MapDB", "postgres", "admin");//DriverManager.getConnection("jdbc:postgresql://localhost/test", "sjm84", "password");
+			System.out.println("Done!");
+			_database = c.createStatement();
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+
+	/**
+	 * Searches the database for a specific place by using the place name and
+	 * the parent's place names
+	 * 
+	 * @param id
+	 *            is the id of the place to search for in the database
+	 * @return the list of places found (in case there is more than one match)
+	 */
+	public static ArrayList<Place> getSpecificPlace(Long id)
+	{
+		try
+		{
+			ResultSet results = _database.executeQuery("SELECT * FROM places WHERE id = " + id);
+
+			// Go through the results and create a list of places
+			ArrayList<Place> placeList = new ArrayList<Place>();
+
+			while (results.next())
+			{
+				Place p = Place.createPlaceFromInformation(results.getLong("id"), results.getString("placename"), results.getString("subregion"), results.getString("region"), results.getString("country"), results.getString("placetype"), (Float) (results.getObject("longitude")), (Float) (results.getObject("latitude")), results.getLong("population"));
+
+				if (p != null)
+				{
+					placeList.add(p);
+				}
+			}
+
+			if (placeList.size() == 0)
+			{
+				placeList = null;
+			}
+
+			results.close();
+
+			// Return the list of places
+			return placeList;
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * Searches the database for a specific place by using the place name and
+	 * the parent's place names
+	 * 
+	 * @param placeName
+	 *            is the name of the place to find
+	 * @param parentPlaceName
+	 *            is the name of the place's parents (seperated by ", ")
+	 * @return the list of places found (in case there is more than one match)
+	 */
+	public static ArrayList<Place> getSpecificPlace(String placeName, String parentPlaceName)
+	{
+		// Make sure the place name to find is not null
+		if (placeName == null)
+		{
+			return null;
+		}
+
+		ResultSet results = null;
+
+		String firstParent = null;
+		String secondParent = null;
+		String thirdParent = null;
+
+		// Separate the parents
+		if (parentPlaceName != null)
+		{
+			String[] parents = parentPlaceName.split(", ");
+
+			if (parents.length == 1)
+			{
+				firstParent = parents[0];
+			}
+			else if (parents.length == 2)
+			{
+				firstParent = parents[0];
+				secondParent = parents[1];
+			}
+			else
+			{
+				firstParent = parents[0];
+				secondParent = parents[1];
+				thirdParent = parents[2];
+			}
+		}
+
+		try
+		{
+			// If the place does not have a parent
+			if (parentPlaceName == null)
+			{
+				results = _database.executeQuery("SELECT * FROM places WHERE placename = '" + placeName.replaceAll("'", "\\\\'") + "' " + "and subregion is null " + "and region is null " + "and country is null;");
+			}
+			// If the place has one parent
+			else if (secondParent == null)
+			{
+				results = _database.executeQuery("SELECT * FROM places WHERE placename = '" + placeName.replaceAll("'", "\\\\'") + "' " + "and subregion is null " + "and region is null " + "and country = '" + firstParent.replaceAll("'", "\\\\'") + "';");
+			}
+			// If the place has two parents
+			else if (thirdParent == null)
+			{
+				results = _database.executeQuery("SELECT * FROM places WHERE placename = '" + placeName.replaceAll("'", "\\\\'") + "' " + "and subregion is null " + "and region = '" + firstParent.replaceAll("'", "\\\\'") + "' " + "and country = '" + secondParent.replaceAll("'", "\\\\'") + "';");
+			}
+			// If the place has three parents
+			else
+			{
+				results = _database.executeQuery("SELECT * FROM places WHERE placename = '" + placeName.replaceAll("'", "\\\\'") + "' " + "and subregion = '" + firstParent.replaceAll("'", "\\\\'") + "' " + "and region = '" + secondParent.replaceAll("'", "\\\\'") + "' " + "and country = '" + thirdParent.replaceAll("'", "\\\\'") + "';");
+			}
+
+			// Go through the results and create a list of places
+			ArrayList<Place> placeList = new ArrayList<Place>();
+
+			while (results.next())
+			{
+				Place p = Place.createPlaceFromInformation(results.getLong("id"), results.getString("placename"), results.getString("subregion"), results.getString("region"), results.getString("country"), results.getString("placetype"), (Float) (results.getObject("longitude")), (Float) (results.getObject("latitude")), results.getLong("population"));
+
+				if (p != null)
+				{
+					placeList.add(p);
+				}
+			}
+
+			if (placeList.size() == 0)
+			{
+				placeList = null;
+			}
+
+			results.close();
+
+			// Return the list of places
+			return placeList;
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * Gets all of the place with the given name
+	 * 
+	 * @param placeName
+	 * @return
+	 */
+	public static ArrayList<Place> getPlaces(String placeName)
+	{
+		ArrayList<Place> placeList = new ArrayList<Place>();
+
+		ArrayList<Place> places = null;
+		if((places = _knownPlaces.get(placeName)) != null)
+		{
+			return places;
+		}
+		
+		try
+		{
+			if (_database == null)
+			{
+				System.out.println("***?***");
+			}
+			// Do a database query with the given place name
+			ResultSet results = _database.executeQuery("SELECT * FROM places WHERE placename = \'" + placeName.replaceAll("'", "\\\\'") + "\'");
+
+			// Go through the results and add them to the list of found places
+			while (results.next())
+			{
+				Place p = Place.createPlaceFromInformation(results.getLong("id"), results.getString("placename"), results.getString("subregion"), results.getString("region"), results.getString("country"), results.getString("placetype"), (Float) (results.getObject("longitude")), (Float) (results.getObject("latitude")), results.getLong("population"));
+
+				if (p != null)
+				{
+					placeList.add(p);
+				}
+			}
+			results.close();
+
+			// Find if any places have this place name as a alternative name
+			ResultSet altNames = _database.executeQuery("SELECT * FROM alternatenames WHERE alternatename = \'" + placeName.replaceAll("'", "\\\\'") + "\'");
+
+			ArrayList<Long> alternateIndexes = new ArrayList<Long>();
+
+			while (altNames.next())
+			{
+				alternateIndexes.add(altNames.getLong("placeindex"));
+			}
+			altNames.close();
+
+			for (Long index : alternateIndexes)
+			{
+				results = _database.executeQuery("SELECT * FROM places WHERE id = " + index);
+
+				while (results.next())
+				{
+					Place p = Place.createPlaceFromInformation(results.getLong("id"), results.getString("placename"), results.getString("subregion"), results.getString("region"), results.getString("country"), results.getString("placetype"), (Float) (results.getObject("longitude")), (Float) (results.getObject("latitude")), results.getLong("population"));
+
+					if (p != null)
+					{
+						placeList.add(p);
+					}
+				}
+			}
+			results.close();
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+
+		_knownPlaces.put(placeName, placeList);
+		
+		return placeList;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ScanConfiguration.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ScanConfiguration.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ScanConfiguration.java	(revision 23906)
@@ -0,0 +1,63 @@
+package org.greenstone.server;
+
+public class ScanConfiguration
+{
+	protected double _penaltyPercentage = -1;
+	protected double _parentBonusPercentage = -1;
+	protected double _indirectReferencePercentage = -1;
+	protected double _parentLimitPercentage = -1;
+	
+	public ScanConfiguration(double penaltyPercentage, double parentBonusPercentage, double indirectReferencePercentage, double parentLimitPercentage)
+	{
+		_penaltyPercentage = penaltyPercentage;
+		_parentBonusPercentage = parentBonusPercentage;
+		_indirectReferencePercentage = indirectReferencePercentage;
+		_parentLimitPercentage = parentLimitPercentage;
+	}
+	
+	public double getPenalty()
+	{
+		return _penaltyPercentage;
+	}
+	
+	public double getParentBonus()
+	{
+		return _parentBonusPercentage;
+	}
+	
+	public double getIndirectReferencePenalty()
+	{
+		return _indirectReferencePercentage;
+	}
+	
+	public String toString()
+	{
+		return "" + _penaltyPercentage + " " + _parentBonusPercentage + " " + _indirectReferencePercentage + " " + _parentLimitPercentage;
+	}
+	
+	public boolean equals (Object other)
+	{
+		if(other == null)
+		{
+			return false;
+		}
+		
+		if(!(other instanceof ScanConfiguration))
+		{
+			return false;
+		}
+		
+		ScanConfiguration othersc = (ScanConfiguration)other;
+		
+		if(_penaltyPercentage == othersc.getPenalty() && _parentBonusPercentage == othersc.getParentBonus() && _indirectReferencePercentage == othersc.getIndirectReferencePenalty())
+		{
+			return true;
+		}
+		return false;
+	}
+
+	public double getParentLimit()
+	{
+		return _parentLimitPercentage;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ScanResultSet.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ScanResultSet.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ScanResultSet.java	(revision 23906)
@@ -0,0 +1,139 @@
+package org.greenstone.server;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.greenstone.client.Place;
+import org.greenstone.server.ServerPlace;
+
+public class ScanResultSet
+{
+	HashMap<String, ServerPlace> _resultSet = new HashMap<String, ServerPlace>(); 
+	String _name = null;
+
+	/*
+	public ScanResultSet(String name, ArrayList<ServerPlace> results, Object nothing)
+	{
+		_name = name;
+		for(ServerPlace p : results)
+		{
+			if(_resultSet.containsKey(p.getName()))
+			{
+				ServerPlace old = _resultSet.get(p.getName());
+				if (p.getScore() > old.getScore())
+				{
+					_resultSet.put(p.getName(), p);
+				}
+			}
+			else
+			{
+				_resultSet.put(p.getName(), p);
+			}
+		}
+	}
+	*/
+	public ScanResultSet(String name, ArrayList<Place> results)
+	{
+		_name = name;
+		for(Place p : results)
+		{
+			if(_resultSet.containsKey(p.getName()))
+			{
+				ServerPlace old = _resultSet.get(p.getName());
+				if (p.getScore() > old.getScore())
+				{
+					_resultSet.put(p.getName(), ServerPlace.convert(p));
+				}
+				else if(p.getScore() == old.getScore())
+				{
+					if(p.getPopulation() > old.getPopulation())
+					{
+						_resultSet.put(p.getName(), ServerPlace.convert(p));
+					}
+				}
+			}
+			else
+			{
+				_resultSet.put(p.getName(), ServerPlace.convert(p));
+			}
+		}
+	}
+	
+	public ScanResultSet(String name, Long[] ids)
+	{
+		_name = name;
+		for(int i = 0; i < ids.length; i++)
+		{
+//			if(i >= ids.length)
+//			{
+//				return;
+//			}
+			
+			ServerPlace p = ServerPlace.createPlaceFromId(ids[i]);
+			
+			if(PlaceInformation.getPlaces(p.getName()).size() > 1)
+			{
+				_resultSet.put(p.getName(), p);
+			}
+			//_resultSet.put(p.getName(), p);
+		}
+	}
+	
+	public int[] compareResults (ScanResultSet otherResults)
+	{
+		if(otherResults == null)
+		{
+			return null;
+		}
+		
+		int count = 0;
+		int total = 0;
+		for(String s : _resultSet.keySet())
+		{
+			if(otherResults.getResult(s) != null && otherResults.getResult(s).equals(_resultSet.get(s)))
+			{
+				count++;
+			}
+			else
+			{
+				if(otherResults.getResult(s) != null)
+				{
+					//System.out.println(_resultSet.get(s).getName() + ", " + _resultSet.get(s).getParentPlaceName() +  " (" + _resultSet.get(s).getScore() + ") != " + otherResults.getResult(s).getName() + ", " + otherResults.getResult(s).getParentPlaceName() + " (" + otherResults.getResult(s).getScore() + ")");
+				}
+				else
+				{
+					//System.out.println(_resultSet.get(s).getName() + ", " + _resultSet.get(s).getParentPlaceName() + " != " + otherResults.getResult(s));
+				}
+			}
+			total++;
+		}
+		
+		System.out.println("Result = " + count + "/" + total);
+		
+		return new int[]{count, total};
+	}
+	
+	public ServerPlace getResult(String key)
+	{
+		return _resultSet.get(key);
+	}
+	
+	public String getName()
+	{
+		return _name;
+	}
+
+	public void updateScores(ArrayList<Place> places)
+	{
+		for(String s : _resultSet.keySet())
+		{
+			for(int i = 0; i < places.size(); i++)
+			{
+				if(_resultSet.get(s).equals(places.get(i)) && _resultSet.get(s).getScore() < places.get(i).getScore())
+				{
+					_resultSet.get(s).setScore(places.get(i).getScore());
+				}
+			}
+		}
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ServerPlace.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ServerPlace.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ServerPlace.java	(revision 23906)
@@ -0,0 +1,375 @@
+package org.greenstone.server;
+
+import java.io.Serializable;
+import org.greenstone.client.Place;
+
+public class ServerPlace implements Serializable
+{
+	private static final long serialVersionUID = 6106265145261780206L;
+	protected static FindPlaceServiceImpl _fpsi = new FindPlaceServiceImpl();
+
+	protected String _placeName = null;
+	protected String _parentPlaceName = null;
+	protected String _placeType = null;
+
+	protected Float _latitude = null;
+	protected Float _longitude = null;
+
+	protected long _population = 0;
+	protected long _id = -1;
+	protected int _score = 0;
+
+	protected boolean _directlyReferenced = false;
+	protected static boolean _loopDone = false;
+	
+	protected static ServerPlace _tempPlace = null;
+
+	public ServerPlace()
+	{
+
+	}
+
+	public ServerPlace(String placeName)
+	{
+		_placeName = placeName;
+	}
+
+	public void setParentPlaceName(String parentPlaceName)
+	{
+		_parentPlaceName = parentPlaceName;
+	}
+
+	public boolean equals(Object placeToCompare)
+	{
+		if (placeToCompare == null)
+		{
+			return false;
+		}
+
+		if (placeToCompare instanceof ServerPlace)
+		{
+			ServerPlace otherPlace = (ServerPlace)(placeToCompare);
+			if(_placeName != null && otherPlace.getName() != null && _parentPlaceName != null && otherPlace.getParentPlaceName() != null && _placeName.equals(otherPlace.getName()))
+			{
+				String[] cols = _parentPlaceName.split(", ");
+				String[] otherCols = otherPlace.getParentPlaceName().split(", ");
+				
+				if(cols.length == 3 && otherCols.length == 2)
+				{
+					if(cols[0].equals(otherPlace.getName()) && cols[1].equals(otherCols[0]) && cols[2].equals(otherCols[1]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 2 && otherCols.length == 3)
+				{
+					if(otherCols[0].equals(_placeName) && otherCols[1].equals(cols[0]) && otherCols[2].equals(cols[1]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 2 && otherCols.length == 1)
+				{
+					if(cols[0].equals(otherPlace.getName()) && cols[1].equals(otherCols[0]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 1 && otherCols.length == 2)
+				{
+					if(otherCols[0].equals(_placeName) && otherCols[1].equals(cols[0]))
+					{
+						return true;
+					}
+				}
+			}
+			
+			if (_id != -1 && ((ServerPlace)(placeToCompare)).getId() != -1 && _id == otherPlace.getId())
+			{
+				return true;
+			}
+
+			String otherPlaceName = otherPlace.getName();
+			String otherParentName = otherPlace.getParentPlaceName();
+
+			if ((_placeName == null && otherPlaceName == null) && (_parentPlaceName == null && otherParentName == null))
+			{
+				return true;
+			}
+
+			if ((_placeName == null && otherPlaceName == null) && _parentPlaceName != null && otherParentName != null && _parentPlaceName.equals(otherParentName))
+			{
+				return true;
+			}
+
+			if ((_parentPlaceName == null && otherParentName == null) && _placeName != null && otherPlaceName != null && _placeName.equals(otherPlaceName))
+			{
+				return true;
+			}
+
+			if (_placeName != null && otherPlaceName != null && _parentPlaceName != null && otherParentName != null && _parentPlaceName.equals(otherPlace.getParentPlaceName()) && _placeName.equals(otherPlace.getName()))
+			{
+				return true;
+			}
+		}
+		
+		if (placeToCompare instanceof Place)
+		{
+			Place otherPlace = (Place)(placeToCompare);
+			if(_placeName != null && otherPlace.getName() != null && _parentPlaceName != null && otherPlace.getParentPlaceName() != null && _placeName.equals(otherPlace.getName()))
+			{
+				String[] cols = _parentPlaceName.split(", ");
+				String[] otherCols = otherPlace.getParentPlaceName().split(", ");
+				
+				if(cols.length == 3 && otherCols.length == 2)
+				{
+					if(cols[0].equals(otherPlace.getName()) && cols[1].equals(otherCols[0]) && cols[2].equals(otherCols[1]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 2 && otherCols.length == 3)
+				{
+					if(otherCols[0].equals(_placeName) && otherCols[1].equals(cols[0]) && otherCols[2].equals(cols[1]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 2 && otherCols.length == 1)
+				{
+					if(cols[0].equals(otherPlace.getName()) && cols[1].equals(otherCols[0]))
+					{
+						return true;
+					}
+				}
+				else if(cols.length == 1 && otherCols.length == 2)
+				{
+					if(otherCols[0].equals(_placeName) && otherCols[1].equals(cols[0]))
+					{
+						return true;
+					}
+				}
+			}
+			
+			if (_id != -1 && ((Place)(placeToCompare)).getId() != -1 && _id == ((Place)(placeToCompare)).getId())
+			{
+				return true;
+			}
+
+			String otherPlaceName = otherPlace.getName();
+			String otherParentName = otherPlace.getParentPlaceName();
+
+			if ((_placeName == null && otherPlaceName == null) && (_parentPlaceName == null && otherParentName == null))
+			{
+				return true;
+			}
+
+			if ((_placeName == null && otherPlaceName == null) && _parentPlaceName != null && otherParentName != null && _parentPlaceName.equals(otherParentName))
+			{
+				return true;
+			}
+
+			if ((_parentPlaceName == null && otherParentName == null) && _placeName != null && otherPlaceName != null && _placeName.equals(otherPlaceName))
+			{
+				return true;
+			}
+
+			if (_placeName != null && otherPlaceName != null && _parentPlaceName != null && otherParentName != null && _parentPlaceName.equals(otherPlace.getParentPlaceName()) && _placeName.equals(otherPlace.getName()))
+			{
+				return true;
+			}
+		}
+		
+		return false;
+	}
+
+	public String toString()
+	{
+		String info = "*************************";
+
+		info += "\nPlace name = " + _placeName + "\nParents = " + _parentPlaceName + "\nChildren = ";
+
+		info += "\nPlace type = " + _placeType;
+		info += "\nPopulation = " + _population;
+		info += "\nLatitude = " + _latitude + " Longitude = " + _longitude;
+		info += "\n*************************";
+
+		return info;
+	}
+
+	// Get Methods
+
+	public int getScore()
+	{
+		return _score;
+	}
+
+	public String getPlaceType()
+	{
+		return _placeType;
+	}
+
+	public String getParentPlaceName()
+	{
+		return _parentPlaceName;
+	}
+
+	public String getName()
+	{
+		return _placeName;
+	}
+
+	public Float getLatitude()
+	{
+		return _latitude;
+	}
+
+	public Float getLongitude()
+	{
+		return _longitude;
+	}
+
+	public long getPopulation()
+	{
+		return _population;
+	}
+
+	public long getId()
+	{
+		return _id;
+	}
+
+	public boolean isDirectlyReferenced()
+	{
+		return _directlyReferenced;
+	}
+
+	// Set Methods
+
+	public void setScore(int score)
+	{
+		_score = score;
+	}
+
+	public void setPlaceType(String placeType)
+	{
+		_placeType = placeType;
+	}
+
+	public void setLatitude(Float latitude)
+	{
+		_latitude = latitude;
+	}
+
+	public void setLongitude(Float longitude)
+	{
+		_longitude = longitude;
+	}
+
+	public void setPopulation(long population)
+	{
+		_population = population;
+	}
+
+	public void setId(long id)
+	{
+		_id = id;
+	}
+
+	public void directReference()
+	{
+		_directlyReferenced = true;
+	}
+
+	/**
+	 * Figures out what sort of place the line in the file is refering to (e.g.
+	 * locality, region, etc.)
+	 * 
+	 * @param placeName
+	 *            is the name of the place
+	 * @param subRegionName
+	 *            is the possible sub region the place is in
+	 * @param regionName
+	 *            is the possible region the place is in
+	 * @param countryName
+	 *            is the country the place is in
+	 * @param placeType
+	 *            is the type of place
+	 * @return returns the place
+	 */
+	public static ServerPlace createPlaceFromInformation(long id, String placeName, String subRegionName, String regionName, String countryName, String placeType, Float longitude, Float latitude, long population)
+	{
+		ServerPlace newPlace = new ServerPlace(placeName);
+
+		String parentPlaceName = null;
+		if (!(subRegionName == null))
+		{
+			parentPlaceName = subRegionName;
+		}
+		if (!(regionName == null))
+		{
+			if (parentPlaceName == null)
+			{
+				parentPlaceName = regionName;
+			}
+			else
+			{
+				parentPlaceName += ", " + regionName;
+			}
+		}
+		if (!(countryName == null))
+		{
+			if (parentPlaceName == null)
+			{
+				parentPlaceName = countryName;
+			}
+			else
+			{
+				parentPlaceName += ", " + countryName;
+			}
+		}
+
+		if (parentPlaceName == null && countryName == null)
+		{
+			if (placeType.equals("country"))
+			{
+				countryName = placeName;
+			}
+			else
+			{
+				return null;
+			}
+		}
+		
+		newPlace.setId(id);
+		newPlace.setParentPlaceName(parentPlaceName);
+		newPlace.setLatitude(latitude);
+		newPlace.setLongitude(longitude);
+		newPlace.setPopulation(population);
+		newPlace.setPlaceType(placeType);
+		return newPlace;
+	}
+	
+	public static ServerPlace convert(Place p)
+	{
+		ServerPlace sp = ServerPlace.createPlaceFromInformation(p.getId(), p.getName(), null, null, p.getParentPlaceName(), p.getPlaceType(), p.getLongitude(), p.getLatitude(), p.getPopulation()); 
+		
+		sp.setScore(p.getScore());
+		
+		if(p.isDirectlyReferenced())
+		{
+			sp.directReference();
+		}
+		
+		return sp;
+	}
+
+	public static ServerPlace createPlaceFromId(long id)
+	{
+		Place p = _fpsi.getPlaceById(id);
+		
+		//System.out.println("P = " + p);
+		
+		return ServerPlace.createPlaceFromInformation(p.getId(), p.getName(), null, null, p.getParentPlaceName(), p.getPlaceType(), p.getLongitude(), p.getLatitude(), p.getPopulation());
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ServerSideTests.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ServerSideTests.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/ServerSideTests.java	(revision 23906)
@@ -0,0 +1,1742 @@
+package org.greenstone.server;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.DataOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.ListModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import org.greenstone.client.Place;
+
+public class ServerSideTests
+{
+	String _currentLine = null;
+	ArrayList<String> _words = new ArrayList<String>();
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args)
+	{
+		// GazetteerTrie trie = new
+		// GazetteerTrie("/home/sam/Desktop/Research/Greenstone3-gate-svn2/web/GPNMS/dataen.txt");
+		// PlacesToPoints();
+
+		ServerSideTests sst = new ServerSideTests();
+		sst.go();
+	}
+
+	public void go()
+	{
+		DatabaseFix();
+		//PlacesToPoints();
+		// ExperimentSetUp();
+		//PrecisionExperiment();
+		//GazetteerExperiment();
+		//GazetteerTest();
+		//ModPlaceNames();
+		//analyseResults();
+	}
+	
+	public void analyseResults()
+	{
+		try
+		{
+			BufferedReader in = new BufferedReader(new FileReader("/home/sjm84/Desktop/deleteMe.txt"));
+			
+			TreeMap<Double, ArrayList<Double[]>> scores = new TreeMap<Double, ArrayList<Double[]>>();
+			
+			Double[] currentConfig = null;
+			
+			String line = null;
+			boolean score = false;
+			while((line = in.readLine()) != null)
+			{
+				if(score)
+				{
+					String[] scanScores = line.split(" ");
+					Double averageScore = Double.parseDouble(scanScores[5]);
+					
+					if(scores.containsKey(averageScore))
+					{
+						scores.get(averageScore).add(currentConfig);
+					}
+					else
+					{
+						ArrayList<Double[]> newList = new ArrayList<Double[]>();
+						newList.add(currentConfig);
+						scores.put(averageScore, newList);
+					}
+				}
+				else
+				{
+					String[] configValues = line.split(" ");
+					currentConfig = new Double[configValues.length];
+					
+					for(int i = 0; i < configValues.length; i++)
+					{
+						currentConfig[i] = Double.parseDouble(configValues[i]);
+					}
+				}
+				
+				score = !score;
+			}
+			
+			BufferedWriter out = new BufferedWriter(new FileWriter("/home/sjm84/Desktop/SortedScores.txt"));
+			
+			for(Double d : scores.keySet())
+			{
+				out.write(d + " -> ");
+				ArrayList<Double[]> current = scores.get(d);
+				
+				for(Double[] da : current)
+				{
+					for(int i = 0; i < da.length; i++)
+					{
+						out.write(da[i] + " ");
+					}
+					
+					out.write(" - ");
+				}
+				out.write("\n");
+			}
+			
+			in.close();
+			out.close();
+		}
+		catch(Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+	
+	public void ModPlaceNames()
+	{
+		try
+		{
+			BufferedReader in = new BufferedReader(new FileReader("/home/sjm84/Desktop/placenames.txt"));
+			
+			ArrayList<String> words = new ArrayList<String>();
+			
+			String line = null;
+			while((line = in.readLine()) != null)
+			{
+				String[] w = line.split(" ");
+				for(int i = 0; i < w.length; i++)
+				{
+					words.add(w[i]);
+				}
+			}
+			
+			BufferedWriter out = new BufferedWriter(new FileWriter("/home/sjm84/Desktop/placenames.txt"));
+			
+			for(int i = 0; i < words.size(); i++)
+			{
+				for(int j = 0; j < words.get(i).length(); j++)
+				{
+					if(words.get(i).charAt(j) > 127)
+					{
+						out.write(words.get(i) + "\n");
+					}
+				}
+			}
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+		}
+	}
+
+	public void GazetteerExperiment()
+	{
+		showMemoryUsage();
+	}
+	
+	public interface ObjectFactory
+	{
+		public Object makeObject();
+	}
+
+	public long calculateMemoryUsage()
+	{
+		ObjectFactory factory = new ObjectFactory()
+		{
+			public Object makeObject()
+			{
+				return new GazetteerTrieType5("/home/sam/Desktop/Research/Greenstone3-gate-svn2/web/GPNMS/dataen.txt");
+			}
+		};
+		
+		Object handle = factory.makeObject();
+		long mem0 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+		long mem1 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+		handle = null;
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		mem0 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+		handle = factory.makeObject();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		System.gc();
+		mem1 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+		return mem1 - mem0;
+	}
+
+	public void showMemoryUsage()
+	{
+		
+		long mem = 0;
+		for(int i = 0;i < 10; i++)
+		{
+			mem += calculateMemoryUsage();
+		}
+		System.out.println("Object takes " + (int)(mem/10) + " bytes");
+	}
+
+	public void ExperimentSetUp()
+	{
+		final JFrame frame = new JFrame("Experiment setup program");
+		JList placeList = null;
+		final ButtonGroup alts = new ButtonGroup();
+		final ArrayList<JRadioButton> buttons = new ArrayList<JRadioButton>();
+		final JPanel buttonPanel = new JPanel();
+		buttonPanel.setLayout(new GridLayout(30, 3));
+		final HashMap<String, Place> choices = new HashMap<String, Place>();
+		final ArrayList<Place> possibleChoices = new ArrayList<Place>();
+		JButton enter = new JButton("GO");
+		String filename = "/research/sjm84/Msc/GNSDocs/ireland.html";// "/home/sam/Desktop/Research/GNSDocs/canada.html";//
+
+		try
+		{
+			PageScanner ps = new PageScanner("/research/sjm84/Msc/Downloads/");// new
+			// PageScanner("/home/sam/Desktop/Research/");//
+
+			BufferedReader file = new BufferedReader(new FileReader(filename));
+
+			ArrayList<ArrayList<String>> lines = new ArrayList<ArrayList<String>>();
+			String line = null;
+			while ((line = file.readLine()) != null)
+			{
+				lines.add(MarkupService.findWords(line));
+			}
+
+			ps.examineTextWithoutGate(lines);
+			final ArrayList<Place> filePlaces = ps.getPlaces();
+			ArrayList<Place> seenPlaces = new ArrayList<Place>();
+			for (Place p : filePlaces)
+			{
+				if (p.isDirectlyReferenced())
+				{
+					boolean found = false;
+					for (int i = 0; i < seenPlaces.size(); i++)
+					{
+						if (p.getName().equals(seenPlaces.get(i).getName()))
+						{
+							found = true;
+							break;
+						}
+					}
+					if (!found)
+					{
+						seenPlaces.add(p);
+					}
+				}
+			}
+
+			String[] items = new String[seenPlaces.size()];
+			for (int i = 0; i < seenPlaces.size(); i++)
+			{
+				items[i] = seenPlaces.get(i).getName();
+				choices.put(seenPlaces.get(i).getName(), null);
+			}
+
+			placeList = new JList(items);
+
+			placeList.addListSelectionListener(new ListSelectionListener()
+			{
+				int prevIndex = -1;
+
+				public void valueChanged(ListSelectionEvent e)
+				{
+					ListModel model = ((JList) (e.getSource())).getModel();
+					int index = ((JList) (e.getSource())).getSelectedIndex();
+					final String s = (String) (model.getElementAt(index));
+					if (index != prevIndex)
+					{
+						System.out.println("Value Changed!");
+						for (JRadioButton button : buttons)
+						{
+							buttonPanel.remove(button);
+							alts.remove(button);
+						}
+
+						buttons.clear();
+						possibleChoices.clear();
+
+						System.out.println("*Adding Button!");
+						JRadioButton noneButton = new JRadioButton("*None*");
+						alts.add(noneButton);
+						buttons.add(noneButton);
+						buttonPanel.add(noneButton);
+						noneButton.addActionListener(new ActionListener()
+						{
+							public void actionPerformed(ActionEvent e)
+							{
+								choices.put(s, null);
+							}
+						});
+
+						int count = 0;
+						for (Place p : filePlaces)
+						{
+							if (p.getName().equals(s))
+							{
+								System.out.println("Adding Button!");
+								possibleChoices.add(p);
+
+								JRadioButton button = new JRadioButton(p.getName() + ", " + p.getParentPlaceName());
+								alts.add(button);
+								buttons.add(button);
+								buttonPanel.add(button);
+								button.addActionListener(new RadioActionListener(count++, possibleChoices, choices));
+							}
+						}
+
+						buttonPanel.setSize(600, 150);
+						buttonPanel.validate();
+						frame.pack();
+						frame.validate();
+					}
+					prevIndex = ((JList) (e.getSource())).getSelectedIndex();
+				}
+			});
+
+			enter.addActionListener(new ButtonActionListener(choices));
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+
+		JScrollPane sp = new JScrollPane(placeList);
+		frame.setLayout(new BorderLayout());
+		sp.setSize(600, 150);
+		frame.add(sp, BorderLayout.WEST);
+		buttonPanel.setSize(600, 150);
+		frame.add(buttonPanel, BorderLayout.CENTER);
+		enter.setSize(600, 100);
+		frame.add(enter, BorderLayout.EAST);
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setSize(600, 400);
+		frame.setVisible(true);
+	}
+
+	class RadioActionListener implements ActionListener
+	{
+		int _index = -1;
+		ArrayList<Place> _possibleChoices = null;
+		HashMap<String, Place> _choices = null;
+
+		public RadioActionListener(int index, ArrayList<Place> possibleChoices, HashMap<String, Place> choices)
+		{
+			_index = index;
+			_possibleChoices = possibleChoices;
+			_choices = choices;
+		}
+
+		public void actionPerformed(ActionEvent e)
+		{
+			System.out.println("BUTTON CLICKED");
+			_choices.put(_possibleChoices.get(_index).getName(), _possibleChoices.get(_index));
+		}
+	}
+
+	class ButtonActionListener implements ActionListener
+	{
+		HashMap<String, Place> _choices = null;
+
+		public ButtonActionListener(HashMap<String, Place> choices)
+		{
+			_choices = choices;
+		}
+
+		public void actionPerformed(ActionEvent e)
+		{
+			System.out.println("GO CLICKED");
+			try
+			{
+				BufferedWriter out = new BufferedWriter(new FileWriter("/home/sjm84/Desktop/test.txt"));
+
+				for (String s : _choices.keySet())
+				{
+					System.out.println(s + " == " + _choices.get(s));
+					if (_choices.get(s) != null)
+					{
+						out.write("new Long(" + _choices.get(s).getId() + "), /* " + _choices.get(s).getName() + " */\n");
+					}
+				}
+				out.close();
+			}
+			catch (Exception exception)
+			{
+				exception.printStackTrace();
+			}
+		}
+	}
+
+	public void PrecisionExperiment()
+	{
+		ArrayList<ScanConfiguration> configs = new ArrayList<ScanConfiguration>();
+		HashMap<ScanConfiguration, ArrayList<int[]>> results = new HashMap<ScanConfiguration, ArrayList<int[]>>();
+
+		for (int i = 0; i < 6; i++)
+		{
+			for (int j = 0; j < 6; j++)
+			{
+				for (int k = 0; k < 6; k++)
+				{
+					for(int l = 0; l < 6; l++)
+					{		
+						ScanConfiguration sc = new ScanConfiguration(0.2 * i, 0.2 * j, 0.2 * k, l);
+						//ScanConfiguration sc = new ScanConfiguration(0, 0.8, 0.0, 1);
+						configs.add(sc);
+						results.put(sc, new ArrayList<int[]>());
+						
+						if(j == 0)
+						{
+							break;
+						}
+					}
+				}
+			}
+		}
+
+		try
+		{
+			PageScanner ps = new PageScanner("/research/sjm84/Msc/Downloads/");
+			//PageScanner ps = new PageScanner("/home/sam/Desktop/Research/");
+
+			String[] filenames = new String[] { "australia", "canada", "ireland", "southafrica", "washington" };
+			for (int i = 0; i < filenames.length; i++)
+			{
+				BufferedReader file = new BufferedReader(new FileReader("/research/sjm84/Msc/GNSDocs/" + filenames[i] + ".html"));
+				//BufferedReader file = new BufferedReader(new FileReader("/home/sam/Desktop/Research/GNSDocs/" + filenames[i] + ".html"));
+
+				StringBuilder text = new StringBuilder();
+				String line = null;
+				while ((line = file.readLine()) != null)
+				{
+					text.append(line);
+				}
+
+				ScanResultSet correctResults = getCorrectScanResultSet(i);
+				ArrayList<String> textPlaces = ps.getPlaceNames(text.toString());
+
+				int count = 0;
+				for (ScanConfiguration sc : configs)
+				{
+					count++;
+					System.out.println(sc + " " + count);
+					ps.setScoringParams(sc);
+					ps.examineArrayOfStrings(textPlaces);
+					ScanResultSet scanResult = new ScanResultSet(filenames[i], ps.getPlaces());
+					//correctResults.updateScores(ps.getPlaces());
+					int[] compareResults = correctResults.compareResults(scanResult);
+					if(compareResults == null)
+					{
+						System.out.println("NULL?!?");
+					}
+					else
+					{
+						results.get(sc).add(compareResults);
+					}
+				}
+			}
+
+			BufferedWriter out = new BufferedWriter(new FileWriter("/home/sjm84/Desktop/deleteMe.txt"));
+			//BufferedWriter out = new BufferedWriter(new FileWriter("/home/sam/Desktop/scanResults.txt"));
+
+			for (ScanConfiguration sc : results.keySet())
+			{
+				out.write(sc.toString() + "\n");
+				int correctCount = 0;
+				int totalCount = 0;
+				for (int[] i : results.get(sc))
+				{
+					out.write(((double)(i[0]))/((double)(i[1])) + " ");
+					correctCount += i[0];
+					totalCount += i[1];
+				}
+
+				out.write(((double)(correctCount)) / ((double)(totalCount)) + "\n");
+			}
+			out.close();
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+
+	public static void ListPopulate()
+	{
+		Statement database = null;
+
+		try
+		{
+			Class.forName("org.postgresql.Driver");
+			Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/GS3MapDB", "sam", "password");
+
+			database = c.createStatement();
+
+			ResultSet places = database.executeQuery("SELECT placename, placetype FROM places WHERE population > -1");
+
+			BufferedWriter countryList = new BufferedWriter(new FileWriter("/research/sjm84/Msc/Downloads/CountryList.lst"));
+			BufferedWriter cityList = new BufferedWriter(new FileWriter("/research/sjm84/Msc/Downloads/CityList.lst"));
+
+			while (places.next())
+			{
+				String pn = places.getString("placename");
+				String pt = places.getString("placetype");
+
+				if (pt.equals("country"))
+				{
+					countryList.write(pn + "\n");
+				}
+				else
+				{
+					cityList.write(pn + "\n");
+				}
+			}
+			places.close();
+			ResultSet alternatePlaces = database.executeQuery("SELECT * FROM alternatenames WHERE alternatename is not null");
+
+			while (alternatePlaces.next())
+			{
+				cityList.write(alternatePlaces.getString("alternatename") + "\n");
+			}
+			alternatePlaces.close();
+
+			cityList.close();
+			countryList.close();
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+
+	public static void RPCTest()
+	{
+		try
+		{
+			String payload = "5|0|4|http://localhost:9090/greenstone3/interfaces/default/js/mapScripts/|FC39F395243F4B8A4145D4DCB673D89A|org.greenstone.client.findPlaceService|isGazetteerLoaded|1|2|3|4|0|";
+
+			URL url = new URL("http://localhost:9090/gpnms/simplemaps/findPlace");
+			HttpURLConnection con = (HttpURLConnection) url.openConnection();
+
+			con.setUseCaches(false);
+			con.setDoOutput(true);
+			con.setDoInput(true);
+			DataOutputStream dataOut = new DataOutputStream(con.getOutputStream());
+			dataOut.write(payload.getBytes());
+			dataOut.flush();
+			dataOut.close();
+			// DataInputStream in = new DataInputStream(con.getInputStream());
+			System.out.println("Content = " + con.getInputStream());
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+			System.out.println("Message = " + ex.getMessage());
+		}
+	}
+
+	public void GazetteerTest()
+	{
+		//GazetteerTrieType4 gaz = new GazetteerTrieType4("/home/sam/Desktop/Research/Greenstone3-gate-svn2/web/GPNMS/dataen.txt");
+		GazetteerTrieType5 gaz = new GazetteerTrieType5("/research/sjm84/Msc/Downloads/dataen.txt");
+		initWords();
+		int totalTime = 0;
+		for (int i = 0; i < 100; i++)
+		{
+			Long time = System.currentTimeMillis();
+			try
+			{
+				int count = 0;
+				boolean done = false;
+				while (!done)
+				{
+					StringBuilder currentWords = new StringBuilder();
+
+					int length = 0;
+					while (gaz.checkPlaceName(currentWords.toString()) != -1)
+					{		
+						String word = null;
+						if(count + length < _words.size())
+						{
+							word = _words.get(count + length);
+						}
+						if (word == null)
+						{
+							totalTime += System.currentTimeMillis() - time;
+							// _in.close();
+							//System.out.println("DONE");
+							done = true;
+						}
+						if(currentWords.length() == 0)
+						{
+							currentWords.append(word);
+						}
+						else
+						{
+							currentWords.append(" " + word);
+						}
+						length++;
+					}
+					currentWords = new StringBuilder();
+					count++;
+				}
+			}
+			catch (Exception e)
+			{
+				e.printStackTrace();
+			}
+		}
+		System.out.println("Total = " + totalTime / 100);
+	}
+
+	public void initWords()
+	{
+		try
+		{
+			BufferedReader in = new BufferedReader(new FileReader("/home/sjm84/Desktop/placenames.txt"));
+
+			String line = null;
+			while ((line = in.readLine()) != null)
+			{	
+				for (String word : line.split(" "))
+				{
+					_words.add(word);
+					_words.add(word);
+					_words.add(word);
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		return;
+	}
+
+
+	public static void PlaceNameCheck()
+	{
+		GazetteerTrieType2 gazetteer = new GazetteerTrieType2("/research/sjm84/Msc/Downloads/dataen.txt");
+
+		try
+		{
+			String inputLine = "";
+			BufferedReader inputStream = new BufferedReader(new InputStreamReader(System.in));
+			while ((inputLine = inputStream.readLine()) != "q")
+			{
+				if (gazetteer.checkPlaceName(inputLine) == 1)
+				{
+					System.out.println(inputLine + " is a place");
+				}
+				else
+				{
+					System.out.println(inputLine + " is not a place");
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+
+	public static void PlacesToPoints()
+	{
+		ArrayList<Float> latitudes = new ArrayList<Float>();
+		ArrayList<Float> longitudes = new ArrayList<Float>();
+		ArrayList<Long> ids = new ArrayList<Long>();
+
+		Statement database = null;
+
+		try
+		{
+			Class.forName("org.postgresql.Driver");
+			Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/GS3MapDB", "postgres", "admin");
+
+			database = c.createStatement();
+
+			ResultSet places = database.executeQuery("SELECT id, latitude, longitude FROM places WHERE latitude is not null");
+
+			while (places.next())
+			{
+				latitudes.add(places.getFloat("latitude"));
+				longitudes.add(places.getFloat("longitude"));
+				ids.add(places.getLong("id"));
+			}
+
+			for (int i = 0; i < latitudes.size(); i++)
+			{
+				System.out.println("INSERT INTO placepoints (id, point) VALUES (" + ids.get(i) + ", GeomFromText('POINT(" + latitudes.get(i) + " " + longitudes.get(i) + ")'));");
+				database.execute("INSERT INTO placepoints (id, point) VALUES (" + ids.get(i) + ", GeomFromText('POINT(" + latitudes.get(i) + " " + longitudes.get(i) + ")'));");
+			}
+
+			System.out.println("lat size = " + latitudes.size());
+			System.out.println("lng size = " + longitudes.size());
+			System.out.println("id size = " + ids.size());
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+
+	public static void PopulateDatabase()
+	{
+		Statement database = null;
+		try
+		{
+			Class.forName("org.postgresql.Driver");
+			Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/GS3MapDB", "postgres", "admin");
+
+			database = c.createStatement();
+
+			BufferedReader gazetteerFile = new BufferedReader(new FileReader("C:\\Users\\sjm84\\Desktop\\EclipseWorkspace\\ATLAS\\war\\dataen.txt"));
+
+			String line = "";
+			while ((line = gazetteerFile.readLine()) != null)
+			{
+				// The file is tab seperated so split it by tabs
+				String[] columns = line.split("\t");
+
+				String placeName = GazetteerHelper.getMainPlaceName(columns);
+				if (placeName != null)
+				{
+					placeName = placeName.replaceAll("\'", "\\\\\'");
+				}
+				String countryName = GazetteerHelper.getCountryName(columns);
+				if (countryName != null)
+				{
+					countryName = countryName.replaceAll("\'", "\\\\\'");
+				}
+				String regionName = GazetteerHelper.getRegionName(columns);
+				if (regionName != null)
+				{
+					regionName = regionName.replaceAll("\'", "\\\\\'");
+				}
+				String subRegionName = GazetteerHelper.getSubRegionName(columns);
+				if (subRegionName != null)
+				{
+					subRegionName = subRegionName.replaceAll("\'", "\\\\\'");
+				}
+				String placeType = GazetteerHelper.getPlaceType(columns);
+				if (placeType != null)
+				{
+					placeType = placeType.replaceAll("\'", "\\\\\'");
+				}
+				Float latitude = GazetteerHelper.getLatitude(columns);
+				Float longitude = GazetteerHelper.getLongitude(columns);
+				int population = GazetteerHelper.getPopulation(columns);
+				Integer id = GazetteerHelper.getID(columns);
+
+				ArrayList<String> alternatePlaceNames = GazetteerHelper.getAlternativePlaceNames(columns);
+
+				// Because counties do not contain the region information they
+				// are not helpful, so ignore them
+				if (placeName == null || id == null || placeType == null || placeType.equals("county") || placeType.equals("agglomeration"))
+				{
+					continue;
+				}
+
+				try
+				{
+					/*
+					 * System.out.println("Inserting ---> \n" +
+					 * "INSERT INTO places (id, placename, placetype, subregion, region, country, population, latitude, longitude) VALUES("
+					 * + id + ", " + "\'" + placeName + "\'" + ", " + "\'" +
+					 * placeType + "\'" + ", " + (subRegionName == null ? "null"
+					 * : ("\'" + subRegionName + "\'")) + ", " + (regionName ==
+					 * null ? "null" : ("\'" + regionName + "\'")) + ", " +
+					 * (countryName == null ? "null" : ("\'" + countryName +
+					 * "\'")) + ", " + population + ", " + latitude + ", " +
+					 * longitude + ");");
+					 */
+					database.execute("INSERT INTO places (id, placename, placetype, subregion, region, country, population, latitude, longitude) VALUES(" + id + ", " + "\'" + placeName + "\'" + ", " + "\'" + placeType + "\'" + ", " + (subRegionName == null ? "null" : ("\'" + subRegionName + "\'")) + ", " + (regionName == null ? "null" : ("\'" + regionName + "\'")) + ", " + (countryName == null ? "null" : ("\'" + countryName + "\'")) + ", " + population + ", " + latitude + ", " + longitude + ");");
+
+					if (alternatePlaceNames != null)
+					{
+						for (String altName : alternatePlaceNames)
+						{
+							if (altName != null)
+							{
+								altName = altName.replaceAll("\'", "\\\\\'");
+								/*
+								 * System.out.println("INSERT INTO alternatenames (alternatename, placeindex) VALUES("
+								 * + "\'" + altName + "\', " + id + ");");
+								 */
+								database.execute("INSERT INTO alternatenames (alternatename, placeindex) VALUES(" + "\'" + altName + "\', " + id + ");");
+							}
+						}
+					}
+				}
+				catch (Exception ex)
+				{
+					ex.printStackTrace();
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+
+	public static void DatabaseFix()
+	{
+		Statement database = null;
+
+		try
+		{
+			Class.forName("org.postgresql.Driver");
+			Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/GS3MapDB", "postgres", "admin");
+
+			database = c.createStatement();
+
+			ResultSet results = database.executeQuery("SELECT * FROM places WHERE population >= 0;");
+
+			ArrayList<String> placeNameList = new ArrayList<String>();
+			ArrayList<String> subRegionList = new ArrayList<String>();
+			ArrayList<String> regionList = new ArrayList<String>();
+			ArrayList<String> countryList = new ArrayList<String>();
+
+			while (results.next())
+			{
+				placeNameList.add(results.getString("placename"));
+				subRegionList.add(results.getString("subregion"));
+				regionList.add(results.getString("region"));
+				countryList.add(results.getString("country"));
+			}
+
+			int counter = 0;
+			while (counter < placeNameList.size())
+			{
+				String placeName = placeNameList.get(counter);
+				String subRegionName = subRegionList.get(counter);
+				String regionName = regionList.get(counter);
+				String countryName = countryList.get(counter);
+
+				System.out.println("* " + counter++ + " " + placeName);
+
+				if (subRegionName != null)
+				{
+					ResultSet subRegionResults = database.executeQuery("SELECT * FROM places WHERE " + "placename = \'" + subRegionName.replaceAll("\'", "\\\\\'") + "\' AND " + "region = \'" + regionName.replaceAll("\'", "\\\\\'") + "\' AND " + "country = \'" + countryName.replaceAll("\'", "\\\\\'") + "\';");
+
+					int count = 0;
+					while (subRegionResults.next())
+					{
+						count++;
+					}
+
+					if (count == 0)
+					{
+						System.out.println("Adding " + subRegionName.replaceAll("\'", "\\\\\'"));
+
+						database.execute("INSERT INTO places (placename, placetype, subregion, region, country, population, latitude, longitude) VALUES(" + "\'" + subRegionName.replaceAll("\'", "\\\\\'") + "\', " + "\'region\', " + "null, " + "\'" + regionName.replaceAll("\'", "\\\\\'") + "\', " + "\'" + countryName.replaceAll("\'", "\\\\\'") + "\', " + "0, " + "null, " + "null);");
+					}
+				}
+
+				if (regionName != null)
+				{
+					ResultSet regionResults = database.executeQuery("SELECT * FROM places WHERE " + "placename = \'" + regionName.replaceAll("\'", "\\\\\'") + "\' AND " + "country = \'" + countryName.replaceAll("\'", "\\\\\'") + "\';");
+
+					int count = 0;
+					while (regionResults.next())
+					{
+						count++;
+					}
+
+					if (count == 0)
+					{
+						System.out.println("Adding " + regionName.replaceAll("\'", "\\\\\'"));
+
+						database.execute("INSERT INTO places (placename, placetype, subregion, region, country, population, latitude, longitude) VALUES(" + "\'" + regionName.replaceAll("\'", "\\\\\'") + "\', " + "\'region\', " + "null, " + "null, " + "\'" + countryName.replaceAll("\'", "\\\\\'") + "\', " + "0, " + "null, " + "null);");
+					}
+				}
+
+				if (countryName != null)
+				{
+					ResultSet countryResults = database.executeQuery("SELECT * FROM places WHERE " + "placename = \'" + countryName.replaceAll("\'", "\\\\\'") + "\';");
+
+					int count = 0;
+					while (countryResults.next())
+					{
+						count++;
+					}
+
+					if (count == 0)
+					{
+						System.out.println("Adding " + countryName.replaceAll("\'", "\\\\\'"));
+
+						database.execute("INSERT INTO places (placename, placetype, subregion, region, country, population, latitude, longitude) VALUES(" + "\'" + countryName.replaceAll("\'", "\\\\\'") + "\', " + "\'country\', " + "null, " + "null, " + "null, " + "0, " + "null, " + "null);");
+					}
+				}
+			}
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+
+	public ScanResultSet getCorrectScanResultSet(int index)
+	{
+		ScanResultSet correctResultSet = null;
+
+		if (index == 0)
+		{
+			correctResultSet = new ScanResultSet("australia", new Long[] { new Long(-24), /* Australia */
+			new Long(196916913), /* Canberra */
+			new Long(202101121), /* Sydney */
+			new Long(-81), /* UK */
+			new Long(-435), /* Tasmania */
+			new Long(-101), /* Indonesia */
+			new Long(-214), /* East_Timor */
+			new Long(-170), /* PNG */
+			new Long(-186), /* Solomon_Islands */
+			new Long(-232), /* Vanuatu */
+			new Long(-155), /* New_Caledonia */
+			new Long(-165), /* New_Zealand */
+			new Long(187916496), /* Melbourne */
+			new Long(225177302), /* Brisbane */
+			new Long(208973584), /* Perth */
+			new Long(198283860), /* Adelaide */
+			new Long(279283085), /* Darwin */
+			new Long(-431), /* NSW */
+			new Long(-430), /* ACT */
+			new Long(301780683), /* Jakarta */
+			new Long(-160), /* Netherlands */
+			new Long(-1495), /* England */
+			new Long(-434), /* South_Australia */
+			new Long(-436), /* Victoria */
+			new Long(-433), /* Queensland */
+			new Long(-432), /* Northern_Territory */
+			new Long(-437), /* WA */
+			new Long(469532668), /* Gelibolu(Gallipoli) */
+			new Long(-223), /* Christmas_Island */
+			new Long(-3902), /* Cocos_Islands */
+			new Long(-3900), /* Ashmore_and_Cartier */
+			new Long(-3903), /* Coral_Sea_Islands */
+			new Long(-157), /* Norfolk_Island */
+			new Long(263120575), /* Cairns */
+			new Long(-111), /* Japan */
+			new Long(-152), /* Malaysia */
+			new Long(-190), /* Singapore */
+			new Long(-45), /* Canada */
+			new Long(-188), /* Sudan */
+			new Long(-105), /* Iraq */
+			new Long(-161), /* Norway */
+			new Long(-155), /* Kiribati */
+			new Long(-163), /* Nauru */
+			new Long(-217), /* Tuvalu */
+			new Long(-54), /* China */
+			new Long(-108), /* Italy */
+			new Long(-231), /* Vietnam */
+			new Long(222981344), /* Gold_Coast */
+			new Long(205521175), /* Newcastle */
+			new Long(196772922), /* Queanbeyan */
+			new Long(200121087), /* Wollongong */
+			new Long(230865256), /* Sunshine_Coast */
+			new Long(169772729), /* Hobart */
+			new Long(186728432), /* Geelong */
+			new Long(254696678), /* Townsville */
+			new Long(224817196), /* Toowoomba */
+			new Long(174812713), /* Launceston */
+			new Long(194216692), /* Albury */
+			new Long(193964688), /* Wodonga */
+			new Long(188816384), /* Ballarat */
+			new Long(191696428), /* Bendigo */
+			new Long(-126), /* Liechtenstein */
+			new Long(-63), /* Denmark */
+			new Long(-972), /* Aomen */
+			new Long(-32), /* Bulgaria */
+			new Long(-79), /* France */
+			new Long(-140), /* Mali */
+			new Long(-189), /* Sweden */
+			new Long(-131), /* Luxembourg */
+			new Long(510353874), /* Oxford */
+			new Long(511974013), /* Cambridge */
+			// new Long(509489990), /* London */
+			new Long(-75), /* Fiji */
+			new Long(-77), /* Micronesia */
+			new Long(-138), /* Marshall Islands */
+			new Long(-178), /* Palau */
+			new Long(-234), /* Samoa */
+			new Long(-213), /* Tonga */
+			new Long(-232), /* Vanuatu */
+			new Long(-169), /* French Polynesia */
+			new Long(-155), /* New Caledonia */
+			new Long(-233), /* Wallis and Futuna */
+			new Long(-51), /* Cook Islands */
+			new Long(-164), /* Niue */
+			new Long(-210), /* Tokelau */
+			new Long(-3912), /* Pitcairn */
+			new Long(-22), /* American Samoa */
+			new Long(-94), /* Guam */
+			new Long(-3767), /* Hawaii */
+			new Long(-143), /* Chile */
+			new Long(-3907), /* Easter Island */
+			new Long(-102), /* Ireland */
+			new Long(-239), /* Isle of Man */
+			new Long(-238), /* Jersey */
+			new Long(-147), /* Malta */
+			new Long(-14), /* Antigua and Barbuda */
+			new Long(-40), /* Bahamas */
+			new Long(-28), /* Barbados */
+			new Long(-64), /* Dominica */
+			new Long(-96), /* Guyana */
+			new Long(-109), /* Jamaica */
+			new Long(-117), /* Saint Kitts and Nevis */
+			new Long(-227), /* Saint Vincent and The Grenadines */
+			new Long(-216), /* Trinidad and Tobago */
+			new Long(-44), /* Belize */
+			new Long(-82), /* Grenada */
+			new Long(-125), /* Saint Lucia */
+			new Long(-37), /* Brunei */
+			new Long(-185), /* Saudi Arabia */
+			new Long(-203), /* Swaziland */
+			new Long(-226), /* Vatican City */
+			new Long(-11), /* Andorra */
+			new Long(-33), /* Bahrain */
+			new Long(-30), /* Belgium */
+			new Long(-41), /* Bhutan */
+			new Long(-114), /* Cambodia */
+			new Long(-110), /* Jordan */
+			new Long(-120), /* Kuwait */
+			new Long(-129), /* Lesothol */
+			new Long(-152), /* Malaysia */
+			new Long(-135), /* Monaco */
+			new Long(-134), /* Morocco */
+			new Long(-166), /* Oman */
+			new Long(-180), /* Qatar */
+			new Long(-72), /* Spain */
+			new Long(-208), /* Thailand */
+			new Long(-12), /* United Arab Emirates */
+			new Long(-85), /* Ghana */
+			new Long(-1520), /* Ashanti */
+			new Long(-2608), /* Johor */
+			new Long(-2609), /* Kedah */
+			new Long(-2610), /* Kelantan */
+			new Long(-2614), /* Negeri Sembilan */
+			new Long(-2615), /* Pahang */
+			new Long(-2616), /* Perak */
+			new Long(-2617), /* Perlis */
+			new Long(-2621), /* Selangor */
+			new Long(-2622), /* Terengganu */
+			new Long(-158), /* Nigeria */
+			new Long(350586393), /* Ibadan */
+			new Long(-2677), /* Kano */
+			new Long(-2691), /* Sokoto */
+			new Long(-2682), /* Lagos */
+			new Long(-221), /* Uganda */
+			new Long(-265), /* Abu Dhabi */
+			new Long(-267), /* Dubai */
+			new Long(-270), /* Sharjah */
+			new Long(-3891), /* Wallis and Futuna */
+			new Long(-3895), /* Sigave */
+			new Long(-246), /* Zambia */
+			new Long(-245), /* South Africa */
+			new Long(-29), /* Bangladesh */
+			new Long(-42), /* Botswana */
+			new Long(-53), /* Cameroon */
+			new Long(-59), /* Cyprus */
+			new Long(-88), /* Gambia */
+			new Long(-104), /* India */
+			new Long(-112), /* Kenya */
+			new Long(-150), /* Malawi */
+			new Long(-149), /* Maldives */
+			new Long(-148), /* Mauritius */
+			new Long(-153), /* Mozambique */
+			new Long(-154), /* Namibia */
+			new Long(-172), /* Pakistan */
+			new Long(-187), /* Seychelles */
+			new Long(-195), /* Sierra Leone */
+			new Long(-127), /* Sri Lanka */
+			new Long(-219), /* Tanzania */
+			});
+		}
+		else if (index == 1)
+		{
+			correctResultSet = new ScanResultSet("canada", new Long[] { new Long(-3795), /*
+																						 * Rhode
+																						 * Island
+																						 */
+			new Long(-433), /* Queensland */
+			new Long(-159), /* Nicaragua */
+			new Long(-13), /* Afghanistan */
+			new Long(-3785), /* New Hampshire */
+			new Long(-3774), /* Louisiana */
+			new Long(-121), /* Cayman Islands */
+			new Long(498353663), /* Victoria */
+			new Long(-3780), /* Mississippi */
+			new Long(-96), /* Guyana */
+			new Long(-115), /* Kiribati */
+			new Long(-123), /* Laos */
+			new Long(-90), /* Guadeloupe */
+			new Long(-201), /* El Salvador */
+			new Long(-126), /* Liechtenstein */
+			new Long(474823250), /* Rome */
+			new Long(-174), /* Saint Pierre and Miquelon */
+			new Long(-47), /* Central African Republic */
+			new Long(-838), /* British Columbia */
+			new Long(490475686), /* Charlottetown */
+			new Long(487450810), /* Sherbrooke */
+			new Long(511747334), /* Saskatoon */
+			new Long(-218), /* Taiwan */
+			new Long(-149), /* Maldives */
+			new Long(-220), /* Ukraine */
+			new Long(503576283), /* Winnipeg */
+			new Long(-3765), /* Florida */
+			new Long(-837), /* Alberta */
+			new Long(-108), /* Italy */
+			new Long(-136), /* Moldova */
+			new Long(-3901), /* Christmas Island */
+			new Long(-59), /* Cyprus */
+			new Long(-233), /* Wallis and Futuna */
+			new Long(480465950), /* Kitchener */
+			new Long(-51), /* Cook Islands */
+			new Long(-849), /* Yukon */
+			new Long(-91), /* Equatorial Guinea */
+			new Long(-6751), /* Paris */
+			new Long(-98), /* Croatia */
+			new Long(-848), /* Saskatchewan */
+			new Long(476289697), /* Windsor */
+			new Long(-435), /* Tasmania */
+			new Long(-189), /* Sweden */
+			new Long(-144), /* Martinique */
+			new Long(-204), /* Turks and Caicos Islands */
+			new Long(-2614), /* Negeri Sembilan */
+			new Long(-844), /* Nunavut */
+			new Long(-168), /* Peru */
+			new Long(-88), /* Gambia */
+			new Long(-3767), /* Hawaii */
+			new Long(-187), /* Seychelles */
+			new Long(-221), /* Uganda */
+			new Long(-207), /* Togo */
+			new Long(-188), /* Sudan */
+			new Long(-40), /* Bahamas */
+			new Long(-216), /* Trinidad and Tobago */
+			new Long(-246), /* Zambia */
+			new Long(-65), /* Dominican Republic */
+			new Long(-48), /* Congo */
+			new Long(-177), /* Portugal */
+			new Long(-999), /* Hong Kong */
+			new Long(484823639), /* Halifax */
+			new Long(-75), /* Fiji Islands */
+			new Long(-164), /* Niue */
+			new Long(520998673), /* Athabasca */
+			new Long(-234), /* Samoa */
+			new Long(-215), /* Turkey */
+			new Long(-3900), /* Ashmore and Cartier */
+			new Long(472726708), /* New Haven */
+			new Long(505627539), /* Regina */
+			new Long(-205), /* Chad */
+			new Long(-182), /* Romania */
+			new Long(-84), /* French Guiana */
+			new Long(-137), /* Madagascar */
+			new Long(-116), /* Comoros */
+			new Long(-185), /* Saudi Arabia */
+			new Long(-102), /* Ireland */
+			new Long(-1495), /* England */
+			new Long(481150062), /* Toronto */
+			new Long(-152), /* Malaysia */
+			new Long(-25), /* Aruba */
+			new Long(-245), /* South Africa */
+			new Long(489826804), /* Victoriaville */
+			new Long(-135), /* Monaco */
+			new Long(-145), /* Mauritania */
+			new Long(-3762), /* Connecticut */
+			new Long(-2620), /* Sarawak */
+			new Long(-840), /* New Brunswick */
+			new Long(-20), /* Antarctica */
+			new Long(-173), /* Poland */
+			new Long(-3757), /* Alaska */
+			new Long(-32), /* Bulgaria */
+			new Long(-83), /* Georgia */
+			new Long(-61), /* Germany */
+			new Long(-161), /* Norway */
+			new Long(-57), /* Cuba */
+			new Long(-56), /* Costa Rica */
+			new Long(-104), /* India */
+			new Long(-594), /* Luxemburg */
+			new Long(-197), /* Senegal */
+			new Long(-3777), /* Massachusetts */
+			new Long(-2621), /* Selangor */
+			new Long(-3776), /* Maryland */
+			new Long(-154), /* Namibia */
+			new Long(-846), /* Prince Edward Island */
+			new Long(-845), /* Ontario */
+			new Long(-125), /* Saint Lucia */
+			new Long(-3788), /* New York */
+			new Long(477873552), /* East Lansing */
+			new Long(-3794), /* Pennsylvania */
+			new Long(-3840), /* Grenadines */
+			new Long(-217), /* Tuvalu */
+			new Long(-72), /* Spain */
+			new Long(-437), /* Western Australia */
+			new Long(523014522), /* Slave Lake */
+			new Long(-97), /* Honduras */
+			new Long(-2612), /* Labuan */
+			new Long(-76), /* Falkland Islands */
+			new Long(-109), /* Jamaica */
+			new Long(-2622), /* Terengganu */
+			new Long(-42), /* Botswana */
+			new Long(-171), /* Philippines */
+			new Long(-134), /* Morocco */
+			new Long(-99), /* Haiti */
+			new Long(-79), /* France */
+			new Long(-175), /* Puerto Rico */
+			new Long(-2615), /* Pahang */
+			new Long(-58), /* Cape Verde */
+			new Long(-132), /* Latvia */
+			new Long(-167), /* Panama */
+			new Long(-34), /* Burundi */
+			new Long(-2609), /* Kedah */
+			new Long(504473703), /* Whistler */
+			new Long(-227), /* Saint Vincent and The Grenadines */
+			new Long(-151), /* Mexico */
+			new Long(479746015), /* Hamilton */
+			new Long(-138), /* Marshall Islands */
+			new Long(-107), /* Iceland */
+			new Long(-124), /* Lebanon */
+			new Long(-129), /* Lesotho */
+			new Long(-157), /* Norfolk Island */
+			new Long(479134098), /* Niagara Falls */
+			new Long(-229), /* British Virgin Islands */
+			new Long(-192), /* Slovenia */
+			new Long(-9192), /* Washington */
+			new Long(-146), /* Montserrat */
+			new Long(-31), /* Burkina Faso */
+			new Long(-69), /* Egypt */
+			new Long(501413687), /* Vancouver */
+			new Long(-183), /* Russia */
+			new Long(-165), /* New Zealand */
+			new Long(-194), /* Slovakia */
+			new Long(477765545), /* Lansing */
+			new Long(-92), /* Greece */
+			new Long(-208), /* Thailand */
+			new Long(-213), /* Tonga */
+			new Long(485470468), /* Iroquois */
+			new Long(-77), /* Micronesia */
+			new Long(-63), /* Denmark */
+			new Long(-120), /* Kuwait */
+			new Long(-226), /* Vatican City */
+			new Long(-172), /* Pakistan */
+			new Long(-3802), /* Virginia */
+			new Long(-2616), /* Perak */
+			new Long(-842), /* Northwest Territories */
+			new Long(482014114), /* Oshawa */
+			new Long(-53), /* Cameroon */
+			new Long(-3778), /* Michigan */
+			new Long(-3786), /* New Jersey */
+			new Long(-247), /* Zimbabwe */
+			new Long(-244), /* Serbia */
+			new Long(-68), /* Estonia */
+			new Long(488639613), /* Oxford */
+			new Long(-186), /* Solomon Islands */
+			new Long(-112), /* Kenya */
+			new Long(-150), /* Malawi */
+			new Long(-212), /* Tunisia */
+			new Long(-54), /* China */
+			new Long(-1520), /* Ashanti */
+			new Long(523157692), /* Mackenzie */
+			new Long(-3875), /* Saint Croix */
+			new Long(-114), /* Cambodia */
+			new Long(487882643), /* Montreal */
+			new Long(-3789), /* North Carolina */
+			new Long(-101), /* Indonesia */
+			new Long(-2610), /* Kelantan */
+			new Long(-52), /* Chile */
+			new Long(-33), /* Bahrain */
+			new Long(483490166), /* Peterborough */
+			new Long(-176), /* Palestine */
+			new Long(-166), /* Oman */
+			new Long(-839), /* Manitoba */
+			new Long(492022827), /* Donnacona */
+			new Long(-156), /* Niger */
+			new Long(-18), /* Netherlands Antilles */
+			new Long(-3903), /* Coral Sea Islands */
+			new Long(-60), /* Czech Republic */
+			new Long(-843), /* Nova Scotia */
+			new Long(-3753), /* Navassa Island */
+			new Long(-847), /* Quebec */
+			new Long(-841), /* Newfoundland and Labrador */
+			new Long(-49), /* Switzerland */
+			new Long(478701876), /* London */
+			new Long(-3793), /* Oregon */
+			new Long(516858646), /* Edmonton */
+			new Long(-23), /* Austria */
+			new Long(-1376), /* South Sandwich Islands */
+			new Long(-100), /* Hungary */
+			new Long(-36), /* Bermuda */
+			new Long(-148), /* Mauritius */
+			new Long(-21), /* Argentina */
+			new Long(-210), /* Tokelau */
+			new Long(511567873), /* Swan River */
+			new Long(-111), /* Japan */
+			new Long(-12), /* United Arab Emirates */
+			new Long(-431), /* New South Wales */
+			new Long(-3796), /* South Carolina */
+			new Long(-127), /* Sri Lanka */
+			new Long(-3902), /* Cocos Islands */
+			new Long(-87), /* Greenland */
+			new Long(-139), /* Macedonia */
+			new Long(-110), /* Jordan */
+			new Long(-3791), /* Ohio */
+			new Long(507786594), /* Calgary */
+			new Long(-117), /* Saint Kitts and Nevis */
+			new Long(-184), /* Rwanda */
+			new Long(-203), /* Swaziland */
+			new Long(-238), /* Jersey */
+			new Long(487522429), /* Ottawa */
+			new Long(-434), /* South Australia */
+			new Long(-180), /* Qatar */
+			new Long(458717784), /* Stanford */
+			new Long(-178), /* Palau */
+			new Long(-41), /* Bhutan */
+			new Long(-231), /* Vietnam */
+			new Long(-24), /* Australia */
+			new Long(-160), /* Netherlands */
+			new Long(-86), /* Gibraltar */
+			new Long(-3763), /* Delaware */
+			new Long(-239), /* Isle of Man */
+			new Long(-130), /* Lithuania */
+			new Long(-199), /* Suriname */
+			new Long(514267762), /* Hudson Bay */
+			new Long(-82), /* Grenada */
+			new Long(-190), /* Singapore */
+			new Long(-95), /* Guinea-Bissau */
+			});
+		}
+		else if (index == 2)
+		{
+			correctResultSet = new ScanResultSet("ireland", new Long[] { new Long(-1395), /* Asturias */
+			new Long(512693027), /* Causeway */
+			new Long(510137147), /* Kinsale */
+			new Long(516005375), /* Dublin */
+			new Long(515609332), /* Naas */
+			new Long(-1496), /* Northern Ireland */
+			new Long(518489358), /* Dundalk */
+			new Long(474823250), /* Rome */
+			new Long(512441352), /* Wexford */
+			new Long(513737108), /* Shannon */
+			// new Long(521009377), /* Antrim */
+			new Long(514925071), /* Lisdoonvarna */
+			new Long(-108), /* Italy */
+			new Long(517121180), /* Roscommon */
+			new Long(514097385), /* Arklow */
+			new Long(519065365), /* Newry */
+			new Long(-1497), /* Scotland */
+			new Long(518561381), /* Carlingford */
+			// new Long(519677333), /* Armagh */
+			new Long(-1298), /* Galicia */
+			new Long(514781395), /* Wicklow */
+			new Long(522809335), /* Portrush */
+			new Long(512477258), /* Carrick-on-Suir */
+			new Long(-999), /* Hong Kong */
+			new Long(517445219), /* Longford */
+			new Long(-234), /* Samoa */
+			new Long(514853389), /* Ashford */
+			new Long(-102), /* Ireland */
+			new Long(517625108), /* Knock */
+			new Long(-1399), /* Barcelona */
+			new Long(-1495), /* England */
+			new Long(515825094), /* Galway */
+			new Long(202101121), /* Sydney */
+			new Long(512153287), /* Waterford */
+			new Long(515393301), /* Kildare */
+			new Long(-104), /* India */
+			new Long(516725266), /* Mullingar */
+			new Long(512981183), /* Tipperary */
+			new Long(514745251), /* Castletown */
+			new Long(510713170), /* Cobh */
+			new Long(-1498), /* Wales */
+			new Long(-3788), /* New York */
+			new Long(519389141), /* Strandhill */
+			new Long(517193331), /* Navan */
+			new Long(-72), /* Spain */
+			new Long(514241308), /* Carlow */
+			new Long(520145171), /* Bundoran */
+			new Long(513593274), /* Kilkenny */
+			new Long(520793188), /* Donegal */
+			new Long(519317303), /* Monaghan */
+			new Long(478665434), /* Grand Rapids */
+			new Long(519425152), /* Sligo */
+			new Long(-81), /* United Kingdom */
+			new Long(512045133), /* Buttevant */
+			new Long(514601065), /* Lahinch */
+			new Long(-79), /* France */
+			new Long(-45), /* Canada */
+			new Long(512513229), /* Clonmel */
+			// new Long(520577407), /* Belfast */
+			new Long(522017267), /* Londonderry */
+			new Long(517409364), /* Drogheda */
+			new Long(513017343), /* Enniscorthy */
+			new Long(511037215), /* Youghal */
+			new Long(-165), /* New Zealand */
+			new Long(518237346), /* Louth */
+			new Long(-3802), /* Virginia */
+			new Long(527417729), /* Carnoustie */
+			new Long(-3786), /* New Jersey */
+			new Long(-247), /* Zimbabwe */
+			new Long(516545775), /* Manchester */
+			new Long(515897124), /* Athenry */
+			new Long(510353874), /* Oxford */
+			new Long(514061177), /* Silvermines */
+			new Long(470674597), /* Hoboken */
+			new Long(513089211), /* Cashel */
+			new Long(520793432), /* Bangor */
+			new Long(512657305), /* New Ross */
+			new Long(509993050), /* Bantry */
+			new Long(-101), /* Indonesia */
+			new Long(513701217), /* Thurles */
+			// new Long(520289394), /* Lisburn */
+			new Long(511001143), /* Blarney */
+			new Long(511684975), /* Dingle */
+			new Long(-3139), /* Moscow */
+			new Long(516797320), /* Trim */
+			new Long(-6055), /* London */
+			new Long(-6030), /* Cornwall */
+			new Long(-111), /* Japan */
+			// new Long(521477418), /* Larne */
+			new Long(-87), /* Greenland */
+			new Long(510929183), /* Midleton */
+			new Long(510857153), /* Cork */
+			new Long(-228), /* Venezuela */
+			new Long(518417263), /* Cavan */
+			new Long(513629137), /* Limerick */
+			new Long(514349180), /* Nenagh */
+			new Long(-24), /* Australia */
+			new Long(-256907), /* Killarney */
+			new Long(-239), /* Isle of Man */
+			new Long(497323158), /* Munich */
+			// new Long(522485331), /* Coleraine */
+			});
+		}
+		else if (index == 3)
+		{
+			correctResultSet = new ScanResultSet("southafrica", new Long[] { new Long(-245), /*
+																							 * South
+																							 * Africa
+																							 */
+			new Long(231392822), /* Pretoria */
+			new Long(219080623), /* Bloemfontein */
+			new Long(201871846), /* Cape Town */
+			new Long(229736804), /* Johannesburg */
+			new Long(-81), /* United Kingdom */
+			new Long(-154), /* Namibia */
+			new Long(-42), /* Botswana */
+			new Long(-153), /* Mozambique */
+			new Long(-203), /* Swaziland */
+			new Long(-129), /* Lesotho */
+			new Long(-3991), /* KwaZulu Natal */
+			new Long(-3988), /* Eastern Cape */
+			new Long(-101), /* Indonesia */
+			new Long(-137), /* Madagascar */
+			new Long(-24), /* Australia */
+			new Long(-101), /* Indonesia */
+			new Long(-104), /* India */
+			new Long(-3990), /* Gauteng */
+			new Long(-3992), /* Limpopo */
+			new Long(-3993), /* Mpumalanga */
+			new Long(-3995), /* North West */
+			new Long(-34), /* Burundi */
+			new Long(-116), /* Comoros */
+			new Long(-247), /* Zimbabwe */
+			new Long(-3994), /* Northern Cape */
+			new Long(-3989), /* Free State */
+			new Long(-3996), /* Western Cape */
+			new Long(205688739), /* Bhisho */
+			new Long(217425039), /* Pietermaritzburg */
+			new Long(238016945), /* Polokwane */
+			new Long(232329098), /* Nelspruit */
+			new Long(220520477), /* Kimberley */
+			new Long(238016945), /* Polokwane */
+			new Long(231032561), /* Mmabatho */
+			new Long(-55), /* Colombia */
+			new Long(-78614), /* Sutherland */
+			new Long(221564123), /* Upington */
+			new Long(203132219), /* Oudtshoorn */
+			new Long(-39), /* Brazil */
+			new Long(-54), /* China */
+			new Long(-151), /* Mexico */
+			new Long(201764559), /* Port Elizabeth */
+			new Long(-133), /* Libya */
+			new Long(216489099), /* Durban */
+			new Long(-148), /* Mauritius */
+			new Long(-150), /* Malawi */
+			new Long(-61), /* Germany */
+			new Long(-111), /* Japan */
+			new Long(-72), /* Spain */
+			new Long(201907886), /* Stellenbosch */
+			new Long(202699896), /* Paarl */
+			new Long(-79), /* France */
+			new Long(445545558), /* Atlanta */
+			new Long(-14), /* Antigua and Barbuda */
+			new Long(-40), /* Bahamas */
+			new Long(-28), /* Barbados */
+			new Long(-64), /* Dominica */
+			new Long(-96), /* Guyana */
+			new Long(-109), /* Jamaica */
+			new Long(-117), /* Saint Kitts and Nevis */
+			new Long(-227), /* Saint Vincent and The Grenadines */
+			new Long(-216), /* Trinidad and Tobago */
+			new Long(-44), /* Belize */
+			new Long(-82), /* Grenada */
+			new Long(-125), /* Saint Lucia */
+			new Long(-37), /* Brunei */
+			new Long(-185), /* Saudi Arabia */
+			new Long(-203), /* Swaziland */
+			new Long(-33), /* Bahrain */
+			new Long(-30), /* Belgium */
+			new Long(-120), /* Kuwait */
+			new Long(-129), /* Lesothol */
+			new Long(-152), /* Malaysia */
+			new Long(-134), /* Morocco */
+			new Long(-166), /* Oman */
+			new Long(-180), /* Qatar */
+			new Long(-72), /* Spain */
+			new Long(-208), /* Thailand */
+			new Long(-12), /* United Arab Emirates */
+			new Long(-85), /* Ghana */
+			new Long(-158), /* Nigeria */
+			new Long(-221), /* Uganda */
+			new Long(-246), /* Zambia */
+			});
+		}
+		else if (index == 4)
+		{
+			correctResultSet = new ScanResultSet("washington", new Long[] { new Long(-3795), /*
+																							 * Rhode
+																							 * Island
+																							 */
+			new Long(498317769), /* Mount Vernon */
+			new Long(-3750), /* Johnston Atoll */
+			new Long(-3785), /* New Hampshire */
+			new Long(-3774), /* Louisiana */
+			new Long(-3780), /* Mississippi */
+			new Long(496193763), /* Edmonds */
+			new Long(498605777), /* Sedro-Woolley */
+			new Long(493781747), /* Lakewood */
+			new Long(491729947), /* Yakima */
+			new Long(492197704), /* Centralia */
+			new Long(-838), /* British Columbia */
+			new Long(495113796), /* Issaquah */
+			new Long(493349711), /* Olympia */
+			new Long(493205945), /* Ellensburg */
+			new Long(-3797), /* South Dakota */
+			new Long(494970242), /* Cheney */
+			new Long(493961689), /* Shelton */
+			new Long(-3765), /* Florida */
+			new Long(-3756), /* Alabama */
+			new Long(494321766), /* Federal Way */
+			new Long(496661780), /* Everett */
+			new Long(-3768), /* Idaho */
+			new Long(-3748), /* Howland Island */
+			new Long(495618259), /* Spokane */
+			new Long(496049769), /* Mountlake Terrace */
+			new Long(-849), /* Yukon */
+			new Long(495257777), /* Mercer Island */
+			new Long(489858166), /* Walla Walla */
+			new Long(-3747), /* Baker Island */
+			new Long(-3771), /* Iowa */
+			new Long(-3790), /* North Dakota */
+			new Long(-3770), /* Indiana */
+			new Long(-3779), /* Minnesota */
+			new Long(490362023), /* Prosser */
+			new Long(-3767), /* Hawaii */
+			new Long(-3782), /* Montana */
+			new Long(495581748), /* Bainbridge Island */
+			new Long(495618276), /* Spokane Valley */
+			new Long(496229998), /* Chelan */
+			new Long(490830295), /* Asotin */
+			new Long(-3787), /* New Mexico */
+			new Long(488957885), /* Klickitat */
+			new Long(494717971), /* East Wenatchee */
+			new Long(-3760), /* California */
+			new Long(493205708), /* Tumwater */
+			new Long(496949759), /* Langley */
+			new Long(490326083), /* Kennewick */
+			new Long(492809731), /* Rainier */
+			new Long(497849738), /* Oak Harbor */
+			new Long(-3755), /* Wake */
+			new Long(494645769), /* Des Moines */
+			new Long(496698102), /* Coulee Dam */
+			new Long(495365784), /* Bellevue */
+			new Long(498569737), /* Anacortes */
+			new Long(496590100), /* Grand Coulee */
+			new Long(490865595), /* Long Beach */
+			new Long(490757999), /* Sunnyside */
+			new Long(-3762), /* Connecticut */
+			new Long(494537790), /* Covington */
+			new Long(-3758), /* Arizona */
+			new Long(-3757), /* Alaska */
+			new Long(-3783), /* Nebraska */
+			new Long(495653781), /* Kirkland */
+			new Long(-3777), /* Massachusetts */
+			new Long(494933780), /* Renton */
+			new Long(-3776), /* Maryland */
+			new Long(496193770), /* Lynnwood */
+			new Long(-3801), /* Vermont */
+			new Long(496553769), /* Mukilteo */
+			new Long(-3788), /* New York */
+			new Long(495185730), /* Bremerton */
+			new Long(490650065), /* West Richland */
+			new Long(494285779), /* Auburn */
+			new Long(-3761), /* Colorado */
+			new Long(-3794), /* Pennsylvania */
+			new Long(491045974), /* Zillah */
+			new Long(488813747), /* Battle Ground */
+			new Long(-3784), /* Nevada */
+			new Long(490506087), /* Pasco */
+			new Long(494537796), /* Maple Valley */
+			new Long(495905764), /* Shoreline */
+			new Long(498786210), /* Colville */
+			new Long(495941776), /* Kenmore */
+			new Long(453969645), /* New Market */
+			new Long(493853772), /* Puyallup */
+			new Long(494933773), /* Tukwila */
+			new Long(493133958), /* Kittitas */
+			new Long(-3798), /* Tennessee */
+			new Long(487949734), /* Portland */
+			new Long(-151), /* Mexico */
+			new Long(-45), /* Canada */
+			new Long(-3775), /* Maine */
+			new Long(494789770), /* SeaTac */
+			new Long(495005821), /* North Bend */
+			new Long(495293790), /* Sammamish */
+			new Long(-3805), /* Wisconsin */
+			new Long(-9192), /* Washington */
+			new Long(496301780), /* Mill Creek */
+			new Long(493925801), /* Enumclaw */
+			new Long(490073711), /* Kelso */
+			new Long(488273736), /* Vancouver */
+			new Long(-183), /* Russia */
+			new Long(495941780), /* Bothell */
+			new Long(499469753), /* Bellingham */
+			new Long(-3802), /* Virginia */
+			new Long(497021785), /* Marysville */
+			new Long(-3799), /* Texas */
+			new Long(494105754), /* Tacoma */
+			new Long(-3778), /* Michigan */
+			new Long(-3786), /* New Jersey */
+			new Long(-3759), /* Arkansas */
+			new Long(493925906), /* Cle Elum */
+			new Long(495149736), /* Port Orchard */
+			new Long(493601742), /* Fort Lewis */
+			new Long(494861765), /* Burien */
+			new Long(493638071), /* Moses Lake */
+			new Long(497525788), /* Arlington */
+			new Long(496517791), /* Snohomish */
+			new Long(493242286), /* Garfield */
+			new Long(-8340), /* District of Columbia */
+			new Long(-3749), /* Jarvis Island */
+			new Long(-3789), /* North Carolina */
+			new Long(497489654), /* Port Angeles */
+			new Long(-101), /* Indonesia */
+			new Long(490650071), /* Richland */
+			new Long(495617789), /* Redmond */
+			new Long(-3806), /* Wyoming */
+			new Long(-3772), /* Kansas */
+			new Long(498102042), /* Okanogan */
+			new Long(-3753), /* Navassa Island */
+			new Long(493133619), /* Aberdeen */
+			new Long(-3781), /* Missouri */
+			new Long(495905789), /* Woodinville */
+			new Long(493997746), /* University Place */
+			new Long(-3793), /* Oregon */
+			new Long(-3769), /* Illinois */
+			new Long(495365934), /* Leavenworth */
+			new Long(494753968), /* Wenatchee */
+			new Long(-3773), /* Kentucky */
+			new Long(-3800), /* Utah */
+			new Long(-3796), /* South Carolina */
+			new Long(488093765), /* Washougal */
+			new Long(494609776), /* Kent */
+			new Long(495437765), /* Seattle */
+			new Long(496301801), /* Monroe */
+			new Long(-3791), /* Ohio */
+			new Long(-3804), /* West Virginia */
+			new Long(-3751), /* Kingman Reef */
+			new Long(490145705), /* Longview */
+			new Long(490506009), /* Grandview */
+			new Long(492234284), /* Pullman */
+			new Long(500225754), /* Lynden */
+			new Long(493889783), /* Bonney Lake */
+			new Long(495941771), /* Lake Forest Park */
+			new Long(-3763), /* Delaware */
+			new Long(488129758), /* Camas */
+			new Long(493313719), /* Lacey */
+			new Long(-3792), /* Oklahoma */
+			});
+		}
+		return correctResultSet;
+	}
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/StringLengthComparator.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/StringLengthComparator.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/StringLengthComparator.java	(revision 23906)
@@ -0,0 +1,25 @@
+package org.greenstone.server;
+
+
+import java.util.Comparator;
+
+public class StringLengthComparator implements Comparator<String> 
+{
+	@Override
+	public int compare(String firstString, String secondString) 
+	{
+		if(firstString.length() < secondString.length())
+		{
+			return 1;
+		}
+		else if (firstString.length() == secondString.length())
+		{
+			return 0;
+		}
+		else
+		{
+			return -1;
+		}
+	}
+
+}
Index: gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/Word.java
===================================================================
--- gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/Word.java	(revision 23906)
+++ gs3-extensions/atlas-src/trunk/src/org/greenstone/atlas/server/Word.java	(revision 23906)
@@ -0,0 +1,35 @@
+package org.greenstone.server;
+
+import java.util.ArrayList;
+
+public class Word 
+{
+	private int _classificationIndex = 0;
+	private ArrayList<String> _classifications = new ArrayList<String>();
+	
+	private String _value = null;
+	
+	public void setValue(String value)
+	{
+		_value = value;
+	}
+	
+	public void addClassification(String classification)
+	{
+		_classifications.add(classification);
+	}
+	
+	public String getValue()
+	{
+		return _value;
+	}
+	
+	public String getNextClassification()
+	{
+		if(_classificationIndex >= _classifications.size())
+		{
+			return null;
+		}
+		return _classifications.get(_classificationIndex++);
+	}
+}
