From 5628a5b76e768eb1a1ae8b8804b0c5732f1693d7 Mon Sep 17 00:00:00 2001 From: Flare Microsystems Date: Fri, 8 Mar 2024 21:31:49 -0800 Subject: [PATCH 1/4] Added hourly forecast --- CanadaDatamartProvider.jar | Bin 13264 -> 13652 bytes CanadaDatamartProvider.properties | 4 +- MockForecastProvider.jar | Bin 3704 -> 4167 bytes .../visualforecast/RenderPanel.java | 82 +++--- .../visualforecast/VisualForecastFrame.java | 26 +- .../displays/HourlyForecastDisplay.java | 252 ++++++++++++++++++ .../visualforecast/forecast/DayForecast.java | 4 +- .../forecast/HourlyForecast.java | 24 ++ .../visualforecast/forecast/TownForecast.java | 17 +- .../graphics/RenderConstants.java | 2 +- vf1000.properties | 4 +- 11 files changed, 358 insertions(+), 57 deletions(-) create mode 100644 src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java create mode 100644 src/com/flaremicro/visualforecast/forecast/HourlyForecast.java diff --git a/CanadaDatamartProvider.jar b/CanadaDatamartProvider.jar index b4a6838e8d30349d3ee8e8afa1092d21b43ec0b0..d793fc99b48603e972a88440a22fc2738abe9147 100644 GIT binary patch delta 8600 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 Vjq(58#bE`r@=zjtXaCRX{{v?x$kPA- diff --git a/CanadaDatamartProvider.properties b/CanadaDatamartProvider.properties index 0da62c2..21f75b6 100644 --- a/CanadaDatamartProvider.properties +++ b/CanadaDatamartProvider.properties @@ -1,4 +1,4 @@ #VisualForecast 1000 Properties file. Functional provider must be set for successful boot! -#Thu Mar 07 16:27:03 PST 2024 +#Fri Mar 08 16:38:56 PST 2024 towns-by-code= -towns-by-name-and-province=Vancouver,BC;Kamloops,BC;Kelowna,BC +towns-by-name-and-province=Duncan,BC;Halifax,NS;Courtenay,BC;Comox,BC diff --git a/MockForecastProvider.jar b/MockForecastProvider.jar index d6144df5f398dae5281bfedd1dbc5022f4f103be..08e10bea9e23e700a98489dca12930b3e21b9883 100644 GIT binary patch delta 3524 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 diff --git a/src/com/flaremicro/visualforecast/RenderPanel.java b/src/com/flaremicro/visualforecast/RenderPanel.java index 37ecee8..3834f14 100644 --- a/src/com/flaremicro/visualforecast/RenderPanel.java +++ b/src/com/flaremicro/visualforecast/RenderPanel.java @@ -29,6 +29,7 @@ import com.flaremicro.visualforecast.api.ForecastProvider; import com.flaremicro.visualforecast.displays.BootupDisplay; import com.flaremicro.visualforecast.displays.DayForecastDisplay; import com.flaremicro.visualforecast.displays.Display; +import com.flaremicro.visualforecast.displays.HourlyForecastDisplay; import com.flaremicro.visualforecast.forecast.ForecastDetails; import com.flaremicro.visualforecast.graphics.DrawingUtil; import com.flaremicro.visualforecast.graphics.FontManager; @@ -54,6 +55,8 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { private Rectangle exclusiveRedrawBound = null; private Rectangle crawlBound = null; + private Rectangle currentBound = new Rectangle(0, 0, 0, 0); + private String currentTown = ""; private String currentForecast = ""; @@ -109,30 +112,8 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = prepareFrameBuffer(); - - - if(g.getClipBounds().equals(this.crawlBound)) - { - g2d.setColor(BG_BLUE); - g2d.fillRect(0, H - INFOBAR_HEIGHT, W, INFOBAR_HEIGHT); - - g2d.setColor(Color.DARK_GRAY); - g2d.drawLine(0, H - INFOBAR_HEIGHT + STROKE_OFFSET, W, H - INFOBAR_HEIGHT + STROKE_OFFSET); - - g2d.setColor(Color.WHITE); - g2d.drawLine(0, H - INFOBAR_HEIGHT + STROKE_WIDTH + STROKE_OFFSET, W, H - INFOBAR_HEIGHT + STROKE_WIDTH + STROKE_OFFSET); - if (this.currentCrawlString != null) - { - g2d.setFont(font.deriveFont(26F)); - DrawingUtil.drawOutlinedString(g2d, this.crawlPosition, H - INFOBAR_HEIGHT + 30, this.currentCrawlString, Color.WHITE, Color.BLACK, 2); - } - g2d.dispose(); - g.drawImage(frameBuffer, 0, 0, getWidth(), getHeight(), this); - return; - } - drawMainRegion(g2d); - + if (currentFlavour != null) { if (this.getBounds().equals(g.getClipBounds())) @@ -152,7 +133,7 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { g2d.setColor(BG_PURPLE); g2d.fillRect(0, 0, W, TOPBAR_HEIGHT); - g2d.setPaint(new GradientPaint(0, HEADERBAR_Y, BG_OORANGE, 0, HEADERBAR_Y + (HEADERBAR_HEIGHT + 10), BG_PURPLE)); + g2d.setPaint(new GradientPaint(0, HEADERBAR_Y, BG_ORANGE, 0, HEADERBAR_Y + (HEADERBAR_HEIGHT + 10), BG_PURPLE)); g2d.shear(HEADERBAR_SHEAR, 0); g2d.fillRect(-HEADERBAR_OFFSET, HEADERBAR_Y, HEADERBAR_WIDTH, HEADERBAR_HEIGHT); @@ -160,7 +141,7 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { g2d.fillRect(W - TIMEBAR_WIDTH + TIMEBAR_OFFSET, TIMEBAR_Y, TIMEBAR_WIDTH, TIMEBAR_HEIGHT); g2d.shear(-HEADERBAR_SHEAR, 0); - g2d.setPaint(new GradientPaint(0, TOPBAR_HEIGHT, BG_PURPLE, 0, MAINBAR_HEIGHT, BG_OORANGE)); + g2d.setPaint(new GradientPaint(0, TOPBAR_HEIGHT, BG_PURPLE, 0, MAINBAR_HEIGHT, BG_ORANGE)); g2d.fillRect(0, TOPBAR_HEIGHT, W, MAINBAR_HEIGHT); g2d.fillRect(0, TOPBAR_HEIGHT, W, MAINBAR_HEIGHT); @@ -177,7 +158,7 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { DrawingUtil.drawOutlinedString(g2d, W - sw - 60, TIMEBAR_Y + 36, timeString, Color.WHITE, Color.BLACK, 2); g2d.setFont(font.deriveFont(36F)); DrawingUtil.drawOutlinedString(g2d, 60, HEADERBAR_Y + 52, currentTown, Color.YELLOW, Color.BLACK, 2); - + g2d.setColor(BG_BLUE); g2d.fillRect(0, H - INFOBAR_HEIGHT, W, INFOBAR_HEIGHT); @@ -202,7 +183,7 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { //Clock and icon animations, should use repaint regions later if (getWidth() > 0 && getHeight() > 0) { - repaint(this.redrawBound); + requestRepaint(this.redrawBound); } } if (this.currentFlavour != null) @@ -218,10 +199,37 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { this.currentCrawlStringWidth = g.getFontMetrics(font.deriveFont(26F)).stringWidth(currentCrawlString); g.dispose(); } - else if (this.currentCrawlString != null) + } + + public void requestFullRepaint() { + currentBound = this.getBounds(); + } + + + public void requestRepaint(Rectangle bound) { + if(currentBound.width == 0) + currentBound = new Rectangle(bound); + else currentBound.add(bound); + } + + public void requestBoundedRepaint() { + requestRepaint(this.redrawBound); + } + + public void requestExclusiveBoundedRepaint() { + requestRepaint(exclusiveRedrawBound); + } + + public void performPaintIfNeeded() { + if (this.currentCrawlString != null) { - this.crawlPosition-=2; - repaint(0, this.crawlBound.x, this.crawlBound.y, this.crawlBound.width, this.crawlBound.height); + this.crawlPosition -= 2; + requestRepaint(crawlBound); + } + if(this.currentBound.width != 0) + { + repaint(currentBound); + currentBound.width = 0; } } @@ -293,7 +301,7 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { } public void nextDisplay() { - this.currentFlavour = new DayForecastDisplay(); + this.currentFlavour = new HourlyForecastDisplay(); this.currentFlavour.initDisplay(this, forecastProvider, ticks, iconAnimationTicks); this.loseRedrawRegion(); this.requestFullRepaint(); @@ -307,18 +315,6 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { this.currentForecast = currentForecast; } - public void requestFullRepaint() { - repaint(); - } - - public void requestBoundedRepaint() { - repaint(redrawBound); - } - - public void requestExclusiveBoundedRepaint() { - repaint(exclusiveRedrawBound); - } - private void loadCrawlStrings() { File crawl = new File("./crawl.txt"); ArrayList strings = new ArrayList(); diff --git a/src/com/flaremicro/visualforecast/VisualForecastFrame.java b/src/com/flaremicro/visualforecast/VisualForecastFrame.java index a12df52..b2bd384 100644 --- a/src/com/flaremicro/visualforecast/VisualForecastFrame.java +++ b/src/com/flaremicro/visualforecast/VisualForecastFrame.java @@ -2,6 +2,8 @@ package com.flaremicro.visualforecast; import java.awt.BorderLayout; import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowEvent; @@ -9,6 +11,7 @@ import java.awt.event.WindowListener; import java.io.File; import javax.swing.JFrame; +import javax.swing.Timer; import com.flaremicro.visualforecast.api.ForecastProvider; import com.flaremicro.visualforecast.api.ForecastProviderManager; @@ -23,6 +26,8 @@ public class VisualForecastFrame extends JFrame implements WindowListener, KeyLi private ForecastProviderManager forecastProviderManager; private Executor executor; private PropertyManager propertyManager = new PropertyManager(); + private Timer timer; + private Timer timer2; private boolean isFullscreen = false; /** @@ -37,7 +42,7 @@ public class VisualForecastFrame extends JFrame implements WindowListener, KeyLi frame.setVisible(true); frame.init(); //GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[1].setFullScreenWindow(frame); - frame.createBufferStrategy(2); + //frame.createBufferStrategy(2); } catch (Exception e) { @@ -50,6 +55,10 @@ public class VisualForecastFrame extends JFrame implements WindowListener, KeyLi public void end() { if (executor != null) executor.end(); + if (timer != null) + { + timer.stop(); + } if (forecastProviderManager != null) forecastProviderManager.end(); propertyManager.store(); @@ -58,6 +67,14 @@ public class VisualForecastFrame extends JFrame implements WindowListener, KeyLi public void init() { executor = new Executor(this.renderPane, 30); executor.begin(); + timer = new Timer(33, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + renderPane.performPaintIfNeeded(); + } + }); + timer.start(); + new Thread() { public void run() { @@ -67,6 +84,7 @@ public class VisualForecastFrame extends JFrame implements WindowListener, KeyLi propertyManager.store(); ForecastProvider provider = forecastProviderManager.loadProvider(new File(forecastProvider)); renderPane.setForecastProvider(provider); + } }.start(); @@ -79,9 +97,9 @@ public class VisualForecastFrame extends JFrame implements WindowListener, KeyLi setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setBounds(100, 100, 640 * 2, 480 * 2); renderPane = new RenderPanel(propertyManager); - renderPane.setBorder(null); - renderPane.setLayout(new BorderLayout(0, 0)); - setContentPane(renderPane); + //renderPane.setBorder(null); + //renderPane.setLayout(new BorderLayout(0, 0)); + add(renderPane); setUndecorated(true); addWindowListener(this); addKeyListener(this); diff --git a/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java b/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java new file mode 100644 index 0000000..7940630 --- /dev/null +++ b/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java @@ -0,0 +1,252 @@ +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.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; + + DateFormat simpleDateFormat = new SimpleDateFormat("ha"); + + 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) { + + } + + @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()); + } + redrawRegionlost(renderer); + } + + @Override + public void drawDisplay(RenderPanel renderer, Graphics2D g2d, long ticks, int iconTicks) { + } + + 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; + + 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); + + float range = Math.abs(min)+Math.abs(max); + + + + 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); + + int slotWidth = (RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60) / 12; + + int lastDewpoint = 0; + int lastTemperature = 0; + int lastPercip = 0; + + 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 + g2d.setClip(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + 40, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60, RenderConstants.MAINBAR_HEIGHT - 124); + g2d.setStroke(new BasicStroke(2)); + g2d.setColor(Color.BLACK); + for(int i = 0; i < Math.min(12, forecast.length); i++) + { + int nextDewpoint = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int)(((max-forecast[i].dewPoint)/range)*(RenderConstants.MAINBAR_HEIGHT - 144)); + int nextTemperature = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int)(((max-forecast[i].temp)/range)*(RenderConstants.MAINBAR_HEIGHT - 144)); + int nextPercip = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) ((forecast[i].percip/100F)*(RenderConstants.MAINBAR_HEIGHT - 144)); + if(i == 0) + { + lastDewpoint = nextDewpoint; + lastTemperature = nextTemperature; + lastPercip = nextPercip; + } + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1))+1, lastDewpoint+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i)+2, nextDewpoint+2); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1))+1, lastPercip+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i)+2, nextPercip+2); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1))+1, lastTemperature+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i)+2, nextTemperature+2); + if(i == Math.min(12, forecast.length)-1) + { + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextDewpoint+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+2)), nextDewpoint+2); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextPercip+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+2)), nextPercip+2); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextTemperature+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+2)), nextTemperature+2); + } + lastPercip = nextPercip; + lastTemperature = nextTemperature; + lastDewpoint = nextDewpoint; + } + g2d.setClip(null); + + for(int i = 0; i < Math.min(12, forecast.length); i++) + { + g2d.setClip(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + 40, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60, RenderConstants.MAINBAR_HEIGHT - 124); + int nextDewpoint = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int)(((max-forecast[i].dewPoint)/range)*(RenderConstants.MAINBAR_HEIGHT - 144)); + int nextTemperature = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int)(((max-forecast[i].temp)/range)*(RenderConstants.MAINBAR_HEIGHT - 144)); + int nextPercip = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) ((forecast[i].percip/100F)*(RenderConstants.MAINBAR_HEIGHT - 144)); + if(i == 0) + { + lastDewpoint = nextDewpoint; + lastTemperature = nextTemperature; + lastPercip = nextPercip; + } + g2d.setStroke(new BasicStroke(2)); + g2d.setColor(Color.WHITE); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1)), lastDewpoint, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i), nextDewpoint); + g2d.setColor(Color.CYAN); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1)), lastPercip, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i), nextPercip); + g2d.setColor(Color.RED); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1)), lastTemperature, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i), nextTemperature); + if(i == Math.min(12, forecast.length)-1) + { + g2d.setColor(Color.WHITE); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextDewpoint, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+1)), nextDewpoint); + g2d.setColor(Color.CYAN); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextPercip, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+1)), nextPercip); + g2d.setColor(Color.RED); + g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextTemperature, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+1)), nextTemperature); + } + + g2d.setClip(null); + lastPercip = nextPercip; + lastTemperature = nextTemperature; + lastDewpoint = nextDewpoint; + + 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); + } + 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); + + } + + @Override + public void redrawRegionlost(RenderPanel renderer) { + renderer.addRedrawBound(0, RenderConstants.TOPBAR_HEIGHT, RenderConstants.W, RenderConstants.MAINBAR_HEIGHT); + } + + @Override + public void notifyForecastProviderUpdate(RenderPanel renderer, ForecastProvider forecastProvider) { + + } +} diff --git a/src/com/flaremicro/visualforecast/forecast/DayForecast.java b/src/com/flaremicro/visualforecast/forecast/DayForecast.java index 5a68f71..ab3c612 100644 --- a/src/com/flaremicro/visualforecast/forecast/DayForecast.java +++ b/src/com/flaremicro/visualforecast/forecast/DayForecast.java @@ -1,5 +1,7 @@ package com.flaremicro.visualforecast.forecast; +import com.flaremicro.visualforecast.icons.IconProvider; + public class DayForecast { public byte hiTemp; public byte loTemp; @@ -11,7 +13,7 @@ public class DayForecast { public DayForecast(byte hiTemp, byte loTemp, byte iconId, String weatherLine1, String weatherLine2, float percipPercent){ this.hiTemp = hiTemp; this.loTemp = loTemp; - this.iconId = (byte)(iconId & 63); + this.iconId = (byte)(iconId % IconProvider.INDEXED_ICONS.length); this.weatherLine1 = weatherLine1; this.weatherLine2 = weatherLine2; this.percipPercent = percipPercent; diff --git a/src/com/flaremicro/visualforecast/forecast/HourlyForecast.java b/src/com/flaremicro/visualforecast/forecast/HourlyForecast.java new file mode 100644 index 0000000..43f8f26 --- /dev/null +++ b/src/com/flaremicro/visualforecast/forecast/HourlyForecast.java @@ -0,0 +1,24 @@ +package com.flaremicro.visualforecast.forecast; + +import java.util.Date; + +import com.flaremicro.visualforecast.icons.IconProvider; + +public class HourlyForecast { + public final Date hour; + public final byte iconId; + public final byte temp; + public final short windSpeed; + public final float percip; + public final byte dewPoint; + + public HourlyForecast(Date hour, byte iconId, byte temp, short windSpeed, float percip, byte dewPoint) + { + this.hour = hour; + this.iconId = (byte) (iconId % IconProvider.INDEXED_ICONS.length); + this.temp = temp; + this.windSpeed = windSpeed; + this.percip = percip; + this.dewPoint = dewPoint; + } +} diff --git a/src/com/flaremicro/visualforecast/forecast/TownForecast.java b/src/com/flaremicro/visualforecast/forecast/TownForecast.java index 553ff45..49d31af 100644 --- a/src/com/flaremicro/visualforecast/forecast/TownForecast.java +++ b/src/com/flaremicro/visualforecast/forecast/TownForecast.java @@ -1,18 +1,27 @@ package com.flaremicro.visualforecast.forecast; public class TownForecast { - public TownForecast(String townName, DayForecast[] dayForecast){ + public TownForecast(String townName, DayForecast[] dayForecast) { this.townName = townName; this.dayForecast = dayForecast; } - + private final String townName; private final DayForecast[] dayForecast; - + private HourlyForecast[] hourlyForecast; + + public void setHourlyForecast(HourlyForecast[] forecast) { + this.hourlyForecast = forecast; + } + + public HourlyForecast[] getHourlyForecast() { + return this.hourlyForecast; + } + public DayForecast[] getDayForecast() { return dayForecast; } - + public String getTownName() { return townName; } diff --git a/src/com/flaremicro/visualforecast/graphics/RenderConstants.java b/src/com/flaremicro/visualforecast/graphics/RenderConstants.java index 4e033d8..828faeb 100644 --- a/src/com/flaremicro/visualforecast/graphics/RenderConstants.java +++ b/src/com/flaremicro/visualforecast/graphics/RenderConstants.java @@ -31,6 +31,6 @@ public class RenderConstants { public static final Color BG_PURPLE = new Color(0x2b29b3); - public static final Color BG_OORANGE = new Color(0xff8c00); + public static final Color BG_ORANGE = new Color(0xff8c00); public static final Color BG_BLUE = new Color(0x394aa8); } diff --git a/vf1000.properties b/vf1000.properties index 871d714..0c82524 100644 --- a/vf1000.properties +++ b/vf1000.properties @@ -1,3 +1,3 @@ #VisualForecast 1000 Properties file. Functional provider must be set for successful boot! -#Thu Mar 07 16:27:03 PST 2024 -forecast-provider-jar=CanadaDatamartProvider.jar +#Fri Mar 08 21:30:25 PST 2024 +forecast-provider-jar=MockForecastProvider.jar -- 2.39.5 From 28e19dd91c70fcdcdc4c522a16ccd73632c04b46 Mon Sep 17 00:00:00 2001 From: Flare Microsystems Date: Sat, 9 Mar 2024 23:35:03 -0800 Subject: [PATCH 2/4] Completed graphing --- MockForecastProvider.jar | Bin 4167 -> 4787 bytes .../displays/HourlyForecastDisplay.java | 247 ++++++++++++++---- vf1000.properties | 2 +- 3 files changed, 191 insertions(+), 58 deletions(-) diff --git a/MockForecastProvider.jar b/MockForecastProvider.jar index 08e10bea9e23e700a98489dca12930b3e21b9883..ebbf2aee0ee1cd3d9cf9b6bed887003edb4136a2 100644 GIT binary patch delta 3709 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 aI;|>^5gme^j)R7jfRte7QaeHSTlhc0^S}}S delta 3064 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 G0002DUc~VL diff --git a/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java b/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java index 7940630..af723fc 100644 --- a/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java +++ b/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java @@ -9,6 +9,7 @@ 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; @@ -39,7 +40,11 @@ public class HourlyForecastDisplay implements Display { 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"); public HourlyForecastDisplay() { @@ -52,6 +57,26 @@ public class HourlyForecastDisplay implements Display { } + 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; @@ -63,6 +88,7 @@ public class HourlyForecastDisplay implements Display { townIndex = 0; currentTown = details.getTownForecast()[townIndex]; renderer.setCurrentTown(currentTown.getTownName()); + setGraphValues(currentTown); } redrawRegionlost(renderer); } @@ -110,27 +136,32 @@ public class HourlyForecastDisplay implements Display { @Override public void drawBoundLimitedDisplay(RenderPanel renderer, Graphics2D g2d, Rectangle bounds, long ticks, int iconTicks) { HourlyForecast[] forecast = currentTown.getHourlyForecast(); - if(forecast == null) + 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); - - float range = Math.abs(min)+Math.abs(max); - - + + 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++) { @@ -138,67 +169,103 @@ public class HourlyForecastDisplay implements Display { }*/ 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); + 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; - int lastDewpoint = 0; - int lastTemperature = 0; - int lastPercip = 0; - - 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 g2d.setClip(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + 40, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60, RenderConstants.MAINBAR_HEIGHT - 124); - g2d.setStroke(new BasicStroke(2)); + + g2d.translate(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + RenderConstants.MAINBAR_HEIGHT - 94); + + g2d.translate(2, 2); g2d.setColor(Color.BLACK); - for(int i = 0; i < Math.min(12, forecast.length); i++) + 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); + + //This is so ugly + for (int i = 0; i < Math.min(12, forecast.length); i++) { - int nextDewpoint = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int)(((max-forecast[i].dewPoint)/range)*(RenderConstants.MAINBAR_HEIGHT - 144)); - int nextTemperature = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int)(((max-forecast[i].temp)/range)*(RenderConstants.MAINBAR_HEIGHT - 144)); - int nextPercip = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) ((forecast[i].percip/100F)*(RenderConstants.MAINBAR_HEIGHT - 144)); - if(i == 0) + int nextDewpoint = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) (((max - forecast[i].dewPoint) / range) * (RenderConstants.MAINBAR_HEIGHT - 144)); + int nextTemperature = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) (((max - forecast[i].temp) / range) * (RenderConstants.MAINBAR_HEIGHT - 144)); + int nextPercip = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) ((forecast[i].percip / 100F) * (RenderConstants.MAINBAR_HEIGHT - 144)); + if (i == 0) { lastDewpoint = nextDewpoint; lastTemperature = nextTemperature; lastPercip = nextPercip; } - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1))+1, lastDewpoint+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i)+2, nextDewpoint+2); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1))+1, lastPercip+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i)+2, nextPercip+2); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1))+1, lastTemperature+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i)+2, nextTemperature+2); - if(i == Math.min(12, forecast.length)-1) + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)) + 2, lastDewpoint + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i) + 2, nextDewpoint + 2); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)) + 2, lastPercip + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i) + 2, nextPercip + 2); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)) + 2, lastTemperature + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i) + 2, nextTemperature + 2); + if (i == Math.min(12, forecast.length) - 1) { - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextDewpoint+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+2)), nextDewpoint+2); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextPercip+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+2)), nextPercip+2); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextTemperature+2, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+2)), nextTemperature+2); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)) + 2, nextDewpoint + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 2)) + 2, nextDewpoint + 2); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)) + 2, nextPercip + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 2)) + 2, nextPercip + 2); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)) + 2, nextTemperature + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 2)) + 2, nextTemperature + 2); } lastPercip = nextPercip; lastTemperature = nextTemperature; lastDewpoint = nextDewpoint; } g2d.setClip(null); - - for(int i = 0; i < Math.min(12, forecast.length); i++) + + for (int i = 0; i < Math.min(12, forecast.length); i++) { g2d.setClip(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + 40, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60, RenderConstants.MAINBAR_HEIGHT - 124); - int nextDewpoint = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int)(((max-forecast[i].dewPoint)/range)*(RenderConstants.MAINBAR_HEIGHT - 144)); - int nextTemperature = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int)(((max-forecast[i].temp)/range)*(RenderConstants.MAINBAR_HEIGHT - 144)); - int nextPercip = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) ((forecast[i].percip/100F)*(RenderConstants.MAINBAR_HEIGHT - 144)); - if(i == 0) + int nextDewpoint = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) (((max - forecast[i].dewPoint) / range) * (RenderConstants.MAINBAR_HEIGHT - 144)); + int nextTemperature = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) (((max - forecast[i].temp) / range) * (RenderConstants.MAINBAR_HEIGHT - 144)); + int nextPercip = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) ((forecast[i].percip / 100F) * (RenderConstants.MAINBAR_HEIGHT - 144)); + if (i == 0) { lastDewpoint = nextDewpoint; lastTemperature = nextTemperature; @@ -206,38 +273,104 @@ public class HourlyForecastDisplay implements Display { } g2d.setStroke(new BasicStroke(2)); g2d.setColor(Color.WHITE); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1)), lastDewpoint, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i), nextDewpoint); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)), lastDewpoint, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i), nextDewpoint); g2d.setColor(Color.CYAN); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1)), lastPercip, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i), nextPercip); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)), lastPercip, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i), nextPercip); g2d.setColor(Color.RED); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i-1)), lastTemperature, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*i), nextTemperature); - if(i == Math.min(12, forecast.length)-1) + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)), lastTemperature, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i), nextTemperature); + if (i == Math.min(12, forecast.length) - 1) { g2d.setColor(Color.WHITE); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextDewpoint, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+1)), nextDewpoint); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)), nextDewpoint, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 1)), nextDewpoint); g2d.setColor(Color.CYAN); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextPercip, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+1)), nextPercip); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)), nextPercip, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 1)), nextPercip); g2d.setColor(Color.RED); - g2d.drawLine(RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i)), nextTemperature, RenderConstants.SIDE_OFFSET+40+slotWidth/2+(slotWidth*(i+1)), nextTemperature); + g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)), nextTemperature, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 1)), nextTemperature); } - + g2d.setClip(null); lastPercip = nextPercip; lastTemperature = nextTemperature; lastDewpoint = nextDewpoint; - + 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); + 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); } + */ 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); + } + + } + + 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 diff --git a/vf1000.properties b/vf1000.properties index 0c82524..eb0a101 100644 --- a/vf1000.properties +++ b/vf1000.properties @@ -1,3 +1,3 @@ #VisualForecast 1000 Properties file. Functional provider must be set for successful boot! -#Fri Mar 08 21:30:25 PST 2024 +#Sat Mar 09 23:33:26 PST 2024 forecast-provider-jar=MockForecastProvider.jar -- 2.39.5 From fee43c3ee26858c192e55c7236a94b58300a7ba5 Mon Sep 17 00:00:00 2001 From: Flare Microsystems Date: Sat, 9 Mar 2024 23:50:47 -0800 Subject: [PATCH 3/4] Optimized --- .../displays/HourlyForecastDisplay.java | 290 +++++++----------- vf1000.properties | 2 +- 2 files changed, 117 insertions(+), 175 deletions(-) diff --git a/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java b/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java index af723fc..0685e38 100644 --- a/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java +++ b/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java @@ -47,6 +47,8 @@ public class HourlyForecastDisplay implements Display { 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")); @@ -95,6 +97,112 @@ public class HourlyForecastDisplay implements Display { @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) { @@ -138,183 +246,16 @@ public class HourlyForecastDisplay implements Display { 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) + if (this.lastBuffer != null) { - min -= 1; - max += 1; - range = 2; + g2d.drawImage(lastBuffer, RenderConstants.SIDE_OFFSET - 1, RenderConstants.TOPBAR_HEIGHT + 19, renderer); } - - 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); - - //This is so ugly for (int i = 0; i < Math.min(12, forecast.length); i++) { - int nextDewpoint = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) (((max - forecast[i].dewPoint) / range) * (RenderConstants.MAINBAR_HEIGHT - 144)); - int nextTemperature = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) (((max - forecast[i].temp) / range) * (RenderConstants.MAINBAR_HEIGHT - 144)); - int nextPercip = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) ((forecast[i].percip / 100F) * (RenderConstants.MAINBAR_HEIGHT - 144)); - if (i == 0) - { - lastDewpoint = nextDewpoint; - lastTemperature = nextTemperature; - lastPercip = nextPercip; - } - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)) + 2, lastDewpoint + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i) + 2, nextDewpoint + 2); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)) + 2, lastPercip + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i) + 2, nextPercip + 2); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)) + 2, lastTemperature + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i) + 2, nextTemperature + 2); - if (i == Math.min(12, forecast.length) - 1) - { - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)) + 2, nextDewpoint + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 2)) + 2, nextDewpoint + 2); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)) + 2, nextPercip + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 2)) + 2, nextPercip + 2); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)) + 2, nextTemperature + 2, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 2)) + 2, nextTemperature + 2); - } - lastPercip = nextPercip; - lastTemperature = nextTemperature; - lastDewpoint = nextDewpoint; + 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); } - g2d.setClip(null); - - for (int i = 0; i < Math.min(12, forecast.length); i++) - { - g2d.setClip(RenderConstants.SIDE_OFFSET + 40, RenderConstants.TOPBAR_HEIGHT + 40, RenderConstants.W - RenderConstants.SIDE_OFFSET * 2 - 60, RenderConstants.MAINBAR_HEIGHT - 124); - int nextDewpoint = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) (((max - forecast[i].dewPoint) / range) * (RenderConstants.MAINBAR_HEIGHT - 144)); - int nextTemperature = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) (((max - forecast[i].temp) / range) * (RenderConstants.MAINBAR_HEIGHT - 144)); - int nextPercip = (RenderConstants.TOPBAR_HEIGHT + 50) + (RenderConstants.MAINBAR_HEIGHT - 144) - (int) ((forecast[i].percip / 100F) * (RenderConstants.MAINBAR_HEIGHT - 144)); - if (i == 0) - { - lastDewpoint = nextDewpoint; - lastTemperature = nextTemperature; - lastPercip = nextPercip; - } - g2d.setStroke(new BasicStroke(2)); - g2d.setColor(Color.WHITE); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)), lastDewpoint, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i), nextDewpoint); - g2d.setColor(Color.CYAN); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)), lastPercip, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i), nextPercip); - g2d.setColor(Color.RED); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i - 1)), lastTemperature, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * i), nextTemperature); - if (i == Math.min(12, forecast.length) - 1) - { - g2d.setColor(Color.WHITE); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)), nextDewpoint, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 1)), nextDewpoint); - g2d.setColor(Color.CYAN); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)), nextPercip, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 1)), nextPercip); - g2d.setColor(Color.RED); - g2d.drawLine(RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i)), nextTemperature, RenderConstants.SIDE_OFFSET + 40 + slotWidth / 2 + (slotWidth * (i + 1)), nextTemperature); - } - - g2d.setClip(null); - lastPercip = nextPercip; - lastTemperature = nextTemperature; - lastDewpoint = nextDewpoint; - - 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); - } - */ - 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); - } - } public void graphTemp(Graphics2D g2d, byte[] tempVal, int min, int slotWidth, float range, float multiplier) { @@ -326,7 +267,7 @@ public class HourlyForecastDisplay implements Display { lastTemp = ValueCheck.NO_DATA_INT; continue; } - int nextTemp = -(int) (((tempVal[i]-min) / range) * multiplier); + int nextTemp = -(int) (((tempVal[i] - min) / range) * multiplier); if (i == 0) { lastTemp = nextTemp; @@ -344,7 +285,7 @@ public class HourlyForecastDisplay implements Display { 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++) @@ -375,7 +316,8 @@ public class HourlyForecastDisplay implements Display { @Override public void redrawRegionlost(RenderPanel renderer) { - renderer.addRedrawBound(0, RenderConstants.TOPBAR_HEIGHT, RenderConstants.W, RenderConstants.MAINBAR_HEIGHT); + 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 diff --git a/vf1000.properties b/vf1000.properties index eb0a101..c509e50 100644 --- a/vf1000.properties +++ b/vf1000.properties @@ -1,3 +1,3 @@ #VisualForecast 1000 Properties file. Functional provider must be set for successful boot! -#Sat Mar 09 23:33:26 PST 2024 +#Sat Mar 09 23:49:43 PST 2024 forecast-provider-jar=MockForecastProvider.jar -- 2.39.5 From 0b4ca18c69e97760ca0ffe575fa9e4b230c1a1f3 Mon Sep 17 00:00:00 2001 From: Flare Microsystems Date: Fri, 15 Mar 2024 23:45:35 -0700 Subject: [PATCH 4/4] Added hourly forecast and message forecast --- CanadaDatamartProvider.jar | Bin 13652 -> 15055 bytes CanadaDatamartProvider.properties | 4 +- .../visualforecast/RenderPanel.java | 14 +- .../visualforecast/VisualForecastFrame.java | 31 +++- .../displays/CurrentForecastDisplay.java | 175 ++++++++++++++++++ .../displays/DisplayFactory.java | 32 ++++ .../displays/HourlyForecastDisplay.java | 56 ++++-- .../displays/MessageForecastDisplay.java | 175 ++++++++++++++++++ .../displays/ThirtySixHourDisplay.java | 155 ++++++++++++++++ .../forecast/HourlyForecast.java | 6 +- .../visualforecast/forecast/TownForecast.java | 22 +++ vf1000.properties | 4 +- 12 files changed, 646 insertions(+), 28 deletions(-) create mode 100644 src/com/flaremicro/visualforecast/displays/CurrentForecastDisplay.java create mode 100644 src/com/flaremicro/visualforecast/displays/DisplayFactory.java create mode 100644 src/com/flaremicro/visualforecast/displays/MessageForecastDisplay.java create mode 100644 src/com/flaremicro/visualforecast/displays/ThirtySixHourDisplay.java diff --git a/CanadaDatamartProvider.jar b/CanadaDatamartProvider.jar index d793fc99b48603e972a88440a22fc2738abe9147..2c9f0d6b59d4a80b10f52312a97ee349427e5e79 100644 GIT binary patch delta 10813 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 diff --git a/CanadaDatamartProvider.properties b/CanadaDatamartProvider.properties index 21f75b6..d0ca5fa 100644 --- a/CanadaDatamartProvider.properties +++ b/CanadaDatamartProvider.properties @@ -1,4 +1,4 @@ #VisualForecast 1000 Properties file. Functional provider must be set for successful boot! -#Fri Mar 08 16:38:56 PST 2024 +#Fri Mar 15 19:46:22 PDT 2024 towns-by-code= -towns-by-name-and-province=Duncan,BC;Halifax,NS;Courtenay,BC;Comox,BC +towns-by-name-and-province=Vancouver,BC;Victora,BC;Kelowna,BC;Nanaimo,BC;Squamish,BC;Prince George,BC;Prince Rupert,BC diff --git a/src/com/flaremicro/visualforecast/RenderPanel.java b/src/com/flaremicro/visualforecast/RenderPanel.java index 3834f14..934bf69 100644 --- a/src/com/flaremicro/visualforecast/RenderPanel.java +++ b/src/com/flaremicro/visualforecast/RenderPanel.java @@ -14,7 +14,6 @@ import java.awt.image.BufferedImage; import java.awt.image.VolatileImage; import java.io.BufferedReader; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; @@ -29,7 +28,9 @@ import com.flaremicro.visualforecast.api.ForecastProvider; import com.flaremicro.visualforecast.displays.BootupDisplay; import com.flaremicro.visualforecast.displays.DayForecastDisplay; import com.flaremicro.visualforecast.displays.Display; +import com.flaremicro.visualforecast.displays.DisplayFactory; import com.flaremicro.visualforecast.displays.HourlyForecastDisplay; +import com.flaremicro.visualforecast.displays.ThirtySixHourDisplay; import com.flaremicro.visualforecast.forecast.ForecastDetails; import com.flaremicro.visualforecast.graphics.DrawingUtil; import com.flaremicro.visualforecast.graphics.FontManager; @@ -69,6 +70,8 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { private int crawlPosition = 0; private int stringIndex = -1; private int currentCrawlStringWidth = 0; + + DisplayFactory displayFactory; public RenderPanel(PropertyManager propManager) { loadCrawlStrings(); @@ -77,6 +80,7 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { this.propManager = propManager; font = FontManager.getInstance().getOrCreateFont(Font.TRUETYPE_FONT, this.getClass().getResource("/Star4000.ttf")); currentFlavour.initDisplay(this, null, ticks, iconAnimationTicks); + displayFactory = new DisplayFactory(propManager); //new Thread(new TickThread(this, 30)).start(); } @@ -301,7 +305,13 @@ public class RenderPanel extends JPanel implements Tickable, ComponentListener { } public void nextDisplay() { - this.currentFlavour = new HourlyForecastDisplay(); + /*if(this.currentFlavour instanceof DayForecastDisplay) + this.currentFlavour = new HourlyForecastDisplay(); + else + this.currentFlavour = new DayForecastDisplay();*/ + this.currentFlavour = displayFactory.nextDisplay(); + /*this.currentFlavour = new ThirtySixHourDisplay(); + */ this.currentFlavour.initDisplay(this, forecastProvider, ticks, iconAnimationTicks); this.loseRedrawRegion(); this.requestFullRepaint(); diff --git a/src/com/flaremicro/visualforecast/VisualForecastFrame.java b/src/com/flaremicro/visualforecast/VisualForecastFrame.java index b2bd384..68092f5 100644 --- a/src/com/flaremicro/visualforecast/VisualForecastFrame.java +++ b/src/com/flaremicro/visualforecast/VisualForecastFrame.java @@ -1,7 +1,8 @@ package com.flaremicro.visualforecast; -import java.awt.BorderLayout; import java.awt.EventQueue; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -9,10 +10,13 @@ import java.awt.event.KeyListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; +import java.util.Map; import javax.swing.JFrame; +import javax.swing.JOptionPane; import javax.swing.Timer; +import com.flaremicro.util.Util; import com.flaremicro.visualforecast.api.ForecastProvider; import com.flaremicro.visualforecast.api.ForecastProviderManager; @@ -34,6 +38,7 @@ public class VisualForecastFrame extends JFrame implements WindowListener, KeyLi * Launch the application. */ public static void main(String[] args) { + final Map arguments = Util.parseArgs(args, true); EventQueue.invokeLater(new Runnable() { public void run() { try @@ -41,6 +46,29 @@ public class VisualForecastFrame extends JFrame implements WindowListener, KeyLi VisualForecastFrame frame = new VisualForecastFrame(); frame.setVisible(true); frame.init(); + + if (arguments.containsKey("fullscreen")) + { + try + { + + int index = Integer.parseInt(arguments.get("fullscreen")); + + GraphicsDevice[] devices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); + if(index < 0 || devices.length < index) + { + JOptionPane.showMessageDialog(frame, arguments.get("fullscreen") + " does not exist as a screen index"); + } + else + { + devices[index].setFullScreenWindow(frame); + } + } + catch (NumberFormatException ex) + { + JOptionPane.showMessageDialog(frame, arguments.get("fullscreen") + " is not a valid screen index"); + } + } //GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[1].setFullScreenWindow(frame); //frame.createBufferStrategy(2); } @@ -74,7 +102,6 @@ public class VisualForecastFrame extends JFrame implements WindowListener, KeyLi } }); timer.start(); - new Thread() { public void run() { diff --git a/src/com/flaremicro/visualforecast/displays/CurrentForecastDisplay.java b/src/com/flaremicro/visualforecast/displays/CurrentForecastDisplay.java new file mode 100644 index 0000000..7dd42cd --- /dev/null +++ b/src/com/flaremicro/visualforecast/displays/CurrentForecastDisplay.java @@ -0,0 +1,175 @@ +package com.flaremicro.visualforecast.displays; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.util.ArrayList; + +import com.flaremicro.visualforecast.RenderPanel; +import com.flaremicro.visualforecast.api.ForecastProvider; +import com.flaremicro.visualforecast.graphics.DrawingUtil; +import com.flaremicro.visualforecast.graphics.FontManager; + +import static com.flaremicro.visualforecast.graphics.RenderConstants.*; + +public class CurrentForecastDisplay implements Display { + + private String[] lines = new String[0]; + private Font font; + + private int stringOffset = 0; + private int ticksBeforeChange = 0; + + public CurrentForecastDisplay() { + font = FontManager.getInstance().getOrCreateFont(Font.TRUETYPE_FONT, this.getClass().getResource("/Star4000.ttf")).deriveFont(30F); + + /* + int w2 = g2d.getFontMetrics().stringWidth("EXTREME WEATHER ADVISORY"); + drawOutlinedString(g2d, (W >> 1) - (w2 >> 1), TOPBAR_HEIGHT + 48, "EXTREME WEATHER ADVISORY", Color.RED, Color.BLACK, 2); + + g2d.setFont(font.deriveFont(30F)); + for (int i = 0; i < testString.length; i++) + { + drawOutlinedString(g2d, 90, TOPBAR_HEIGHT + 78 + 25 * i, testString[i], Color.WHITE, Color.BLACK, 1); + } + */ + } + + @Override + public void tick(RenderPanel renderer, long ticks, int iconTicks) { + ticksBeforeChange--; + if (ticksBeforeChange <= 0) + { + stringOffset += 9; + if (stringOffset < lines.length) + { + ticksBeforeChange = 200; + renderer.requestExclusiveBoundedRepaint(); + } + else + { + renderer.nextDisplay(); + } + } + } + + @Override + public void initDisplay(RenderPanel renderer, ForecastProvider forecastProvider, long ticks, int iconTicks) { + ticksBeforeChange = 200; + stringOffset = 0; + String test = "*IMPORTANT NOTICE*\n*THIS BETA IS UNFINISHED*\n\nThis is a Beta version of the VisualForeast 1000! Many things are not finished, and only the hourly and 7 day forecasts are ready. There will be a lot more to come!\nPlease note that there are some issues with data collection from Environment Canada resulting in missing or potentially incorrect information around midnight hours. This will be fixed as the project continues and the disclaimer will be removed."; + ArrayList linesList = new ArrayList(); + + renderer.setCurrentForecast("Welcome to the"); + renderer.setCurrentTown("VisualForecast 1000"); + + BufferedImage disposableImage = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g = disposableImage.createGraphics(); + FontMetrics f = g.getFontMetrics(font); + String[] lines = test.split("\n"); + for (int i = 0; i < lines.length; i++) + { + splitText(lines[i], W - 150 - STROKE_WIDTH, f, linesList); + //if(words[i]) + } + + g.dispose(); + disposableImage.flush(); + + redrawRegionlost(renderer); + + this.lines = linesList.toArray(new String[0]); + } + + private void splitText(String text, int maxWidth, FontMetrics fontMetrics, ArrayList lineList) { + + text = text.trim(); + //this.text.add(text); + if (fontMetrics.stringWidth(text) <= maxWidth) + lineList.add(text); + else + { + while (text.length() > 0) + { + int idx = binSearch(text, fontMetrics, maxWidth, 1, text.length()); + if (idx <= 0) + break; + String texPart = text.substring(0, idx).trim(); + if (fontMetrics.stringWidth(text.substring(0, Math.min(text.length(), idx + 1))) > maxWidth && texPart.contains(" ")) + { + idx = texPart.lastIndexOf(" "); + texPart = texPart.substring(0, idx); + } + lineList.add(texPart); + text = text.substring(idx).trim(); + } + } + } + + private static int binSearch(String string, FontMetrics fontMetrics, int key, int low, int high) { + int index = 0; + + while (low <= high) + { + int mid = low + ((high - low) / 2); + int midmetric = fontMetrics.stringWidth(string.substring(0, mid)); + if (midmetric < key) + { + low = mid + 1; + index = mid; + } + else if (midmetric > key) + { + high = mid - 1; + } + else if (midmetric == key) + { + index = mid; + break; + } + } + return index; + } + + @Override + public void drawDisplay(RenderPanel renderer, Graphics2D g2d, long ticks, int iconTicks) { + } + + @Override + public void drawBoundLimitedDisplay(RenderPanel renderer, Graphics2D g2d, Rectangle bounds, long ticks, int iconTicks) { + DrawingUtil.drawGradientRect(g2d, 60, TOPBAR_HEIGHT, W - 120, MAINBAR_HEIGHT, 20, BG_BLUE.darker(), BG_BLUE.brighter()); + g2d.setFont(font); + FontMetrics fontMetrics = g2d.getFontMetrics(); + g2d.setColor(BG_BLUE.darker()); + g2d.drawRect(60 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET, W - 120 - STROKE_WIDTH, MAINBAR_HEIGHT - STROKE_WIDTH); + for (int i = 0; i < Math.min(9, lines.length - this.stringOffset); i++) + { + if (lines[i + stringOffset].startsWith("*") && lines[i].endsWith("*")) + { + String line = lines[i + stringOffset].substring(1, lines[i + stringOffset].length() - 1); + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, "*", Color.WHITE, Color.BLACK, 2); + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH) / 2 - fontMetrics.stringWidth(line) / 2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, line, Color.WHITE, Color.BLACK, 2); + DrawingUtil.drawOutlinedString(g2d, W - 95 - STROKE_WIDTH, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, "*", Color.WHITE, Color.BLACK, 2); + } + else if (lines[i + stringOffset].startsWith("[") && lines[i].endsWith("]")) + { + String line = lines[i + stringOffset].substring(1, lines[i + stringOffset].length() - 1); + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH) / 2 - fontMetrics.stringWidth(line) / 2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, line, Color.WHITE, Color.BLACK, 2); + } + else DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, lines[i + stringOffset], Color.WHITE, Color.BLACK, 2); + } + } + + @Override + public void redrawRegionlost(RenderPanel renderer) { + renderer.addRedrawBound(TOPBAR_HEIGHT, W - 120, MAINBAR_HEIGHT, 20); + } + + @Override + public void notifyForecastProviderUpdate(RenderPanel renderer, ForecastProvider forecastProvider) { + } + +} diff --git a/src/com/flaremicro/visualforecast/displays/DisplayFactory.java b/src/com/flaremicro/visualforecast/displays/DisplayFactory.java new file mode 100644 index 0000000..2ae42fc --- /dev/null +++ b/src/com/flaremicro/visualforecast/displays/DisplayFactory.java @@ -0,0 +1,32 @@ +package com.flaremicro.visualforecast.displays; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.flaremicro.visualforecast.PropertyManager; + +public class DisplayFactory { + int currentIndex = -1; + String indexValue = ""; + + private List nameOrder = new ArrayList(); + private Map displays = new HashMap(); + + public DisplayFactory(PropertyManager propertyManager) + { + displays.put("message", new MessageForecastDisplay()); + displays.put("day", new DayForecastDisplay()); + displays.put("hourly", new HourlyForecastDisplay()); + nameOrder.add("message"); + nameOrder.add("day"); + nameOrder.add("hourly"); + } + + public Display nextDisplay() { + currentIndex = (currentIndex + 1) % nameOrder.size(); + return displays.get(nameOrder.get(currentIndex)); + } + +} diff --git a/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java b/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java index 0685e38..7e9a18d 100644 --- a/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java +++ b/src/com/flaremicro/visualforecast/displays/HourlyForecastDisplay.java @@ -31,7 +31,6 @@ 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; @@ -41,7 +40,7 @@ public class HourlyForecastDisplay implements Display { private int ticksBeforeChange = 200; private int animationTicks = -1; - private byte[] dewval = null; + private byte[] windchill = null; private byte[] tempval = null; private float[] percipval = null; @@ -56,7 +55,26 @@ public class HourlyForecastDisplay implements Display { @Override public void tick(RenderPanel renderer, long ticks, int iconTicks) { + ticksBeforeChange--; + if (ticksBeforeChange <= 0) + { + ticksBeforeChange = 200; + do + { + townIndex++; + if (details == null || townIndex >= details.getTownForecast().length) + { + renderer.nextDisplay(); + return; + } + this.currentTown = details.getTownForecast()[townIndex]; + } + while (this.currentTown == null || currentTown.getHourlyForecast() == null || currentTown.getHourlyForecast().length == 0); + renderer.setCurrentTown(currentTown.getTownName()); + setGraphValues(currentTown); + renderer.requestFullRepaint(); + } } public void setGraphValues(TownForecast town) { @@ -66,12 +84,12 @@ public class HourlyForecastDisplay implements Display { if (forecast != null && forecast.length > 0) { int size = Math.min(12, forecast.length); - dewval = new byte[size]; + windchill = new byte[size]; tempval = new byte[size]; percipval = new float[size]; for (int i = 0; i < size; i++) { - dewval[i] = forecast[i].dewPoint; + windchill[i] = forecast[i].windChill; tempval[i] = forecast[i].temp; percipval[i] = forecast[i].percip; } @@ -133,15 +151,17 @@ public class HourlyForecastDisplay implements Display { 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, 260, RenderConstants.TOPBAR_HEIGHT + 35, "Wind Chill\u00B0c", Color.WHITE, Color.BLACK, 2); DrawingUtil.drawOutlinedString(g2d, 410, RenderConstants.TOPBAR_HEIGHT + 35, "Precipitation%", Color.CYAN, Color.BLACK, 2); + g2d.setFont(font.deriveFont(20F)); 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); + DrawingUtil.drawOutlinedString(g2d, RenderConstants.SIDE_OFFSET + 38 - fm.stringWidth(maxText), RenderConstants.TOPBAR_HEIGHT + 53, maxText, Color.YELLOW, Color.BLACK, 2); + DrawingUtil.drawOutlinedString(g2d, RenderConstants.SIDE_OFFSET + 38 - fm.stringWidth(minText), RenderConstants.TOPBAR_HEIGHT + MAINBAR_HEIGHT - 92, minText, Color.YELLOW, Color.BLACK, 2); + g2d.setFont(font.deriveFont(20F)); 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)); @@ -150,6 +170,7 @@ public class HourlyForecastDisplay implements Display { //BlackLines + g2d.setFont(smallFont.deriveFont(20F)); if (max > 0 && min < 0) { int nextTemp = (RenderConstants.TOPBAR_HEIGHT + RenderConstants.MAINBAR_HEIGHT - 94) + (int) (((min) / (float) range) * (RenderConstants.MAINBAR_HEIGHT - 144)); @@ -166,10 +187,10 @@ public class HourlyForecastDisplay implements Display { g2d.translate(2, 2); g2d.setColor(Color.BLACK); - this.graphTemp(g2d, this.dewval, min, slotWidth, range, RenderConstants.MAINBAR_HEIGHT - 144); + this.graphTemp(g2d, this.windchill, 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); + this.graphTemp(g2d, this.windchill, min, slotWidth, range, RenderConstants.MAINBAR_HEIGHT - 144); g2d.translate(2, 2); g2d.setColor(Color.BLACK); @@ -196,6 +217,7 @@ public class HourlyForecastDisplay implements Display { for (int i = 0; i < Math.min(12, forecast.length); i++) { String timeString = simpleDateFormat.format(forecast[i].hour); + timeString = timeString.substring(0, timeString.length() - 1); 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); @@ -210,13 +232,13 @@ public class HourlyForecastDisplay implements Display { for (int i = 0; i < forecast.length; i++) { int currMin; - if (ValueCheck.valueNoData(forecast[i].dewPoint) && ValueCheck.valueNoData(forecast[i].temp)) + if (ValueCheck.valueNoData(forecast[i].windChill) && ValueCheck.valueNoData(forecast[i].temp)) continue; - else if (ValueCheck.valueNoData(forecast[i].dewPoint)) + else if (ValueCheck.valueNoData(forecast[i].windChill)) 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); + currMin = forecast[i].windChill; + else currMin = Math.min(forecast[i].temp, forecast[i].windChill); if (currMin < min) min = currMin; } @@ -228,13 +250,13 @@ public class HourlyForecastDisplay implements Display { for (int i = 0; i < forecast.length; i++) { int currMax; - if (ValueCheck.valueNoData(forecast[i].dewPoint) && ValueCheck.valueNoData(forecast[i].temp)) + if (ValueCheck.valueNoData(forecast[i].windChill) && ValueCheck.valueNoData(forecast[i].temp)) continue; - else if (ValueCheck.valueNoData(forecast[i].dewPoint)) + else if (ValueCheck.valueNoData(forecast[i].windChill)) 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); + currMax = forecast[i].windChill; + else currMax = Math.max(forecast[i].temp, forecast[i].windChill); if (currMax > max) max = currMax; } diff --git a/src/com/flaremicro/visualforecast/displays/MessageForecastDisplay.java b/src/com/flaremicro/visualforecast/displays/MessageForecastDisplay.java new file mode 100644 index 0000000..8909c99 --- /dev/null +++ b/src/com/flaremicro/visualforecast/displays/MessageForecastDisplay.java @@ -0,0 +1,175 @@ +package com.flaremicro.visualforecast.displays; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.util.ArrayList; + +import com.flaremicro.visualforecast.RenderPanel; +import com.flaremicro.visualforecast.api.ForecastProvider; +import com.flaremicro.visualforecast.graphics.DrawingUtil; +import com.flaremicro.visualforecast.graphics.FontManager; + +import static com.flaremicro.visualforecast.graphics.RenderConstants.*; + +public class MessageForecastDisplay implements Display { + + private String[] lines = new String[0]; + private Font font; + + private int stringOffset = 0; + private int ticksBeforeChange = 0; + + public MessageForecastDisplay() { + font = FontManager.getInstance().getOrCreateFont(Font.TRUETYPE_FONT, this.getClass().getResource("/Star4000.ttf")).deriveFont(30F); + + /* + int w2 = g2d.getFontMetrics().stringWidth("EXTREME WEATHER ADVISORY"); + drawOutlinedString(g2d, (W >> 1) - (w2 >> 1), TOPBAR_HEIGHT + 48, "EXTREME WEATHER ADVISORY", Color.RED, Color.BLACK, 2); + + g2d.setFont(font.deriveFont(30F)); + for (int i = 0; i < testString.length; i++) + { + drawOutlinedString(g2d, 90, TOPBAR_HEIGHT + 78 + 25 * i, testString[i], Color.WHITE, Color.BLACK, 1); + } + */ + } + + @Override + public void tick(RenderPanel renderer, long ticks, int iconTicks) { + ticksBeforeChange--; + if (ticksBeforeChange <= 0) + { + stringOffset += 9; + if (stringOffset < lines.length) + { + ticksBeforeChange = 200; + renderer.requestExclusiveBoundedRepaint(); + } + else + { + renderer.nextDisplay(); + } + } + } + + @Override + public void initDisplay(RenderPanel renderer, ForecastProvider forecastProvider, long ticks, int iconTicks) { + ticksBeforeChange = 200; + stringOffset = 0; + String test = "*IMPORTANT NOTICE*\n*THIS BETA IS UNFINISHED*\n\nThis is a Beta version of the VisualForeast 1000! Many things are not finished, and only the hourly and 7 day forecasts are ready. There will be a lot more to come!\nPlease note that there are some issues with data collection from Environment Canada resulting in missing or potentially incorrect information around midnight hours. This will be fixed as the project continues and the disclaimer will be removed."; + ArrayList linesList = new ArrayList(); + + renderer.setCurrentForecast("Welcome to the"); + renderer.setCurrentTown("VisualForecast 1000"); + + BufferedImage disposableImage = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g = disposableImage.createGraphics(); + FontMetrics f = g.getFontMetrics(font); + String[] lines = test.split("\n"); + for (int i = 0; i < lines.length; i++) + { + splitText(lines[i], W - 150 - STROKE_WIDTH, f, linesList); + //if(words[i]) + } + + g.dispose(); + disposableImage.flush(); + + redrawRegionlost(renderer); + + this.lines = linesList.toArray(new String[0]); + } + + private void splitText(String text, int maxWidth, FontMetrics fontMetrics, ArrayList lineList) { + + text = text.trim(); + //this.text.add(text); + if (fontMetrics.stringWidth(text) <= maxWidth) + lineList.add(text); + else + { + while (text.length() > 0) + { + int idx = binSearch(text, fontMetrics, maxWidth, 1, text.length()); + if (idx <= 0) + break; + String texPart = text.substring(0, idx).trim(); + if (fontMetrics.stringWidth(text.substring(0, Math.min(text.length(), idx + 1))) > maxWidth && texPart.contains(" ")) + { + idx = texPart.lastIndexOf(" "); + texPart = texPart.substring(0, idx); + } + lineList.add(texPart); + text = text.substring(idx).trim(); + } + } + } + + private static int binSearch(String string, FontMetrics fontMetrics, int key, int low, int high) { + int index = 0; + + while (low <= high) + { + int mid = low + ((high - low) / 2); + int midmetric = fontMetrics.stringWidth(string.substring(0, mid)); + if (midmetric < key) + { + low = mid + 1; + index = mid; + } + else if (midmetric > key) + { + high = mid - 1; + } + else if (midmetric == key) + { + index = mid; + break; + } + } + return index; + } + + @Override + public void drawDisplay(RenderPanel renderer, Graphics2D g2d, long ticks, int iconTicks) { + } + + @Override + public void drawBoundLimitedDisplay(RenderPanel renderer, Graphics2D g2d, Rectangle bounds, long ticks, int iconTicks) { + DrawingUtil.drawGradientRect(g2d, 60, TOPBAR_HEIGHT, W - 120, MAINBAR_HEIGHT, 20, BG_BLUE.darker(), BG_BLUE.brighter()); + g2d.setFont(font); + FontMetrics fontMetrics = g2d.getFontMetrics(); + g2d.setColor(BG_BLUE.darker()); + g2d.drawRect(60 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET, W - 120 - STROKE_WIDTH, MAINBAR_HEIGHT - STROKE_WIDTH); + for (int i = 0; i < Math.min(9, lines.length - this.stringOffset); i++) + { + if (lines[i + stringOffset].startsWith("*") && lines[i].endsWith("*")) + { + String line = lines[i + stringOffset].substring(1, lines[i + stringOffset].length() - 1); + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, "*", Color.WHITE, Color.BLACK, 2); + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH) / 2 - fontMetrics.stringWidth(line) / 2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, line, Color.WHITE, Color.BLACK, 2); + DrawingUtil.drawOutlinedString(g2d, W - 95 - STROKE_WIDTH, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, "*", Color.WHITE, Color.BLACK, 2); + } + else if (lines[i + stringOffset].startsWith("[") && lines[i].endsWith("]")) + { + String line = lines[i + stringOffset].substring(1, lines[i + stringOffset].length() - 1); + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH) / 2 - fontMetrics.stringWidth(line) / 2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, line, Color.WHITE, Color.BLACK, 2); + } + else DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, lines[i + stringOffset], Color.WHITE, Color.BLACK, 2); + } + } + + @Override + public void redrawRegionlost(RenderPanel renderer) { + renderer.addRedrawBound(TOPBAR_HEIGHT, W - 120, MAINBAR_HEIGHT, 20); + } + + @Override + public void notifyForecastProviderUpdate(RenderPanel renderer, ForecastProvider forecastProvider) { + } + +} diff --git a/src/com/flaremicro/visualforecast/displays/ThirtySixHourDisplay.java b/src/com/flaremicro/visualforecast/displays/ThirtySixHourDisplay.java new file mode 100644 index 0000000..bd57794 --- /dev/null +++ b/src/com/flaremicro/visualforecast/displays/ThirtySixHourDisplay.java @@ -0,0 +1,155 @@ +package com.flaremicro.visualforecast.displays; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.util.ArrayList; + +import com.flaremicro.visualforecast.RenderPanel; +import com.flaremicro.visualforecast.api.ForecastProvider; +import com.flaremicro.visualforecast.graphics.DrawingUtil; +import com.flaremicro.visualforecast.graphics.FontManager; + +import static com.flaremicro.visualforecast.graphics.RenderConstants.*; + +public class ThirtySixHourDisplay implements Display { + + private String[] lines = new String[0]; + private Font font; + + + public ThirtySixHourDisplay() { + font = FontManager.getInstance().getOrCreateFont(Font.TRUETYPE_FONT, this.getClass().getResource("/Star4000.ttf")).deriveFont(30F); + + /* + int w2 = g2d.getFontMetrics().stringWidth("EXTREME WEATHER ADVISORY"); + drawOutlinedString(g2d, (W >> 1) - (w2 >> 1), TOPBAR_HEIGHT + 48, "EXTREME WEATHER ADVISORY", Color.RED, Color.BLACK, 2); + + g2d.setFont(font.deriveFont(30F)); + for (int i = 0; i < testString.length; i++) + { + drawOutlinedString(g2d, 90, TOPBAR_HEIGHT + 78 + 25 * i, testString[i], Color.WHITE, Color.BLACK, 1); + } + */ + } + + @Override + public void tick(RenderPanel renderer, long ticks, int iconTicks) { + } + + @Override + public void initDisplay(RenderPanel renderer, ForecastProvider forecastProvider, long ticks, int iconTicks) { + String test = "*IMPORTANT NOTICE*\n*POWER OUTAGE POSSIBLE*\n*DUE TO EXTREME WINDS*\n\nHurricane-force winds capable of reaching 127km/h will be hitting the east coast around noon tomorrow. Keep windows and doors barricaded to prevent"; + ArrayList linesList = new ArrayList(); + + BufferedImage disposableImage = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g = disposableImage.createGraphics(); + FontMetrics f = g.getFontMetrics(font); + System.out.println("yas2"); + String[] lines = test.split("\n"); + for(int i = 0; i < lines.length; i++) + { + splitText(lines[i], W - 150 - STROKE_WIDTH, f, linesList); + //if(words[i]) + } + + g.dispose(); + disposableImage.flush(); + + System.out.println("yas"); + + this.lines = linesList.toArray(new String[0]); + } + + private void splitText(String text, int maxWidth, FontMetrics fontMetrics, ArrayList lineList) { + + text = text.trim(); + //this.text.add(text); + if (fontMetrics.stringWidth(text) <= maxWidth) + lineList.add(text); + else + { + while (text.length() > 0) + { + int idx = binSearch(text, fontMetrics, maxWidth, 1, text.length()); + if (idx <= 0) + break; + String texPart = text.substring(0, idx).trim(); + if (fontMetrics.stringWidth(text.substring(0, Math.min(text.length(), idx + 1))) > maxWidth && texPart.contains(" ")) + { + idx = texPart.lastIndexOf(" "); + texPart = texPart.substring(0, idx); + } + lineList.add(texPart); + text = text.substring(idx).trim(); + } + } + } + + private static int binSearch(String string, FontMetrics fontMetrics, int key, int low, int high) { + int index = 0; + + while (low <= high) + { + int mid = low + ((high - low) / 2); + int midmetric = fontMetrics.stringWidth(string.substring(0, mid)); + if (midmetric < key) + { + low = mid + 1; + index = mid; + } + else if (midmetric > key) + { + high = mid - 1; + } + else if (midmetric == key) + { + index = mid; + break; + } + } + return index; + } + + @Override + public void drawDisplay(RenderPanel renderer, Graphics2D g2d, long ticks, int iconTicks) { + } + + @Override + public void drawBoundLimitedDisplay(RenderPanel renderer, Graphics2D g2d, Rectangle bounds, long ticks, int iconTicks) { + DrawingUtil.drawGradientRect(g2d, 60, TOPBAR_HEIGHT, W - 120, MAINBAR_HEIGHT, 20, BG_BLUE.brighter(), BG_BLUE.darker()); + g2d.setFont(font); + FontMetrics fontMetrics = g2d.getFontMetrics(); + g2d.setColor(BG_BLUE.brighter()); + g2d.drawRect(60 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET, W - 120 - STROKE_WIDTH, MAINBAR_HEIGHT - STROKE_WIDTH); + for(int i = 0; i < lines.length; i++) + { + if(lines[i].startsWith("*") && lines[i].endsWith("*")) + { + String line = lines[i].substring(1, lines[i].length()-1); + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i*30, "*", Color.WHITE, Color.BLACK, 2); + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH)/2 - fontMetrics.stringWidth(line)/2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i*30, line, Color.WHITE, Color.BLACK, 2); + DrawingUtil.drawOutlinedString(g2d, W - 95 - STROKE_WIDTH, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i*30, "*", Color.WHITE, Color.BLACK, 2); + } + else if(lines[i].startsWith("[") && lines[i].endsWith("]")) + { + String line = lines[i].substring(1, lines[i].length()-1); + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH)/2 - fontMetrics.stringWidth(line)/2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i*30, line, Color.WHITE, Color.BLACK, 2); + } + else + DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i*30, lines[i], Color.WHITE, Color.BLACK, 2); + } + } + + @Override + public void redrawRegionlost(RenderPanel renderer) { + } + + @Override + public void notifyForecastProviderUpdate(RenderPanel renderer, ForecastProvider forecastProvider) { + } + +} diff --git a/src/com/flaremicro/visualforecast/forecast/HourlyForecast.java b/src/com/flaremicro/visualforecast/forecast/HourlyForecast.java index 43f8f26..1f3c8ca 100644 --- a/src/com/flaremicro/visualforecast/forecast/HourlyForecast.java +++ b/src/com/flaremicro/visualforecast/forecast/HourlyForecast.java @@ -10,15 +10,15 @@ public class HourlyForecast { public final byte temp; public final short windSpeed; public final float percip; - public final byte dewPoint; + public final byte windChill; - public HourlyForecast(Date hour, byte iconId, byte temp, short windSpeed, float percip, byte dewPoint) + public HourlyForecast(Date hour, byte iconId, byte temp, short windSpeed, float percip, byte windChill) { this.hour = hour; this.iconId = (byte) (iconId % IconProvider.INDEXED_ICONS.length); this.temp = temp; this.windSpeed = windSpeed; this.percip = percip; - this.dewPoint = dewPoint; + this.windChill = windChill; } } diff --git a/src/com/flaremicro/visualforecast/forecast/TownForecast.java b/src/com/flaremicro/visualforecast/forecast/TownForecast.java index 49d31af..01adb88 100644 --- a/src/com/flaremicro/visualforecast/forecast/TownForecast.java +++ b/src/com/flaremicro/visualforecast/forecast/TownForecast.java @@ -1,5 +1,10 @@ package com.flaremicro.visualforecast.forecast; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + public class TownForecast { public TownForecast(String townName, DayForecast[] dayForecast) { this.townName = townName; @@ -8,7 +13,24 @@ public class TownForecast { private final String townName; private final DayForecast[] dayForecast; + private Set displays = new HashSet(); + + private HourlyForecast[] hourlyForecast; + + public boolean isDisplaySupported(String displayName) + { + return displays.size() == 0 || displays.contains(displayName); + } + + public void setSupportedDisplays(String ... displays){ + setSupportedDisplays(Arrays.asList(displays)); + } + + public void setSupportedDisplays(Collection displays){ + this.displays.clear(); + this.displays.addAll(displays); + } public void setHourlyForecast(HourlyForecast[] forecast) { this.hourlyForecast = forecast; diff --git a/vf1000.properties b/vf1000.properties index c509e50..a6c66df 100644 --- a/vf1000.properties +++ b/vf1000.properties @@ -1,3 +1,3 @@ #VisualForecast 1000 Properties file. Functional provider must be set for successful boot! -#Sat Mar 09 23:49:43 PST 2024 -forecast-provider-jar=MockForecastProvider.jar +#Fri Mar 15 19:46:22 PDT 2024 +forecast-provider-jar=CanadaDatamartProvider.jar -- 2.39.5