/*
*
*/
package au.org.aurin.wif.impl.demand;
import java.util.NavigableSet;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import au.org.aurin.wif.exception.validate.WifInvalidInputException;
import au.org.aurin.wif.model.Projection;
import au.org.aurin.wif.model.demand.CurrentDemographic;
import au.org.aurin.wif.model.demand.DemandConfig;
import au.org.aurin.wif.model.demand.DemographicTrend;
import au.org.aurin.wif.model.demand.EmploymentDemographicData;
import au.org.aurin.wif.model.demand.EmploymentSector;
import au.org.aurin.wif.model.demand.ResidentialDemographicData;
import au.org.aurin.wif.model.demand.info.ResidentialDemandInfo;
/**
* <b>DemandProjector.java</b> : includes logic for discovering land demand
*
* @author <a href="mailto:marcosnr@unimelb.edu.au"> Marcos Nino-Ruiz
* marcosnr@unimelb.edu.au</a> - 2012
*/
public class DemandProjector {
/**
* logger.
*/
private static final Logger LOGGER = LoggerFactory
.getLogger(DemandProjector.class);
/**
* Project households demand. See Appendix E of What If manual for detailed
* functionality
*
* @param totalPop
* the total pop
* @param groupQuartersPop
* the group quarters pop
* @param averageHousehold
* the average household
* @return the long
*/
public static Long projectHouseholdsDemand(final Long totalPop,
final Long groupQuartersPop, final Double averageHousehold) {
Long projectedHouseHoldsPopulation = 0l;
if (groupQuartersPop != null) {
projectedHouseHoldsPopulation = totalPop - groupQuartersPop;
} else {
projectedHouseHoldsPopulation = totalPop;
}
LOGGER.debug("projected HouseHolds Population: {}",
projectedHouseHoldsPopulation);
return Math.round(projectedHouseHoldsPopulation / averageHousehold);
}
/**
* Adjust vacancy. See Appendix E of What If manual for detailed functionality
*
* @param projected
* the projected
* @param vacancyRate
* the vacancy rate
* @return the long
*/
public static Long adjustVacancy(final Long projected,
final Double vacancyRate) {
return Math.round(projected / (1 - vacancyRate));
}
/**
* Adjust vacancy. See Appendix E of What If manual for detailed functionality
*
* @param projected
* the projected
* @param vacancyRate
* the vacancy rate
* @return the double
*/
private static Double adjustDemandVacancy(final Double projected,
final Double vacancyRate) {
return projected / (1 - vacancyRate);
}
/**
* Adjust population vacancy. See Appendix E of What If manual for detailed
* functionality
*
* @param projected
* the projected
* @param vacancyRate
* the vacancy rate
* @return the double
*/
private static Double adjustPopulationVacancy(final Double projected,
final Double vacancyRate) {
return projected * (1 - vacancyRate);
}
/**
* Adjust infill. See Appendix E of What If manual for detailed functionality
*
* @param projectedDensityDemand
* the demand
* @param infillRate
* the infill rate
* @return the double
*/
public static Double adjustDemandInfill(final Double projectedDensityDemand,
final Double infillRate) {
return projectedDensityDemand * (1 - infillRate);
}
/**
* Adjust infill. See Appendix E of What If manual for detailed functionality
*
* @param newHUnits
* the demand
* @param infillRate
* the infill rate
* @return the double
*/
public static Double adjustPopulationInfill(final Double newHUnits,
final Double infillRate) {
return newHUnits / (1 - infillRate);
}
/**
* Project housing units. See Appendix E of What If manual for detailed
* functionality
*
* @param totalPop
* the total pop
* @param groupQuartersPop
* the group quarters pop
* @param averageHousehold
* the average household
* @param vacancyRate
* the vacancy rate
* @return the long
*/
public static Long projectHousingUnits(final Long totalPop,
final Long groupQuartersPop, final Double averageHousehold,
final Double vacancyRate) {
final Long projectedHouses = projectHouseholdsDemand(totalPop,
groupQuartersPop, averageHousehold);
LOGGER.debug("projected HouseHolds: {}", projectedHouses);
final Long projectedHouseunits = adjustVacancy(projectedHouses, vacancyRate);
LOGGER.debug("ProjectedUnits,adjusted with Vacancy: {}",
projectedHouseunits);
return projectedHouseunits;
}
/**
* Project density residential demand. See Appendix E of What If manual for
* detailed functionality
*
* @param totalPop
* the total pop
* @param groupQuartersPop
* the group quarters pop
* @param averageHousehold
* the average household
* @param residentialDensity
* the residential density
* @param vacancyRate
* the vacancy rate
* @param infillRate
* the infill rate
* @param currentHousingUnits
* the current housing units
* @param residentialBreakdown
* the residential breakdown
* @return the double
*/
public static Double projectDensityResidentialDemand(final Long totalPop,
final Long groupQuartersPop, final Double averageHousehold,
final Double residentialDensity, final Double vacancyRate,
final Double infillRate, final Long currentHousingUnits,
final Double residentialBreakdown) {
LOGGER.debug("<<<<<<< assuming the following demographic values...");
LOGGER.debug("projected Total Population: {}", totalPop);
LOGGER.debug("projected Group Quarters Population: {}", groupQuartersPop);
LOGGER.debug("projected Average Household Size: {}", averageHousehold);
LOGGER.debug("projected Residential Density: {}", residentialDensity);
LOGGER.debug("projected Vacancy Rate: {}", vacancyRate);
LOGGER.debug("projected infillRate: {}", infillRate);
LOGGER.debug(" current Housing Units: {}", currentHousingUnits);
LOGGER.debug("projected breakdown percentage by housing type: {}",
residentialBreakdown);
LOGGER.debug(">>>>>> calculating the following variables...");
final Long projectedHouseunits = projectHousingUnits(totalPop,
groupQuartersPop, averageHousehold, vacancyRate);
final Long newHousingUnits = projectedHouseunits - currentHousingUnits;
LOGGER.debug("new House Units: {}", newHousingUnits);
final Double projectedResidentialUnitsByType = newHousingUnits
* residentialBreakdown;
LOGGER.debug("breakdown, new House Units by type: {}",
projectedResidentialUnitsByType);
final Double projectedResidentialUnits = adjustDemandInfill(
projectedResidentialUnitsByType, infillRate);
LOGGER.debug("area with infilled Adjusted Density Demand: {}",
projectedResidentialUnits);
final Double densityDemand = projectedResidentialUnits / residentialDensity;
LOGGER.debug("area density Demand : {}", densityDemand);
return densityDemand;
}
/**
* Project density demand of land in area. See Appendix E of What If manual
* for detailed functionality
*
* @param currentPopulation
* the current populination
* @param projectPopulation
* the project population
* @param vacancyRate
* the vacancy rate
* @param infillRate
* the infill rate
* @param projectedDensity
* the projected density
* @return the new area for demand
*/
public static Double projectDensityDemand(final Long currentPopulation,
final Long projectPopulation, final Double vacancyRate,
final Double infillRate, final Double projectedDensity) {
final Long newPopulation = projectPopulation - currentPopulation;
LOGGER.debug("New Population of {}", newPopulation);
final Long projectedUnits = adjustVacancy(newPopulation, vacancyRate);
LOGGER.debug("adjusted with Vacancy Projected Units are: {}",
projectedUnits);
final Double projectedDensityDemand = projectedUnits / projectedDensity;
LOGGER
.debug("area of projected Density Demand: {}", projectedDensityDemand);
final Double infilledAdjustedDensityDemand = adjustDemandInfill(
projectedDensityDemand, infillRate);
LOGGER.debug("area with infilled Adjusted Density Demand: {}",
infilledAdjustedDensityDemand);
return infilledAdjustedDensityDemand;
}
/**
* Project employment density demand.
*
* @param currentEmploymentBySector
* the employment by sector
* @param projectedEmploymentBySector
* the projected employment by sector
* @param infillRate
* the infill rate
* @param employmentDensity
* the projected density
* @param numberOfLandUsesPerSector
* the number of land uses per sector
* @return the double
*/
public static Double projectEmploymentDensityDemand(
final Long currentEmploymentBySector,
final Long projectedEmploymentBySector, final Double infillRate,
final Double employmentDensity, final Integer numberOfLandUsesPerSector,
final Double dPercentage, final Double futureDensity) {
LOGGER
.debug("<<<<<<< assuming the following employment-sector-projection-land use values...");
LOGGER.debug("current employment: {}", currentEmploymentBySector);
LOGGER.debug("projected employment: {}", projectedEmploymentBySector);
LOGGER.debug("number Of LandUses associated with this Sector: {}",
numberOfLandUsesPerSector);
LOGGER.debug("projected Employment Density: {}", employmentDensity);
LOGGER.debug("projected infillRate: {}", infillRate);
//
final Long newPopulation = projectedEmploymentBySector
- currentEmploymentBySector;
LOGGER.debug(" New employees : {}", newPopulation);
final Double projectedDensityDemand = newPopulation / employmentDensity;
LOGGER
.debug("area of projected Density Demand: {}", projectedDensityDemand);
final Double infilledAdjustedDensityDemand = adjustDemandInfill(
projectedDensityDemand, infillRate);
LOGGER.debug("area with infilled Adjusted Density Demand: {}",
infilledAdjustedDensityDemand);
final Double projectedDensityDemandBySector = infilledAdjustedDensityDemand
/ numberOfLandUsesPerSector;
LOGGER.debug("adjusted for {} number of LandUses per sectors: {}",
numberOfLandUsesPerSector, projectedDensityDemandBySector);
// return projectedDensityDemandBySector;
// /new claudia
final double x1 = newPopulation * dPercentage;
final double x2 = x1 * (1 - infillRate);
final double x3 = x2 / futureDensity;
return x3;
// //
}
/**
* Project employment density demand by sector. See Appendix E of What If
* manual for detailed functionality
*
* @param currentEmploymentBySector
* the current employment by sector
* @param projectedEmploymentBySector
* the projected employment by sector
* @param infillRate
* the infill rate
* @param employmentDensity
* the employment density
* @param landUseArea
* the land use area
* @return the double
*/
public static Double projectEmploymentDensityDemandBySector(
final Long currentEmploymentBySector,
final Long projectedEmploymentBySector, final Double infillRate,
Double employmentDensity, final Double landUseArea,
final Double dPercentage, final Double futureDensityLU) {
LOGGER
.debug("<<<<<<< many to many analysis, assuming the following employment-sector-projection-land use values...");
LOGGER.debug("current employment: {}", currentEmploymentBySector);
LOGGER.debug("projected employment: {}", projectedEmploymentBySector);
LOGGER.debug("LandUseArea associated with this Sector: {}", landUseArea);
LOGGER.debug("projected Employment Density: {}", employmentDensity);
LOGGER.debug("projected infillRate: {}", infillRate);
//
final Long newPopulation = projectedEmploymentBySector
- currentEmploymentBySector;
LOGGER.debug(" New employees (growth) : {}", newPopulation);
employmentDensity = currentEmploymentBySector / landUseArea;
LOGGER.debug("associated land use employment density: {}",
employmentDensity);
final Double projectedDensityDemand = newPopulation / employmentDensity;
LOGGER.debug("area of projected Density Demand for this LU/sector: {}",
projectedDensityDemand);
final Double infilledAdjustedDensityDemand = adjustDemandInfill(
projectedDensityDemand, infillRate);
LOGGER.debug("area with infilled Adjusted Density Demand: {}",
infilledAdjustedDensityDemand);
// return infilledAdjustedDensityDemand;
// new claudia
final double x1 = newPopulation * dPercentage;
final double x2 = x1 * (1 - infillRate);
final double x3 = x2 / futureDensityLU;
return x3;
}
/**
* Project residential population. See Appendix E of What If manual for
* detailed functionality
*
* @param unchangedLandUseArea
* the unchanged land use area
* @param newLandUseArea
* the new land use area
* @param averageHousehold
* the average household
* @param currentResidentialDensity
* the current residential density
* @param projectedResidentialDensity
* the projected residential density
* @param vacancyRate
* the vacancy rate
* @param infillRate
* the infill rate
* @param averageHouseHoldSize
* the average house hold size
* @return the double
*/
public static Double projectResidentialPopulation(
final Double unchangedLandUseArea, final Double newLandUseArea,
final Double averageHousehold, final Double currentResidentialDensity,
final Double projectedResidentialDensity, final Double vacancyRate,
final Double infillRate, final Double averageHouseHoldSize) {
final Double currentHUnits = unchangedLandUseArea
* currentResidentialDensity;
LOGGER.debug(" currentHUnits : {}", currentHUnits);
final Double projectedHUnits = newLandUseArea * projectedResidentialDensity;
LOGGER.debug(" projectedHUnits : {}", projectedHUnits);
final Double newHUnits = currentHUnits + projectedHUnits;
LOGGER.debug(" newHUnits : {}", newHUnits);
final Double infilledNnewHUnits = adjustPopulationInfill(newHUnits,
infillRate);
LOGGER.debug(" infilledNnewHUnits : {}", infilledNnewHUnits);
final Double newHouseHolds = adjustPopulationVacancy(infilledNnewHUnits,
vacancyRate);
LOGGER.debug(" newHouseHolds : {}", newHouseHolds);
final Double newPopulation = newHouseHolds * averageHouseHoldSize;
LOGGER.debug(" newPopulation : {}", newPopulation);
return newPopulation;
}
/**
* Project population group quarters. See Appendix E of What If manual for
* detailed functionality
*
* @param unchangedLandUseArea
* the unchanged land use area
* @param newLandUseArea
* the new land use area
* @param currentResidentialDensity
* the current residential density
* @param projectedResidentialDensity
* the projected residential density
* @return the double
*/
public static Double projectPopulationGroupQuarters(
final Double unchangedLandUseArea, final Double newLandUseArea,
final Double currentResidentialDensity,
final Double projectedResidentialDensity) {
final Double currentHUnits = unchangedLandUseArea
* currentResidentialDensity;
LOGGER.debug(" current : {}", currentHUnits);
final Double projectedHUnits = newLandUseArea * projectedResidentialDensity;
LOGGER.debug(" projected : {}", projectedHUnits);
final Double newGroupQuaters = currentHUnits + projectedHUnits;
LOGGER.debug(" newGroupQuaters : {}", newGroupQuaters);
return newGroupQuaters;
}
/**
* Do residential demand area. See Appendix E of What If manual for detailed
* functionality
*
* @param demandInfo
* the demand info
* @param currentDemographic
* the current demographic
* @param nextDemographic
* the next demographic
* @return the double
*/
public static Double doResidentialDemandArea(
final ResidentialDemandInfo demandInfo,
final ResidentialDemographicData currentDemographic,
final ResidentialDemographicData nextDemographic) {
LOGGER.debug(" calculating residential demand area ");
final Double demand = DemandProjector.projectDensityResidentialDemand(
nextDemographic.getTotalPopulation(),
nextDemographic.getgQPopulation(),
nextDemographic.getAverageHouseholdSize(),
demandInfo.getFutureDensity(), demandInfo.getFutureVacancyRate(),
demandInfo.getInfillRate(), currentDemographic.getHousingUnits(),
demandInfo.getFutureBreakdownByHType());
LOGGER
.debug(
"projected demand is {}; updating future housing units for the next cycle... ",
demand);
nextDemographic.setHousingUnits(projectHousingUnits(
nextDemographic.getTotalPopulation(),
nextDemographic.getgQPopulation(),
nextDemographic.getAverageHouseholdSize(),
demandInfo.getFutureVacancyRate()));
return demand;
}
/**
* Him him him Project employment demographic data.
*
* @param dt
* the dt
* @param projections
* the projections
* @param demandConfig
* the demand config
* @param sectors
* the sectors
* @return the demographic trend
* @throws WifInvalidInputException
* the wif invalid input exception
*/
public static DemographicTrend projectEmploymentDemographicData(
final DemographicTrend dt, final TreeSet<Projection> projections,
final DemandConfig demandConfig, final Set<EmploymentSector> sectors)
throws WifInvalidInputException {
// // new claudia
final Projection current = projections.first();
LOGGER.info("current year projection: {}", current.getLabel());
final NavigableSet<Projection> projectedSet = projections.tailSet(
projections.first(), false);
// end new claudia
for (final EmploymentSector employmentSector : sectors) {
LOGGER.debug("******* Current employmentSector: {}",
employmentSector.getLabel());
// Sector growth rates
final double growthRateSector = demandConfig
.getEmploymentGrowthRate(employmentSector);
// Base case
final CurrentDemographic currentDemographic = demandConfig
.getCurrentDemographic();
LOGGER.debug("Current year: {}", currentDemographic.getYear());
Long baseEmployees = currentDemographic.getEmployees(employmentSector);
// new claudia
final EmploymentDemographicData projectedDemographicDataFirst = new EmploymentDemographicData();
projectedDemographicDataFirst.setSector(employmentSector);
projectedDemographicDataFirst.setEmployees(baseEmployees.intValue());
projectedDemographicDataFirst.setSector(employmentSector);
projectedDemographicDataFirst.setSectorLabel(employmentSector.getLabel());
projectedDemographicDataFirst.setProjection(current);
projectedDemographicDataFirst.setProjectionLabel(current.getLabel());
projectedDemographicDataFirst.setDemographicTrend(dt);
dt.getDemographicData().add(projectedDemographicDataFirst);
// end new claudia
// General case
for (final Projection projection : projectedSet) {
// for (final Projection projection : projections) { // for claudia
final EmploymentDemographicData projectedDemographicData = new EmploymentDemographicData();
LOGGER.info("----> projection year: {}", projection.getLabel());
LOGGER.debug("Base employees: {}", baseEmployees);
final Long projectedEmployees = DemandProjector.projectPopulation(
growthRateSector, baseEmployees);
LOGGER.debug("Projected total employees: {}", projectedEmployees);
projectedDemographicData.setEmployees(projectedEmployees.intValue());
baseEmployees = projectedDemographicData.getEmployees().longValue();
projectedDemographicData.setSector(employmentSector);
projectedDemographicData.setSectorLabel(employmentSector.getLabel());
projectedDemographicData.setProjection(projection);
projectedDemographicData.setProjectionLabel(projection.getLabel());
projectedDemographicData.setDemographicTrend(dt);
dt.getDemographicData().add(projectedDemographicData);
}
}
return dt;
}
/**
* Project demographic data. Based on projection and past trends, FIXME
* replace with real algorithm.
*
* @param dt
* the dt
* @param projections
* the projection
* @param demandConfig
* the demand config
* @return the demographic data
* @throws WifInvalidInputException
* the wif invalid input exception
*/
public static DemographicTrend projectResidentialDemographicData(
final DemographicTrend dt, final TreeSet<Projection> projections,
final DemandConfig demandConfig) throws WifInvalidInputException {
// Residential growth rates
final Double growthRatePopulation = demandConfig.getPopulationGrowthRate();
final Double growthRateGQ = demandConfig.getGqGrowthRate();
final Double growthRateHouseholds = demandConfig.getHouseholdsGrowthRate();
// TODO Base case, why do we need the currentDemographicData?
final CurrentDemographic currentDemographic = demandConfig
.getCurrentDemographic();
Long basePopulation = currentDemographic.getTotalPopulation();
double baseHouseholds = currentDemographic.getHouseholds();
double baseAvgHHSize = currentDemographic.getAverageHouseholdSize();
double baseHousingUnits = currentDemographic.getHousingUnits();
LOGGER.debug("Current year: {}", currentDemographic.getYear());
// new claudia
final Projection current = projections.first();
LOGGER.info("current year projection: {}", current.getLabel());
final NavigableSet<Projection> projectedSet = projections.tailSet(
projections.first(), false);
final ResidentialDemographicData projectedDemographicDataFirst = new ResidentialDemographicData();
projectedDemographicDataFirst.setTotalPopulation(basePopulation);
projectedDemographicDataFirst.setHouseholds(baseHouseholds);
projectedDemographicDataFirst.setHousingUnits((long) baseHousingUnits);
projectedDemographicDataFirst.setAverageHouseholdSize(baseAvgHHSize);
projectedDemographicDataFirst.setProjection(current);
projectedDemographicDataFirst.setProjectionLabel(current.getLabel());
projectedDemographicDataFirst.setDemographicTrend(dt);
dt.getDemographicData().add(projectedDemographicDataFirst);
// end new claudia
// General case
for (final Projection projection : projectedSet) {
// for (final Projection projection : projections) {// for claudia
final ResidentialDemographicData projectedDemographicData = new ResidentialDemographicData();
LOGGER.info("----> projection year: {}", projection.getLabel());
LOGGER.debug("Base population: {}", basePopulation);
LOGGER.debug("Base households: {}", baseHouseholds);
LOGGER.debug("Base average household size: {}", baseAvgHHSize);
LOGGER.debug("Base housing units: {}", baseHousingUnits);
final Long projectedTotalPopulation = DemandProjector.projectPopulation(
growthRatePopulation, basePopulation);
LOGGER.debug("Projected total population: {}", projectedTotalPopulation);
final Long projectedHouseholds = Math.round((1 + growthRateHouseholds)
* baseHouseholds);
LOGGER.debug("Projected households: {}", projectedHouseholds);
// FIXME FInd out if I apply this projection to housing units
final Long projectedHousingUnits = Math.round((1 + growthRateHouseholds)
* baseHousingUnits);
LOGGER.debug("Projected HousingUnits: {}", projectedHousingUnits);
projectedDemographicData.setTotalPopulation(projectedTotalPopulation);
projectedDemographicData.setHouseholds(Double
.valueOf(projectedHouseholds));
LOGGER.debug("Projected average household size: {}",
projectedDemographicData.getAverageHouseholdSize());
projectedDemographicData.setHousingUnits(projectedHousingUnits);
// Setting the stage for the next iteration
basePopulation = projectedDemographicData.getTotalPopulation();
baseHouseholds = projectedDemographicData.getHouseholds();
baseHousingUnits = projectedDemographicData.getHousingUnits();
baseAvgHHSize = projectedDemographicData.getAverageHouseholdSize();
projectedDemographicData.setProjection(projection);
projectedDemographicData.setProjectionLabel(projection.getLabel());
projectedDemographicData.setDemographicTrend(dt);
dt.getDemographicData().add(projectedDemographicData);
}
return dt;
}
/**
* Project growth rate.
*
* @param projectionPeriod
* the projection period
* @param observationPeriod
* the observation period
* @param basePopulation
* the base population
* @param nextPopulation
* the next population
* @return the double
*/
public static Double projectGrowthRate(final double projectionPeriod,
final double observationPeriod, final double basePopulation,
final double nextPopulation) {
LOGGER.trace("Projection period: {}", projectionPeriod);
final Double observedGrowthRate = (nextPopulation - basePopulation)
/ basePopulation;
LOGGER.trace("observedGrowthRate: {}", observedGrowthRate);
// Rm = (1 + Rn)^(m/n) – 1
final double periodRate = projectionPeriod / observationPeriod;
LOGGER.trace("period rate: {}", periodRate);
final double baseGrowth = 1 + observedGrowthRate;
LOGGER.trace("baseGrowth: {}", baseGrowth);
final Double rm = Math.pow(1 + observedGrowthRate, periodRate) - 1;
// Double rm = Math.pow((1 + observedGrowthRate), periodRate) - 1;
LOGGER.debug("Projected growth rate: {}", rm);
return rm;
}
/**
* Project population.
*
* @param rmPopulation
* the rm population
* @param currentPopulation
* the current population
* @return the int
*/
public static Long projectPopulation(final Double rmPopulation,
final Long currentPopulation) {
return Math.round((1 + rmPopulation) * currentPopulation);
}
}