package seng302.model; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * A static class for parsing and storing the polars. Will parse the whole polar table and also store the optimised * upwind and downwind in separate tables here as well * Created by wmu16 on 22/05/17. */ public final class PolarTable { //A Polar table will consist of a wind speed key to a hashmap value of pairs of wind angles and boat speeds private static HashMap> polarTable; private static HashMap> upwindOptimal; private static HashMap> downwindOptimal; private static Double optimalAngle; private static int upTwaIndex; private static int dnTwaIndex; /** * Iterates through each row of the polar table, in pairs, to extract the row into a hashmap of angle to boat speed. * These angle boatspeed hashmaps are then added to an outer hashmap at the end of wind speed key to each row hashmap * as a value * @param polarFile polarFile to be parsed */ public static void parsePolarFile(InputStream polarFile) { polarTable = new HashMap<>(); upwindOptimal = new HashMap<>(); downwindOptimal = new HashMap<>(); String line = null; String check; Boolean isHeaderLine = true; try (BufferedReader br = new BufferedReader(new InputStreamReader(polarFile))) { while ((check = br.readLine()) != null) { line = check; String[] thisLine = line.split(","); //Initial line in file if (isHeaderLine) { deduceHeaders(thisLine); isHeaderLine = false; } else { HashMap thisPolar = new HashMap<>(); HashMap thisUpWindPolar = new HashMap<>(); HashMap thisDnWindPolar = new HashMap<>(); Double thisWindSpeed = Double.parseDouble(thisLine[0]); // -3 <== -1 for length -1, and a further -2 as we iterate in pairs of 2 so finish before final 2 for (int i = 1; i < thisLine.length; i += 2) { Double thisWindAngle = Double.parseDouble(thisLine[i]); Double thisBoatSpeed = Double.parseDouble(thisLine[i + 1]); thisPolar.put(thisWindAngle, thisBoatSpeed); if (i == upTwaIndex) { thisUpWindPolar.put(thisWindAngle, thisBoatSpeed); } else if (i == dnTwaIndex) { thisDnWindPolar.put(thisWindAngle, thisBoatSpeed); } } polarTable.put(thisWindSpeed, thisPolar); upwindOptimal.put(thisWindSpeed, thisUpWindPolar); downwindOptimal.put(thisWindSpeed, thisDnWindPolar); } } getMaxSpeedAngle(line); } catch (IOException e) { System.out.println("[PolarTable] IO exception"); } } /** * Passes the final line of the polar table and iterates over the speeds for each * angle, velocity pair to find the angle that produces the highest velocity * * @param line The last line of the polar file */ private static void getMaxSpeedAngle(String line) { String[] theLastLine = line.split(","); Double maxWindVal = Double.parseDouble(theLastLine[0]); Double optimalAngle = Double.parseDouble(theLastLine[1]); Double maxSpeed = Double.parseDouble(theLastLine[2]); for (Map.Entry entry : polarTable.get(maxWindVal).entrySet()) { if (entry.getValue() > maxSpeed) { maxSpeed = entry.getValue(); optimalAngle = entry.getKey(); } } PolarTable.optimalAngle = optimalAngle; } /** * Parses the header line of a polar file * @param thisLine The line which is the header of a polar file */ private static void deduceHeaders(String[] thisLine) { for (int i = 0; i < thisLine.length; i++) { String thisItem = thisLine[i]; if (thisItem.toLowerCase().startsWith("uptwa")) { upTwaIndex = i; } else if (thisItem.toLowerCase().startsWith("dntwa")) { dnTwaIndex = i; } } } public static Double getOptimalAngle() { return optimalAngle; } /** * @return The entire polar table */ public static HashMap> getPolarTable() { return polarTable; } /** * @return The polar table just containing the optimal upwind values */ public static HashMap> getUpwindOptimal() { return upwindOptimal; } /** * @return The polar table just containing the optimal downwind values */ public static HashMap> getDownwindOptimal() { return downwindOptimal; } /** * Will raise an exception if a polar table has just one row of data * @param thisWindSpeed The current wind speed * @return HashMap containing just the optimal upwind angle and resulting boat speed */ public static HashMap getOptimalUpwindVMG(Double thisWindSpeed) { Double polarWindSpeed = getClosestWindSpeedInPolar(thisWindSpeed); return upwindOptimal.get(polarWindSpeed); } /** * Will raise an exception if a polar table has just one row of data * @param thisWindSpeed The current wind speed * @return HashMap containing just the optimal downwind angle and resulting boat speed */ public static HashMap getOptimalDownwindVMG(Double thisWindSpeed) { Double polarWindSpeed = getClosestWindSpeedInPolar(thisWindSpeed); return downwindOptimal.get(polarWindSpeed); } public static Double getBoatSpeed(Double thisWindSpeed, Double thisHeading) { Double polarWindSpeed = getClosestWindSpeedInPolar(thisWindSpeed); Double polarAngle = getClosestAngleInPolar(polarTable.get(polarWindSpeed), thisHeading); return polarTable.get(polarWindSpeed).get(polarAngle); } public static Double getClosestWindSpeedInPolar(Double thisWindSpeed) { Double smallestDif = Double.POSITIVE_INFINITY; Double closestWind = 0d; for (Double polarWindSpeed : polarTable.keySet()) { Double difference = Math.abs(polarWindSpeed - thisWindSpeed); if (difference < smallestDif) { smallestDif = difference; closestWind = polarWindSpeed; } } return closestWind; } public static Double getClosestAngleInPolar(HashMap thisWindSpeedPolar, Double thisHeading) { Double smallestDif = Double.POSITIVE_INFINITY; Double closestAngle = 0d; for (Double polarAngle : thisWindSpeedPolar.keySet()) { Double difference = Math.abs(polarAngle - thisHeading); if (difference < smallestDif) { smallestDif = difference; closestAngle = polarAngle; } } return closestAngle; } }