package com.flaremicro.visualforecast.displays; import static com.flaremicro.visualforecast.graphics.RenderConstants.BG_BLUE; import static com.flaremicro.visualforecast.graphics.RenderConstants.MAINBAR_HEIGHT; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.image.BufferedImage; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; import com.flaremicro.visualforecast.RenderPanel; import com.flaremicro.visualforecast.api.ForecastProvider; import com.flaremicro.visualforecast.forecast.DayForecast; import com.flaremicro.visualforecast.forecast.ForecastDetails; import com.flaremicro.visualforecast.forecast.HourlyForecast; import com.flaremicro.visualforecast.forecast.TownForecast; import com.flaremicro.visualforecast.forecast.ValueCheck; import com.flaremicro.visualforecast.graphics.DrawingUtil; import com.flaremicro.visualforecast.graphics.FontManager; import com.flaremicro.visualforecast.graphics.RenderConstants; import com.flaremicro.visualforecast.icons.IconProvider; public class HourlyForecastDisplay implements Display { private int dayOffset = 0; private Font font; private Font smallFont; private ForecastDetails details; private TownForecast currentTown = null; private int townIndex; private int ticksBeforeChange = 200; private int animationTicks = -1; private byte[] dewval = null; private byte[] tempval = null; private float[] percipval = null; DateFormat simpleDateFormat = new SimpleDateFormat("ha"); BufferedImage lastBuffer = null; public HourlyForecastDisplay() { font = FontManager.getInstance().getOrCreateFont(Font.TRUETYPE_FONT, this.getClass().getResource("/Star4000.ttf")); smallFont = FontManager.getInstance().getOrCreateFont(Font.TRUETYPE_FONT, this.getClass().getResource("/Star4000 Small.ttf")); } @Override public void tick(RenderPanel renderer, long ticks, int iconTicks) { } public void setGraphValues(TownForecast town) { if (town != null) { HourlyForecast[] forecast = town.getHourlyForecast(); if (forecast != null && forecast.length > 0) { int size = Math.min(12, forecast.length); dewval = new byte[size]; tempval = new byte[size]; percipval = new float[size]; for (int i = 0; i < size; i++) { dewval[i] = forecast[i].dewPoint; tempval[i] = forecast[i].temp; percipval[i] = forecast[i].percip; } } } } @Override public void initDisplay(RenderPanel renderer, ForecastProvider forecastProvider, long ticks, int iconTicks) { this.details = forecastProvider != null ? forecastProvider.getForecast() : null; renderer.setCurrentForecast("12 Hour Forecast"); if (details == null || details.getTownForecast() == null || details.getTownForecast().length <= 0) this.details = null; else { townIndex = 0; currentTown = details.getTownForecast()[townIndex]; renderer.setCurrentTown(currentTown.getTownName()); setGraphValues(currentTown); } redrawRegionlost(renderer); } @Override public void drawDisplay(RenderPanel renderer, Graphics2D g2d, long ticks, int iconTicks) { HourlyForecast[] forecast = currentTown.getHourlyForecast(); if (forecast == null) return; g2d.setColor(RenderConstants.BG_BLUE); //g2d.fillRect(RenderConstants.SIDE_OFFSET, RenderConstants.TOPBAR_HEIGHT + 20, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2, RenderConstants.MAINBAR_HEIGHT - 40); DrawingUtil.drawGradientRect(g2d, RenderConstants.SIDE_OFFSET, RenderConstants.TOPBAR_HEIGHT + 20, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2, RenderConstants.MAINBAR_HEIGHT - 40, 15, BG_BLUE.darker(), BG_BLUE.brighter()); int min = minTemp(forecast); int max = maxTemp(forecast); int range = max - min; if (range == 0) { min -= 1; max += 1; range = 2; } g2d.setPaint(new GradientPaint(0, RenderConstants.TOPBAR_HEIGHT + 40, RenderConstants.BG_ORANGE, 0, RenderConstants.MAINBAR_HEIGHT, RenderConstants.BG_PURPLE)); g2d.fillRect(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + 40, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60, RenderConstants.MAINBAR_HEIGHT - 124); g2d.setColor(Color.BLACK); g2d.drawRect(RenderConstants.SIDE_OFFSET, RenderConstants.TOPBAR_HEIGHT + 20, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2, RenderConstants.MAINBAR_HEIGHT - 40); /*float lineHeight = (RenderConstants.MAINBAR_HEIGHT - 104) / 9F; for(int i = 0; i < 10; i++) { g2d.drawLine(RenderConstants.SIDE_OFFSET + 36, (int)(RenderConstants.TOPBAR_HEIGHT + 40 + lineHeight * i), RenderConstants.W - RenderConstants.SIDE_OFFSET - 36, (int) (RenderConstants.TOPBAR_HEIGHT + 40 + lineHeight * i)); }*/ g2d.setFont(smallFont.deriveFont(20F)); FontMetrics fm = g2d.getFontMetrics(); DrawingUtil.drawOutlinedString(g2d, 100, RenderConstants.TOPBAR_HEIGHT + 35, "Temperature\u00B0c", Color.RED, Color.BLACK, 2); DrawingUtil.drawOutlinedString(g2d, 260, RenderConstants.TOPBAR_HEIGHT + 35, "Dew point\u00B0c", Color.WHITE, Color.BLACK, 2); DrawingUtil.drawOutlinedString(g2d, 410, RenderConstants.TOPBAR_HEIGHT + 35, "Precipitation%", Color.CYAN, Color.BLACK, 2); String minText = min + "\u00B0"; String maxText = max + "\u00B0"; DrawingUtil.drawOutlinedString(g2d, RenderConstants.SIDE_OFFSET + 40 - fm.stringWidth(maxText), RenderConstants.TOPBAR_HEIGHT + 53, maxText, Color.YELLOW, Color.BLACK, 2); DrawingUtil.drawOutlinedString(g2d, RenderConstants.SIDE_OFFSET + 40 - fm.stringWidth(minText), RenderConstants.TOPBAR_HEIGHT + MAINBAR_HEIGHT - 92, minText, Color.YELLOW, Color.BLACK, 2); g2d.setColor(RenderConstants.BG_ORANGE.darker()); g2d.drawLine(RenderConstants.SIDE_OFFSET + 40, (RenderConstants.TOPBAR_HEIGHT + 50), (RenderConstants.W - RenderConstants.SIDE_OFFSET - 20), (RenderConstants.TOPBAR_HEIGHT + 50)); g2d.setColor(RenderConstants.BG_PURPLE.brighter()); g2d.drawLine(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + 50 + RenderConstants.MAINBAR_HEIGHT - 144, (RenderConstants.W - RenderConstants.SIDE_OFFSET - 20), RenderConstants.TOPBAR_HEIGHT + 50 + RenderConstants.MAINBAR_HEIGHT - 144); //BlackLines if (max > 0 && min < 0) { int nextTemp = (RenderConstants.TOPBAR_HEIGHT + RenderConstants.MAINBAR_HEIGHT - 94) + (int) (((min) / (float) range) * (RenderConstants.MAINBAR_HEIGHT - 144)); g2d.setColor(Color.LIGHT_GRAY); g2d.drawLine(RenderConstants.SIDE_OFFSET + 40, nextTemp, RenderConstants.W - RenderConstants.SIDE_OFFSET - 20, nextTemp); DrawingUtil.drawOutlinedString(g2d, RenderConstants.SIDE_OFFSET + 40 - fm.stringWidth("0\u00B0"), nextTemp + 4, "0\u00B0", Color.YELLOW, Color.BLACK, 2); } int slotWidth = (RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60) / 12; g2d.setClip(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + 40, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60, RenderConstants.MAINBAR_HEIGHT - 124); g2d.translate(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + RenderConstants.MAINBAR_HEIGHT - 94); g2d.translate(2, 2); g2d.setColor(Color.BLACK); this.graphTemp(g2d, this.dewval, min, slotWidth, range, RenderConstants.MAINBAR_HEIGHT - 144); g2d.setColor(Color.WHITE); g2d.translate(-2, -2); this.graphTemp(g2d, this.dewval, min, slotWidth, range, RenderConstants.MAINBAR_HEIGHT - 144); g2d.translate(2, 2); g2d.setColor(Color.BLACK); this.graphPercent(g2d, this.percipval, slotWidth, 100F, RenderConstants.MAINBAR_HEIGHT - 144); g2d.setColor(Color.CYAN); g2d.translate(-2, -2); this.graphPercent(g2d, this.percipval, slotWidth, 100F, RenderConstants.MAINBAR_HEIGHT - 144); g2d.translate(2, 2); g2d.setColor(Color.BLACK); this.graphTemp(g2d, this.tempval, min, slotWidth, range, RenderConstants.MAINBAR_HEIGHT - 144); g2d.setColor(Color.RED); g2d.translate(-2, -2); this.graphTemp(g2d, this.tempval, min, slotWidth, range, RenderConstants.MAINBAR_HEIGHT - 144); g2d.translate(-RenderConstants.SIDE_OFFSET - 40, 94 - RenderConstants.TOPBAR_HEIGHT - RenderConstants.MAINBAR_HEIGHT); g2d.setClip(null); g2d.setStroke(new BasicStroke(2)); g2d.setColor(Color.BLACK); g2d.drawRect(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + 40, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60, RenderConstants.MAINBAR_HEIGHT - 124); for (int i = 0; i < Math.min(12, forecast.length); i++) { String timeString = simpleDateFormat.format(forecast[i].hour); if ((i % 2) == 0) DrawingUtil.drawOutlinedString(g2d, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i) - fm.stringWidth(timeString) / 2, RenderConstants.TOPBAR_HEIGHT + MAINBAR_HEIGHT - 70, timeString, Color.YELLOW, Color.BLACK, 2); else DrawingUtil.drawOutlinedString(g2d, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i) - fm.stringWidth(timeString) / 2, RenderConstants.TOPBAR_HEIGHT + MAINBAR_HEIGHT - 60, timeString, Color.YELLOW, Color.BLACK, 2); IconProvider.drawIcon(g2d, IconProvider.INDEXED_ICONS[forecast[i].iconId], RenderConstants.SIDE_OFFSET + 45 + i * slotWidth, RenderConstants.TOPBAR_HEIGHT + MAINBAR_HEIGHT - 55, slotWidth - 10, iconTicks); } lastBuffer = renderer.getSnapshot().getSubimage(RenderConstants.SIDE_OFFSET - 1, RenderConstants.TOPBAR_HEIGHT + 19, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 + 2, RenderConstants.MAINBAR_HEIGHT - 38); lastBuffer.setAccelerationPriority(1); } public int minTemp(HourlyForecast[] forecast) { int min = Integer.MAX_VALUE; for (int i = 0; i < forecast.length; i++) { int currMin; if (ValueCheck.valueNoData(forecast[i].dewPoint) && ValueCheck.valueNoData(forecast[i].temp)) continue; else if (ValueCheck.valueNoData(forecast[i].dewPoint)) currMin = forecast[i].temp; else if (ValueCheck.valueNoData(forecast[i].temp)) currMin = forecast[i].dewPoint; else currMin = Math.min(forecast[i].temp, forecast[i].dewPoint); if (currMin < min) min = currMin; } return min; } public int maxTemp(HourlyForecast[] forecast) { int max = Integer.MIN_VALUE; for (int i = 0; i < forecast.length; i++) { int currMax; if (ValueCheck.valueNoData(forecast[i].dewPoint) && ValueCheck.valueNoData(forecast[i].temp)) continue; else if (ValueCheck.valueNoData(forecast[i].dewPoint)) currMax = forecast[i].temp; else if (ValueCheck.valueNoData(forecast[i].temp)) currMax = forecast[i].dewPoint; else currMax = Math.max(forecast[i].temp, forecast[i].dewPoint); if (currMax > max) max = currMax; } return max; } @Override public void drawBoundLimitedDisplay(RenderPanel renderer, Graphics2D g2d, Rectangle bounds, long ticks, int iconTicks) { HourlyForecast[] forecast = currentTown.getHourlyForecast(); if (forecast == null) return; if (this.lastBuffer != null) { g2d.drawImage(lastBuffer, RenderConstants.SIDE_OFFSET - 1, RenderConstants.TOPBAR_HEIGHT + 19, renderer); } int slotWidth = (RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60) / 12; for (int i = 0; i < Math.min(12, forecast.length); i++) { if (IconProvider.INDEXED_ICONS[forecast[i].iconId].isAnimated()) IconProvider.drawIcon(g2d, IconProvider.INDEXED_ICONS[forecast[i].iconId], RenderConstants.SIDE_OFFSET + 45 + i * slotWidth, RenderConstants.TOPBAR_HEIGHT + MAINBAR_HEIGHT - 55, slotWidth - 10, iconTicks); } } public void graphTemp(Graphics2D g2d, byte[] tempVal, int min, int slotWidth, float range, float multiplier) { int lastTemp = ValueCheck.NO_DATA_INT; for (int i = 0; i < tempVal.length; i++) { if (ValueCheck.valueNoData(tempVal[i])) { lastTemp = ValueCheck.NO_DATA_INT; continue; } int nextTemp = -(int) (((tempVal[i] - min) / range) * multiplier); if (i == 0) { lastTemp = nextTemp; } if (ValueCheck.valueNoData(lastTemp)) { lastTemp = nextTemp; continue; } g2d.drawLine(slotWidth / 2 + (slotWidth * (i - 1)), lastTemp, slotWidth / 2 + (slotWidth * i), nextTemp); if (i == Math.min(12, tempVal.length) - 1) { g2d.drawLine(slotWidth / 2 + (slotWidth * (i)), nextTemp, slotWidth / 2 + (slotWidth * (i + 1)), nextTemp); } lastTemp = nextTemp; } } public void graphPercent(Graphics2D g2d, float[] precVal, int slotWidth, float range, float multiplier) { int lastTemp = ValueCheck.NO_DATA_INT; for (int i = 0; i < tempval.length; i++) { if (ValueCheck.valueNoData(precVal[i])) { lastTemp = ValueCheck.NO_DATA_INT; continue; } int nextTemp = -(int) (((precVal[i]) / range) * multiplier); if (i == 0) { lastTemp = nextTemp; } if (ValueCheck.valueNoData(lastTemp)) { lastTemp = nextTemp; continue; } g2d.drawLine(slotWidth / 2 + (slotWidth * (i - 1)), lastTemp, slotWidth / 2 + (slotWidth * i), nextTemp); if (i == Math.min(12, precVal.length) - 1) { g2d.drawLine(slotWidth / 2 + (slotWidth * (i)), nextTemp, slotWidth / 2 + (slotWidth * (i + 1)), nextTemp); } lastTemp = nextTemp; } } @Override public void redrawRegionlost(RenderPanel renderer) { int slotWidth = (RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60) / 12; renderer.addRedrawBound(RenderConstants.SIDE_OFFSET + 45, RenderConstants.TOPBAR_HEIGHT + MAINBAR_HEIGHT - 55, slotWidth * 12 - 10, slotWidth - 10); } @Override public void notifyForecastProviderUpdate(RenderPanel renderer, ForecastProvider forecastProvider) { } }