package uk.ac.leeds.ccg.geotools;
import java.util.*;
import java.io.*;
/**
* A concrete implementation of the GeoData interface, it is suitable for most
* uses and may be extended for more specialist GeoData implementations.
*
* @author James Macgill JM
*/
public class SimpleGeoData implements GeoData,Serializable{
/**
* Stores the name of this GeoData, returned by getName.
*/
private String name="Unknown";
private int type = GeoData.FLOATING;
double min=Double.POSITIVE_INFINITY,max=Double.NEGATIVE_INFINITY;
Hashtable data;
double missingValue = -99999;
int missingCount=0;
/**
* the default constructor for SimpleGeoData
* @author James Macgill JM
*/
public SimpleGeoData(){
data=new Hashtable();
}
/**
* Constructs a SimpleGeoData by pulling the values from a Hashtable.
* The hashtable should be built with Integer objects holding ids as keys and
* Double objects or strings for the values.
*
* @author James Macgill JM
* @param h A hashtable containg the id keys and values for this SimpleGeoData object
*/
public SimpleGeoData(Hashtable h){
data = h;
doMinMax();
}
/**
* Updates the values for min and max that are returned by getMin and getMax.
* This method iterates through all of the data stored to find the current Min and Max values.
*
**/
public void doMinMax(){
min=Double.MAX_VALUE;
max=Double.MIN_VALUE;
missingCount=0;
for (Enumeration e = data.elements() ; e.hasMoreElements() ;) {
Object obj = e.nextElement();
if(obj instanceof Double){
double value = ((Double)obj).doubleValue();
if(value!=missingValue){
min=Math.min(value,min);
max=Math.max(value,max);
}
else{
missingCount++;
}
}
}
}
/**
* Not all posible ids will have a value stored in the GeoData object, so when a call is made to getValue with an id that is
* not stored a special value is returned to signify that a value for this id is missing.
* By default that value is set to MISSING, however this behavoir can be changed by calling this method with a new value.
*
* @see #getMissingValueCode
* @author James Macgill JM
* @param mv A double containing the new value to represent missing data.
*/
public void setMissingValueCode(double mv){
missingValue = mv;
doMinMax();
}
/**
* Not all posible ids will have a value stored in the GeoData object, so when a call is made to getValue with an id that is
* not stored a special value is returned to signify that a value for this id is missing.
* A call to this method will return the current code in use to represent that situation.
*
* @see #setMissingValueCode
* @author James Macgill JM
* @return double The current value representing missing data.
*/
public double getMissingValueCode(){
return missingValue;
}
/**
* Stores the id/value pair into this GeoData.
* If the id specifed already has a value associated with it then the old value is returned.
* If the id is a new one then the returned value will match the current missingValueCode
*
* @author James Macgill JM
* @param id An int holding the id to set the value for.
* @param value The value to associate with the specifed id.
* @return The old value associated with id, or missingValueCode if the id was previusly not set.
*/
public double setValue(int id,double value){
if(value!=missingValue){
min=Math.min(value,min);
max=Math.max(value,max);
}
else{
missingCount++;
}
Double old = (Double)data.put(new Integer(id),new Double(value));
if (old!=null){
if(old.doubleValue()==missingValue){missingCount--;}
return old.doubleValue();
}
return missingValue;
}
/**
* Returns the number of values stored that equal the current missing value code.
*/
public int getMissingCount(){
return missingCount;
}
/**
* Stores the id/text pair into this GeoData.
* If the id specifed already has text associated with it then the old text is returned.
* If the id is a new one then the returned String will be null
*
* @author James Macgill JM
* @param id An int holding the id to set the value for.
* @param text The String to associate with the specifed id.
* @return The old text associated with id, or null if the id was previusly not set.
*/
public String setText(int id,String text){
setDataType(GeoData.CHARACTER);
String old = (String)data.put(new Integer(id),text);
return old;
}
/**
* All GeoData objects can have a name associated with them.
* Typicaly the name will match the Column heading from which the data came from.
* Names can be important when the GeoData is used in thematic maps as this is the
* name that will be placed in the key by default.
*
* @author James Macgill JM
* @param name_ The name to be associated with this GeoData.
*/
public void setName(String name_) {
name = name_;
}
/**
* All GeoData objects can have a name associated with them.
* Typicaly the name will match the Column heading from which the data came from.
* Names can be important when the GeoData is used in thematic maps as this is the
* name that will be placed in the key by default.
* @author James Macgill JM
* @return String The name associated with this GeoData.
*/
public String getName(){
return name;
}
/**
* look up a value for the given id
* if the GeoData object is completly empty with no values set at all
* then it bounces the id back to the caller.
* this is an experimental new behavior for getValue
*/
public double getValue(int id){
if(data.isEmpty()){return id;}
try{
Double d = (Double)data.get(new Integer(id));
if (d!=null){
return d.doubleValue();
}
}
catch(NumberFormatException e){
return missingValue;
}
return missingValue;
}
/**
* A quick statistic relating to the values stored in the GeoData object.
*
* @author James Macgill JM
* @return double The smallest value currently stored in this GeoData. The missingValueCode is not included in this test.
*/
public double getMin(){
return min;
}
/**
* A quick statistic relating to the values stored in the GeoData object.
*
* @author James Macgill JM
* @return double The largest value currently stored in this GeoData. The missingValueCode is not included in this test.
*/
public double getMax(){
return max;
}
/**
* The total number of stored id/value pairs stored in this GeoData.
* @author James Macgill JM
* @return int The number of values stored in this GeoData.
*/
public int getSize(){
return data.size();
}
/**
* Looks up and retreves a string for the specifed feature id.
* Used for example by the ToolTip feature in Themes to provide tool tip text for each feature.
*
* @author James Macgill JM
* @param id An int specifying the feature to retreve the text for.
* @return String A piece of text for the chosen feature id. If no id matches then an empty string should be returned " "
*/
public String getText(int id){
Object o = data.get(new Integer(id));
if (o!=null){
return o.toString();
}
return (" ");//if there is no space this returns a null
}
/**
* In order to allow systems to iterate through all of the data contained within the GeoData object this
* method provides a list of all of the IDs which have associated values stored.
*
* @author James Macgill JM
* @return Enumeration An enumeration of all of the IDs which can then be iterated through.
*/
public Enumeration getIds(){
return data.keys();
}
/**
* Returns the name of the GeoData object. This can be handy when you want to include
* GeoData objets in lists.
* @return Strin the name of this GeoData
*/
public String toString(){
return name;
}
public int getDataType(){
return type;
}
public void setDataType(int t){
type=t;
}
public void clear(){
data = new Hashtable();
doMinMax();
}
}