/* CPSCalculations.java - created: January 2008 * Copyright (C) 2008 Clayton Carter * * This file is part of the project "Crop Planning Software". For more * information: * website: https://github.com/claytonrcarter/cropplanning * email: cropplanning@gmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package CPS.Data; import CPS.Module.CPSGlobalSettings; import CPS.Module.CPSModule; import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.calculation.AbstractEventListCalculation; import java.util.Date; import java.util.GregorianCalendar; /** * CPSCalculations is a class of static utility methods for use in the calculation of * crop and planting data which the user has not specified. * * @author Clayton Carter */ public final class CPSCalculations { /** * Calculates a date to plant based upon a desired harvest date and the crops maturity days. * @param dateHarvest Desired date to harvest. * @param maturityDays Approx. days from planting to harvest. * @return A planting date. */ public static Date calcDatePlantFromDateHarvest( Date dateHarvest, int maturityDays, int matAdjust ) { return calcDatePlantFromDateHarvest( dateHarvest, maturityDays, matAdjust, 0 ); } public static Date calcDatePlantFromDateHarvest( Date dateHarvest, int maturityDays, int matAdjust, int weeksInGH ) { GregorianCalendar c = new GregorianCalendar(); c.setTime( dateHarvest ); // -1 ==> subtract c.add( GregorianCalendar.DAY_OF_YEAR, -1 * ( maturityDays + matAdjust + weeksInGH * 7 ) ); return c.getTime(); } public static Date calcDatePlantFromDateTP( Date dateTP, int weeksInGH ) { /* tricky tricky or smarty smarty? */ return calcDatePlantFromDateHarvest( dateTP, weeksInGH * 7, 0 ); } //****************************************************************************// public static Date calcDateTPFromDatePlant( Date datePlant, int weeksInGH ) { return calcDateHarvestFromDatePlant( datePlant, weeksInGH * 7, 0 ); } public static Date calcDateTPFromDateHarvest( Date dateHarvest, int maturityDays, int matAdjust ) { return calcDatePlantFromDateHarvest( dateHarvest, maturityDays, matAdjust ); } //****************************************************************************// public static Date calcDateHarvestFromDatePlant( Date datePlant, int maturityDays, int matAdjust ) { return calcDateHarvestFromDatePlant( datePlant, maturityDays, matAdjust, 0 ); } public static Date calcDateHarvestFromDatePlant( Date datePlant, int maturityDays, int matAdjust, int weeksInGH ) { GregorianCalendar c = new GregorianCalendar(); c.setTime( datePlant ); c.add( GregorianCalendar.DAY_OF_YEAR, maturityDays + matAdjust ); return c.getTime(); } public static Date calcDateHarvestFromDateTP( Date dateTP, int maturityDays, int matAdjust ) { return calcDateHarvestFromDatePlant( dateTP, maturityDays, matAdjust ); } //****************************************************************************// public static float calcBedsToPlantFromRowFtToPlant( int rowFt, int rowsPerBed, int bedLength ) { return (float) ( ( 1.0 * rowFt / rowsPerBed ) / bedLength ); } public static float calcBedsToPlantFromPlantsNeeded( int plantsNeeded, int inRowSpacing, int rowsPerBed, int bedLength ) { float rowFt = plantsNeeded * ( inRowSpacing / 12f ); // LATER 12 is a little too English-unit-centric // LATER when we implement calculation of rowFtNeeded, we could just call that here return ( rowFt / rowsPerBed ) / bedLength; } public static float calcBedsToPlantFromTotalYield( float totalYield, float yieldPerFt, int rowsPerBed, int bedLength ) { return calcBedsToPlantFromRowFtToPlant( calcRowFtToPlantFromTotalYield( totalYield, yieldPerFt ), rowsPerBed, bedLength ); } //****************************************************************************// public static int calcPlantsNeededFromBedsToPlant( float bedsToPlant, int inRowSpacing, int rowsPerBed, int bedLength ) { /* rowft = beds * rowsPerBed * BED_LENGTH */ /* plants/ft = 12 / inRowSpacing (inRowSpacing is really inches/plant) */ /* plants_needed = rowft * plants/ft */ // TODO Math.ceil() return (int) ( ( bedsToPlant * rowsPerBed * bedLength ) * ( 12.0 / inRowSpacing ) ); } public static int calcPlantsNeededFromRowFtToPlant( int rowFt, int inRowSpacing ) { /* plants_needed = rowFt * plants/ft */ /* plants/ft = 12 / inRowSpace */ // TODO Math.ceil() return (int) ( rowFt * ( 12.0 / inRowSpacing ) ); } public static int calcPlantsNeededFromTotalYield( float totalYield, float yieldPerFt, int inRowSpacing ) { return calcPlantsNeededFromRowFtToPlant( calcRowFtToPlantFromTotalYield( totalYield, yieldPerFt ), inRowSpacing ); } public static int calcPlantsNeededFromPlantsToStart( int plantsToStart ) { float fudgeFactor = CPSGlobalSettings.getFudgeFactor(); return (int) ( plantsToStart / ( 1 + fudgeFactor ) ); } //****************************************************************************// public static int calcRowFtToPlantFromBedsToPlant( float bedsToPlant, int rowsPerBed, int bedLength ) { /* rowft = beds * rowsPerBed * BED_LENGTH */ return (int) ( bedsToPlant * rowsPerBed * bedLength ); } public static int calcRowFtToPlantFromPlantsNeeded( int plantsNeeded, int inRowSpacing ) { /* rowFt = plants_needed * ft/plant */ /* plants/ft = inRowSpace / 12 */ return (int) ( plantsNeeded * ( inRowSpacing / 12.0 ) ); } public static int calcRowFtToPlantFromTotalYield( float totalYield, float yieldPerFt ) { return (int) ( totalYield / yieldPerFt ); } //****************************************************************************// public static int calcPlantsToStart( int plantsNeeded ) { float fudgeFactor = CPSGlobalSettings.getFudgeFactor(); int i = (int) ( plantsNeeded * ( 1 + fudgeFactor ) ); // make sure we always fudge by at least 1 if ( fudgeFactor > 0 && i == plantsNeeded ) i++; return i; } public static int calcPlantsToStart( float flatsToStart, int flatCapacity ) { return (int) ( flatsToStart * flatCapacity ); } //****************************************************************************// public static float calcFlatsNeeded( int plantsToStart, int flatCapacity ) { return roundQuarter( plantsToStart / (float) flatCapacity ); } //****************************************************************************// public static float calcTotalYieldFromRowFtToPlant( int rowFt, float yieldPerFt ) { return precision3( rowFt * yieldPerFt ); } //****************************************************************************// // Public Utility Methods //****************************************************************************// /** * Given a flat size string, calculates a flat capacity. * @param flatSize The flat size string ot parse. * Formats accepted: <number>, <flat name + (number)>, <flat name> * @return The capacity of the flat or 0 on error. */ public static int extractFlatCapacity( String flatSize ) { // TODO - Is 0 a reasonable default? perhaps 1 would be better int cap = 0; if ( flatSize != null ) { // For the case where FlatSize is eg "128" or "72" if ( flatSize.matches( "\\d+" )) { try { cap = Integer.parseInt( flatSize ); } catch ( NumberFormatException ignore ) {} } // For the case where FlatSize is eg "1020 tray (500)" or "mini ( 50 )" else if ( flatSize.matches( ".*\\(\\s*\\d+\\s*\\).*" )) { int openPar = flatSize.lastIndexOf( "(" ); int closePar = flatSize.lastIndexOf( ")" ); try { cap = Integer.parseInt( flatSize.substring( openPar + 1, closePar ).trim() ); } catch ( NumberFormatException ignore ) { System.err.println( "ERROR: couldn't deduce the capacity of given flat size: " + flatSize ); } } } return cap; } /** * Given a field name string, extracts a bed length. * @param fieldName The field name string to parse. * Formats accepted: <blank>, <field name>, <field name + (number)> * @return The bed length of that field or the default value on error. The default value is 100. */ public static int extractBedLength( String fieldName ) { int len = CPSGlobalSettings.getBedLength(); // only extract a bed length if the fieldName is eg "field blah blah (100)" or "garden blah blah ( 50 )" if ( fieldName != null && fieldName.matches( ".*\\(\\s*\\d+\\s*\\).*" ) ) len = extractFlatCapacity( fieldName ); return len; } //****************************************************************************// // Private Utility Methods //****************************************************************************// public static float precision3( float f ) { return (float) ((int) ( f * 1000 )) / 1000f; } public static float precision2( float f ) { return (float) ((int) ( f * 100 )) / 100f; } public static float roundQuarter( float f ) { return (float) Math.ceil( f * 4 ) / 4; } public static float roundHalf( float f ) { return (float) Math.ceil( f * 2 ) / 2; } public static float roundThird( float f ) { return (float) Math.ceil( f * 3 ) / 3; } //****************************************************************************// // Utility Classes //****************************************************************************// //****************************************************************************// // Advanced Calculation //****************************************************************************// public static final class SumBedsRowftFlats extends AbstractEventListCalculation<Float, CPSPlanting> { public float beds, rowUnitLengthes, flats; public SumBedsRowftFlats(EventList<CPSPlanting> source) { super(new Float(0), source); beds = rowUnitLengthes = flats = 0; } protected void inserted( CPSPlanting p ) { beds += p.getBedsToPlant(); rowUnitLengthes += p.getRowFtToPlant(); flats += p.getFlatsNeeded(); } protected void deleted(CPSPlanting p) { beds -= p.getBedsToPlant(); rowUnitLengthes -= p.getRowFtToPlant(); flats -= p.getFlatsNeeded(); } protected void updated( CPSPlanting oldP, CPSPlanting newP ) { beds = beds - oldP.getBedsToPlant() + newP.getBedsToPlant(); rowUnitLengthes = rowUnitLengthes - oldP.getRowFtToPlant() + newP.getRowFtToPlant(); flats = flats - oldP.getFlatsNeeded() + newP.getFlatsNeeded(); } } public static final class SumPlantsFlats extends AbstractEventListCalculation<Float, CPSPlanting> { public int plantsNeeded, plantsToStart; public float flats; public SumPlantsFlats(EventList<CPSPlanting> source) { super(new Float(0), source); plantsNeeded = plantsToStart = 0; flats = 0; } protected void inserted( CPSPlanting p ) { plantsNeeded += p.getPlantsNeeded(); plantsToStart += p.getPlantsToStart(); flats += p.getFlatsNeeded(); } protected void deleted(CPSPlanting p) { plantsNeeded -= p.getPlantsNeeded(); plantsToStart -= p.getPlantsToStart(); flats -= p.getFlatsNeeded(); } protected void updated( CPSPlanting oldP, CPSPlanting newP ) { plantsNeeded = plantsNeeded - oldP.getPlantsNeeded() + newP.getPlantsNeeded(); plantsToStart = plantsToStart - oldP.getPlantsToStart() + newP.getPlantsToStart(); flats = flats - oldP.getFlatsNeeded() + newP.getFlatsNeeded(); } } public static void main( String[] args ) { BasicEventList<CPSPlanting> el = new BasicEventList<CPSPlanting>(); SumPlantsFlats sum1 = new SumPlantsFlats( el ); SumBedsRowftFlats sum2 = new SumBedsRowftFlats( el ); for ( int i = 0; i < 10; i++ ) { CPSPlanting p = new CPSPlanting(); p.setVarietyName( "[" + i + "]" ); p.setFlatsNeeded( i ); el.add( p ); } System.out.println( "Flats: " + sum1.flats ); System.out.println( "Flats: " + sum2.flats ); } }