Skip to content
Snippets Groups Projects
LandUseItem.java 14.3 KiB
Newer Older
Peter Alexander's avatar
Peter Alexander committed
package ac.ed.lurg.landuse;

import java.io.Serializable;
import java.util.Collection;
Peter Alexander's avatar
Peter Alexander committed
import java.util.HashMap;
import java.util.Map;

import ac.ed.lurg.ModelConfig;
Peter Alexander's avatar
Peter Alexander committed
import ac.ed.lurg.types.CropToDouble;
Peter Alexander's avatar
Peter Alexander committed
import ac.ed.lurg.types.CropType;
import ac.ed.lurg.types.LandCoverType;
import ac.ed.lurg.utils.Interpolator;
import ac.sac.raster.InterpolatingRasterItem;
Peter Alexander's avatar
Peter Alexander committed

public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serializable {
	private static final long serialVersionUID = 763526147224355158L;
	
	private Map<CropType, Intensity> intensityMap = new HashMap<CropType, Intensity>();
	private Map<CropType, Double> cropFractions = new HashMap<CropType, Double>();
	private Map<LandCoverType, Double> landCoverAreas = new HashMap<LandCoverType, Double>();
	private Map<LandCoverType, Double> unprotectedAreas = new HashMap<LandCoverType, Double>();
	private double protectedArea; //protected area in Mha
	private double unavailableArea; //area unavailable due to altitude etc 
	private double suitableArea;
Peter Alexander's avatar
Peter Alexander committed
	public LandUseItem() {}
	
	public LandUseItem(LandCoverItem landCover) {
		this();
		if (landCover != null) {
			for (LandCoverType lcType : LandCoverType.values())
				landCoverAreas.put(lcType, landCover.getLandCoverArea(lcType));
			
			setCropFraction(CropType.WHEAT, 0.5); // random start as don't have better data
			setCropFraction(CropType.MAIZE, 0.5);
			setUnavailableArea(landCover.getUnavailableFract());
			updateSuitableArea(ModelConfig.BASE_YEAR);
Peter Alexander's avatar
Peter Alexander committed
	public LandUseItem(LandUseItem luItemToCopy) {
		this();
		intensityMap.putAll(luItemToCopy.intensityMap);
		cropFractions.putAll(luItemToCopy.cropFractions);
		landCoverAreas.putAll(luItemToCopy.landCoverAreas);
		unprotectedAreas.putAll(luItemToCopy.unprotectedAreas);
Peter Alexander's avatar
Peter Alexander committed
		protectedArea = (luItemToCopy.protectedArea);
		suitableArea = (luItemToCopy.suitableArea);
	public void setProtectedFract(double protFrac) {
		if (!Double.isNaN(ModelConfig.CONSTANT_PROTECTED_AREA_RATE))
			protFrac=ModelConfig.CONSTANT_PROTECTED_AREA_RATE;
		
		double totalArea = getTotalLandCoverArea();
		double urbanArea = getLandCoverArea(LandCoverType.URBAN);
		double barrenArea = getLandCoverArea(LandCoverType.BARREN);
		
		this.protectedArea = Math.min(totalArea-urbanArea-barrenArea, protFrac*(totalArea-urbanArea));
		
	}

	public void setProtectedArea(double protectedArea) {
		this.protectedArea = protectedArea;
	public double getProtectedArea() {
		if(ModelConfig.PROTECTED_AREAS_ENABLED)
			return protectedArea;
		else 
			return 0.0;	
	}
	
	public void setSuitableArea(double suitable) {
		this.suitableArea = suitable;
	public double getSuitableArea() {
		return suitableArea;
	}
	
	
	public void setUnavailableArea(double unavailF) {
		unavailableArea = unavailF*getTotalLandCoverArea();
	}
	
	public double getUnavailableArea() {
		return unavailableArea;
	public Intensity getIntensity(CropType crop) {
		return intensityMap.get(crop);
	}
	
	public void setIntensity(CropType crop, Intensity intensityData) {
		intensityMap.put(crop, intensityData);
	}
	
	/** Fertiliser rate in kg/ha */
	public double getFertiliserRate(CropType crop) {
		Intensity i = getIntensity(crop);
		return (i == null) ? 0 : i.getFertiliserAmount();
	}
	
	/** Fertiliser amount in kt (1000 tonnes), for this location */
	public double getFertiliserAmount(CropType c) {
		double rate = getFertiliserRate(c);
		double area = getCropArea(c);
		return rate * area;
	}
	
	public double getFertiliserAverageRate(CropType... crops) {
		double fertTotal = 0;
		double areaTotal = 0;
		
		for (CropType c : crops) {
			fertTotal += getFertiliserAmount(c);
rhenry2's avatar
rhenry2 committed
	
		return areaTotal > 0 ? fertTotal / areaTotal : 0;
	}

	public static double getFertiliserTotal(Collection<? extends LandUseItem> items, CropType... crops) {			
		double total = 0;
		for (LandUseItem a : items) {
			if (a == null)
				continue;
			
			for (CropType c : crops)
				total += a.getFertiliserAmount(c);
		}
			
		return total;
	}

	public static double getFertiliserTotal(Collection<? extends LandUseItem> items, Collection<CropType> crops) {
		return getFertiliserTotal(items, crops.toArray(new CropType[crops.size()]));
	}
	
	/** Irrigation Intensity (unit less) */
	public double getIrrigationIntensity(CropType crop) {
		Intensity i = getIntensity(crop);
		return (i == null) ? 0 : i.getIrrigationIntensity();
	}
	
	/** Irrigation rate in litre/m2 */
	public double getIrrigationRate(CropType crop) {
		Intensity i = getIntensity(crop);
		
		return (i == null) ? 0 : i.getIrrigationRate();
	/** Irrigation amount in km3 or 10^9 m3, for this location */
		double rate = getIrrigationRate(c);
		double area = getCropArea(c);
		return rate * area * 0.01;  // rate(10^-3m or mm or l/m2) * area(10^6ha) = 10^3 m.ha = 10^7 m3 = 1/100 km3
	/** Irrigation Intensity (unit less) */
	public double getIrrigationAverageIntensity(CropType... crops) {
		double irrigTotal = 0;
		double areaTotal = 0;
		
		for (CropType c : crops) {
			double area = getCropArea(c);
			irrigTotal += getIrrigationIntensity(c) * area;
			areaTotal += area;
		}
		
		return areaTotal > 0 ? irrigTotal / areaTotal : 0;
	}
	
	/** Irrigation amount in km3, for this location */
	public static double getIrrigationTotal(Collection<? extends LandUseItem> items, CropType... crops) {			
		double total = 0;
		for (LandUseItem a : items) {
			if (a == null)
				continue;
			
			for (CropType c : crops)
				total += a.getIrrigationAmount(c);
		}
			
		return total;
	}

	public static double getIrrigationTotal(Collection<? extends LandUseItem> items, Collection<CropType> crops) {
		return getIrrigationTotal(items, crops.toArray(new CropType[crops.size()]));
	}
	
	/** move areas from one land cover to another, return any residual not possible */
    public double moveAreas(LandCoverType toType, LandCoverType fromType, double changeReq) {
        double prevTo = getLandCoverArea(toType);
        double prevFrom = getLandCoverArea(fromType);
        double availLC = fromType.isProtectable() ? Math.min(prevFrom, unprotectedAreas.get(fromType)) : prevFrom;  // if protected can not go past protected area
        
        double actuallyChanged = Math.min(availLC, changeReq);

        setLandCoverArea(toType, prevTo + actuallyChanged);
        setLandCoverArea(fromType, prevFrom - actuallyChanged);
Peter Alexander's avatar
Peter Alexander committed

	public double getCropArea(CropType c) {
		Double d = getLandCoverArea(LandCoverType.CROPLAND) * getCropFraction(c);
		return d;
	}
	
	public double getCropFraction(CropType c) {
		Double d = cropFractions.get(c);
		return d == null ? 0.0 : d;
Peter Alexander's avatar
Peter Alexander committed
	}
	public double getCropFraction(CropType... cropsToFind) {
		double totalFract = 0;
		for (CropType crop : cropsToFind) {
			totalFract += getCropFraction(crop);
		}
		return totalFract;
	}

Peter Alexander's avatar
Peter Alexander committed
	
	public void setCropFraction(CropType c, double areaFract) {
		cropFractions.put(c, areaFract);
Peter Alexander's avatar
Peter Alexander committed
	}
	
	public double getLandCoverArea(LandCoverType c) {
Peter Alexander's avatar
Peter Alexander committed
		Double d = landCoverAreas.get(c);
		return d == null ? 0.0 : d;
Peter Alexander's avatar
Peter Alexander committed
	}
	
Peter Alexander's avatar
Peter Alexander committed
	public double getLandCoverFract(LandCoverType c) {
		double totalArea = getTotalLandCoverArea();
		return totalArea==0 ? 0.0 : getLandCoverArea(c) / totalArea;
	public void setLandCoverArea(LandCoverType c, double d) {
rhenry2's avatar
rhenry2 committed
		if (Double.isNaN(d) || Double.isInfinite(d))
			throw new RuntimeException("AreasItem for " + c + " is " + d);
rhenry2's avatar
rhenry2 committed

		double landCover = (d < 0.0) ? 0.0 : d;
rhenry2's avatar
rhenry2 committed
		landCoverAreas.put(c, landCover);
Peter Alexander's avatar
Peter Alexander committed
	}
	
	public double getTotalLandCoverArea() {
		double d = 0;
		for (LandCoverType l : LandCoverType.values()) 
			d += getLandCoverArea(l);
Peter Alexander's avatar
Peter Alexander committed
	public static double getAbandonedPasture(Collection<? extends LandUseItem> landUses) {
		double total = 0;
		for (LandUseItem a : landUses) {
			if (a!=null) {
Peter Alexander's avatar
Peter Alexander committed
				Double d = a.getLandCoverArea(LandCoverType.PASTURE);
				Intensity intensity = a.getIntensity(CropType.PASTURE);
Peter Alexander's avatar
Peter Alexander committed
				if (intensity!=null && d!=null && intensity.getFertiliserAmount()==0 && intensity.getIrrigationRate()==0 && intensity.getOtherIntensity()==0)
	public static double getSuitableTotal(Collection<? extends LandUseItem> landUses, int year) {
		double total = 0;
		for (LandUseItem a : landUses) {
			if (a!=null) {
	public double getTotalNatural() {
		double totalNatural = getLandCoverArea(LandCoverType.OTHER_NATURAL) + getLandCoverArea(LandCoverType.MANAGED_FOREST) + getLandCoverArea(LandCoverType.UNMANAGED_FOREST);
		return totalNatural;
	}
	
	public double getTotalUnprotectedNatural() {	
		double unprotectedNatural = 0;
		for (LandCoverType landType : LandCoverType.values()) {
			if (landType.isProtectable()) {
				unprotectedNatural += unprotectedAreas.get(landType);
				}
		return unprotectedNatural;
	}
	
	public double getUnprotectedLandCoverArea(LandCoverType landType) {
		return unprotectedAreas.get(landType);

	private double getProtectedandUnavailable() {
		double totalSuitableLandCover = getTotalLandCoverArea()-getLandCoverArea(LandCoverType.BARREN)-getLandCoverArea(LandCoverType.URBAN);
		if (totalSuitableLandCover <=0)
		double minAndProtectedA = getUnavailableArea() + getProtectedArea() + totalSuitableLandCover * ModelConfig.MIN_NATURAL_RATE;
		return Math.min(totalSuitableLandCover, minAndProtectedA);
	/** averages protected areas across land cover types */
	private void updateUnprotectedAreas() {
		double desiredProtected = getProtectedandUnavailable();
		double totalNatural = getTotalNatural();
		double unprotectedNat = Math.max(0, totalNatural - desiredProtected);  // if we are already using more than is protected then the unprotectedArea is 0
		double unprotectedFract = (totalNatural > 0) ? unprotectedNat/totalNatural : 0;
		
		for (LandCoverType landType : LandCoverType.values()) {
			if (landType.isProtectable()) {
		      unprotectedAreas.put(landType, getLandCoverArea(landType)*unprotectedFract);
			}
			else unprotectedAreas.put(landType, getLandCoverArea(landType));
			}
	public void updateSuitableArea(int year) {			
		updateUnprotectedAreas(); //update unprotected areas 
Peter Alexander's avatar
Peter Alexander committed
		double currentAgri = getLandCoverArea(LandCoverType.PASTURE) + getLandCoverArea(LandCoverType.CROPLAND);
		double totalNatural = getTotalNatural();
		double natAvailForAgri =  totalNatural - getProtectedandUnavailable();  // could be negative, i.e. excess agricultural area already used
		//this forces protected areas, min_natural_rate and slope constraints to be obeyed 
		if (ModelConfig.FORCE_PROTECTED_AREAS && natAvailForAgri < 0 && year >= ModelConfig.FORCE_PROTECTED_AREAS_START_YEAR) {
			if (year < ModelConfig.FORCE_PROTECTED_AREAS_END_YEAR) 
				proportion = 1.0 / (ModelConfig.FORCE_PROTECTED_AREAS_END_YEAR - year);
			
			suitableArea = Math.max(0, currentAgri + natAvailForAgri * proportion); // netNatAvailForAgri is negative, but suitable area < 0 is not sensible (seems to happen with high barren areas)	
		else //not honouring protected areas if agriculture > natural that should be protected
			suitableArea = currentAgri + getTotalUnprotectedNatural();  
Peter Alexander's avatar
Peter Alexander committed
	}
Peter Alexander's avatar
Peter Alexander committed
	
	public double getForestManagedFraction() {
		double managed = getLandCoverArea(LandCoverType.MANAGED_FOREST);
		double unmanaged = getLandCoverArea(LandCoverType.UNMANAGED_FOREST);
		double d = managed / (managed + unmanaged);
		return (Double.isNaN(d) || Double.isInfinite(d)) ? 1.0 : d;
	}
	public CropToDouble getCropChanges(LandUseItem prevAreaAggItem) {
Peter Alexander's avatar
Peter Alexander committed
		CropToDouble changes = new CropToDouble();
Peter Alexander's avatar
Peter Alexander committed
		
		for (CropType c : CropType.getCropsLessPasture()) {
			double change = getCropArea(c) - prevAreaAggItem.getCropArea(c);
			changes.put(c, change);
Peter Alexander's avatar
Peter Alexander committed
		}
		
		return changes;
	}
	private boolean isZeroOrNull(Double d) {
		return d == null || d == 0;
	}
	 
	@Override
	public void interpolateAll(LandUseItem fromItem, LandUseItem toItem, double factor) {		
		cropFractions = new HashMap<CropType, Double>();
		landCoverAreas = new HashMap<LandCoverType, Double>();

		Double fromCropCover = fromItem.landCoverAreas.get(LandCoverType.CROPLAND);
		Double toCropCover = toItem.landCoverAreas.get(LandCoverType.CROPLAND);

		if (!isZeroOrNull(fromCropCover) && isZeroOrNull(toCropCover)) { // if start with crop but end with none, take starting crop fractions
			cropFractions.putAll(fromItem.cropFractions);
			intensityMap.putAll(fromItem.intensityMap);
		}
		else if (isZeroOrNull(fromCropCover) && !isZeroOrNull(toCropCover)) { // if start with no crop but end with some, take end crop fractions
			cropFractions.putAll(toItem.cropFractions);
			intensityMap.putAll(toItem.intensityMap);
		}
		else { // otherwise we need to interpolate crop fractions
			for (CropType crop : CropType.values()) {
				Double from = fromItem.cropFractions.get(crop);
				Double to = toItem.cropFractions.get(crop);
				Double d = Interpolator.interpolate(from, to, factor);
				cropFractions.put(crop, d);
				
				Intensity fromIntensity = fromItem.intensityMap.get(crop);
				Intensity toIntensity = toItem.intensityMap.get(crop);
				Intensity interpolateIntensity = toIntensity;  // might still be null
				
				if (fromIntensity != null && toIntensity != null)
					interpolateIntensity = new Intensity(fromIntensity, toIntensity, factor);  // both non-null really interpolate
				else if (fromIntensity != null)
					interpolateIntensity = fromIntensity; // just fromIntensity non-null
				
				intensityMap.put(crop, interpolateIntensity);
			}
		}
		
		for (LandCoverType landCover : LandCoverType.values()) {
			Double from = fromItem.landCoverAreas.get(landCover);
			Double to = toItem.landCoverAreas.get(landCover);
			Double d = Interpolator.interpolate(from, to, factor);
			landCoverAreas.put(landCover, d);
		}
	}
	
	public static double getTotalLandCover(Collection<? extends LandUseItem> landUses, LandCoverType landCover) {
		double total = 0;
Peter Alexander's avatar
Peter Alexander committed
		for (LandUseItem a : landUses) {
			if (a!=null) {
				Double d = a.getLandCoverArea(landCover);
				if (d!=null)
					total += d;
			}
		}
		return total;
	}
	public static double getTotalCropArea(Collection<LandUseItem> landUses, CropType crop) {
		double total = 0;
		for (LandUseItem a : landUses)
			total += a.getCropArea(crop);
		
		return total;
	}
		return "LandUseItem: [landCoverAreas=" + landCoverAreas + ", protectedArea=" + protectedArea + ", unavailableArea=" + unavailableArea + "]";