/* GNU General Public License CacheWolf is a software for PocketPC, Win and Linux that enables paperless caching. It supports the sites geocaching.com and opencaching.de Copyright (C) 2006 CacheWolf development team See http://www.cachewolf.de/ for more information. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package CacheWolf.navi; import CacheWolf.CoordsInput; import CacheWolf.CoordsPDAInput; import CacheWolf.MainForm; import CacheWolf.MainTab; import CacheWolf.Preferences; import CacheWolf.controls.GuiImageBroker; import CacheWolf.controls.InfoBox; import CacheWolf.database.CWPoint; import CacheWolf.database.CacheHolder; import CacheWolf.database.CacheType; import CacheWolf.utils.Metrics; import CacheWolf.utils.MyLocale; import ewe.fx.Brush; import ewe.fx.Color; import ewe.fx.Font; import ewe.fx.FontMetrics; import ewe.fx.Graphics; import ewe.fx.Pen; import ewe.fx.Point; import ewe.fx.Rect; import ewe.graphics.AniImage; import ewe.sys.Convert; import ewe.sys.Double; import ewe.sys.Vm; import ewe.sys.VmConstants; import ewe.ui.CellPanel; import ewe.ui.ControlConstants; import ewe.ui.ControlEvent; import ewe.ui.Event; import ewe.ui.FormBase; import ewe.ui.Gui; import ewe.ui.ImageControl; import ewe.ui.Menu; import ewe.ui.MenuEvent; import ewe.ui.MenuItem; import ewe.ui.Window; import ewe.ui.WindowConstants; import ewe.ui.mButton; import ewe.ui.mLabel; /** * Class to create the panel which handles the connection to the GPS-device<br> * Displays: current position,speed and bearing; relation to destination waypoint<br> * Class ID: 1500 */ public final class GotoPanel extends CellPanel { final CellPanel buttonPanel; final CellPanel coordsPanel; final CellPanel rosePanel; private mButton btnGPS, btnCenter, btnNewWpt; private mButton destination; private mButton btnChangeProjection; private int currFormatSel; mLabel lblDestination, lblGPS, lblPosition; Color gpsStatus; ImageControl icRose; GotoRose compassRose; final static Color RED = new Color(255, 0, 0); final static Color YELLOW = new Color(255, 255, 0); final static Color GREEN = new Color(0, 255, 0); final static Color BLUE = new Color(0, 128, 255); Menu mnuContextFormt; MenuItem miCooformat[] = new MenuItem[TransformCoordinates.localSystems.length + 3]; // miDMM, miDMS, miDD, miUTM, miGK; Menu mnuContextRose; MenuItem miLuminary[] = new MenuItem[SkyOrientation.LUMINARY_NAMES.length]; MenuItem miNorthCentered; /** * Create GotoPanel * * @param Preferences * global preferences * @param MainTab * reference to MainTable * @param DetailsPanel * reference to DetailsPanel * @param Vector * cacheDB */ public GotoPanel() { // Button buttonPanel = new CellPanel(); btnGPS = GuiImageBroker.getButton(MyLocale.getMsg(1504, "Start"), "gps"); btnGPS.setToolTip(MyLocale.getMsg(1504, "Start")); btnCenter = GuiImageBroker.getButton(MyLocale.getMsg(309, "Centre"), "snap2gps"); btnCenter.setToolTip(MyLocale.getMsg(646, "Current centre from GPS")); btnNewWpt = GuiImageBroker.getButton(MyLocale.getMsg(733, "Addi Wpt"), "newwpt"); btnNewWpt.setToolTip(MyLocale.getMsg(311, "Create Waypoint")); buttonPanel.addNext(btnGPS); buttonPanel.addNext(btnCenter); buttonPanel.addLast(btnNewWpt); // coordsPanel coordsPanel = new CellPanel(); // Format selection for coords (context) menu mnuContextFormt = new Menu(); currFormatSel = 1; // default to d� m.m mnuContextFormt.addItem(miCooformat[0] = new MenuItem("d.d�")); miCooformat[0].modifiers &= ~MenuItem.Checked; mnuContextFormt.addItem(miCooformat[1] = new MenuItem("d�m.m\'")); miCooformat[1].modifiers |= MenuItem.Checked; // default mnuContextFormt.addItem(miCooformat[2] = new MenuItem("d�m\'s\"")); miCooformat[2].modifiers &= ~MenuItem.Checked; mnuContextFormt.addItems(TransformCoordinates.localSystemsFriendlyShortname()); CellPanel destinationPanel = new CellPanel(); lblDestination = new mLabel("DST: "); lblDestination.setMenu(mnuContextFormt); lblDestination.modifyAll(ControlConstants.WantHoldDown, 0); CellPanel labelPanel = new CellPanel(); labelPanel.backGround = BLUE; labelPanel.addLast(lblDestination, VSTRETCH, CENTER); destinationPanel.addNext(labelPanel, VSTRETCH, VFILL); destination = GuiImageBroker.getButton(MyLocale.getMsg(1500, "DST:"), "goto"); destination.setToolTip(MyLocale.getMsg(1500, "DST:")); destination.setMenu(mnuContextFormt); destination.modifyAll(ControlConstants.WantHoldDown, 0); destinationPanel.addNext(destination, HSTRETCH, HFILL); btnChangeProjection = GuiImageBroker.getButton("...", "projection"); btnChangeProjection.setMenu(mnuContextFormt); btnChangeProjection.modifyAll(ControlConstants.WantHoldDown, 0); destinationPanel.addLast(btnChangeProjection, DONTSTRETCH, DONTFILL); coordsPanel.addLast(destinationPanel, HSTRETCH, HFILL); CellPanel gpsPanel = new CellPanel(); gpsPanel.addNext(lblGPS = new mLabel("GPS: "), DONTSTRETCH, FILL); lblGPS.backGround = RED; lblGPS.setMenu(mnuContextFormt); lblGPS.modifyAll(ControlConstants.WantHoldDown, 0); lblPosition = new mLabel(""); lblPosition.anchor = CENTER; lblPosition.setMenu(mnuContextFormt); lblPosition.modifyAll(ControlConstants.WantHoldDown, 0); gpsPanel.addLast(lblPosition, HSTRETCH, HFILL); coordsPanel.addLast(gpsPanel, HSTRETCH, HFILL); // rosePanel for bearing rosePanel = new CellPanel(); compassRose = new GotoRose(); icRose = new ImageControl(compassRose); icRose.modifyAll(ControlConstants.WantHoldDown, 0); // this is necessary in order to make PenHold on a PDA work as right click // Create context menu for compass rose mnuContextRose = new Menu(); for (int i = 0; i < SkyOrientation.LUMINARY_NAMES.length; i++) { mnuContextRose.addItem(miLuminary[i] = new MenuItem(SkyOrientation.getLuminaryName(i))); } icRose.setMenu(mnuContextRose); mnuContextRose.addItem(new MenuItem("", MenuItem.Separator, null)); mnuContextRose.addItem(miNorthCentered = new MenuItem(MyLocale.getMsg(1503, "North Centered"))); if (compassRose.isNorthCentered()) miNorthCentered.modifiers |= MenuItem.Checked; else miNorthCentered.modifiers &= MenuItem.Checked; rosePanel.addLast(icRose, STRETCH, FILL); // add Panels if (Preferences.itself().tabsAtTop) { if (Preferences.itself().menuAtTab) this.addLast(buttonPanel, HSTRETCH, HFILL); } else { if (!Preferences.itself().menuAtTab) this.addLast(buttonPanel, HSTRETCH, HFILL); } this.addLast(coordsPanel, HSTRETCH, HFILL); this.addLast(rosePanel); if (Preferences.itself().tabsAtTop) { if (!Preferences.itself().menuAtTab) this.addLast(buttonPanel, HSTRETCH, HFILL); } else { if (Preferences.itself().menuAtTab) this.addLast(buttonPanel, HSTRETCH, HFILL); } // select luminary for orientation for (int i = 0; i < SkyOrientation.LUMINARY_NAMES.length; i++) { if (i == Navigate.luminary) miLuminary[i].modifiers |= MenuItem.Checked; else miLuminary[i].modifiers &= MenuItem.Checked; } lblPosition.text = Navigate.gpsPos.toString(CoordsInput.getLocalSystem(currFormatSel)); GuiImageBroker.setButtonText(destination, getGotoBtnText()); } // Overrides public void resizeTo(int pWidth, int pHeight) { super.resizeTo(pWidth, pHeight); Rect coordsRect = coordsPanel.getRect(); Rect buttonRect = buttonPanel.getRect(); int roseHeight = pHeight - buttonRect.height - coordsRect.height; if (Gui.screenIs(Gui.PDA_SCREEN) && Vm.isMobile()) { // some space for the SIP button if ((Vm.getParameter(VmConstants.VM_FLAGS) & (VmConstants.VM_FLAG_SIP_BUTTON_ON_SCREEN)) == (VmConstants.VM_FLAG_SIP_BUTTON_ON_SCREEN)) { Rect screen = (Rect) Window.getGuiInfo(WindowConstants.INFO_SCREEN_RECT, null, new Rect(), 0); roseHeight = roseHeight - screen.height / 14; } } rosePanel.resizeTo(pWidth, roseHeight); icRose.resizeTo(pWidth, roseHeight); compassRose.resize(pWidth, roseHeight); Rect roseRect = rosePanel.getRect(); if (Preferences.itself().tabsAtTop) { if (!Preferences.itself().menuAtTab) buttonPanel.setLocation(0, roseRect.y + roseRect.height); } else { if (Preferences.itself().menuAtTab) buttonPanel.setLocation(0, roseRect.y + roseRect.height); } } // called from myNavigate public void destChanged(CWPoint d) { GuiImageBroker.setButtonText(destination, getGotoBtnText()); updateDistance(); } /** * updates distance and bearing * */ public void updateDistance() { // update distance float distance = -1.0f; if (Navigate.gpsPos.isValid() && Navigate.destination.isValid()) { distance = (float) Navigate.gpsPos.getDistance(Navigate.destination); } compassRose.setWaypointDirectionDist((float) Navigate.gpsPos.getBearing(Navigate.destination), distance); } /** * method which is called if a Navigate.ticked() is set up */ public void updateGps(int fix) { Double bearMov = new Double(); Double speed = new Double(); Double sunAzimut = new Double(); compassRose.setGpsStatus(fix, Navigate.gpsPos.getSats(), Navigate.gpsPos.getSatsInView(), Navigate.gpsPos.getHDOP()); if ((fix > 0) && (Navigate.gpsPos.getSats() >= 0)) { // display values only, if signal good lblPosition.setText(Navigate.gpsPos.toString(CoordsInput.getLocalSystem(currFormatSel))); speed.set(Navigate.gpsPos.getSpeed()); sunAzimut.set(MainTab.itself.navigate.skyOrientationDir.lonDec); bearMov.set(Navigate.gpsPos.getBear()); updateDistance(); compassRose.setSunMoveDirections((float) sunAzimut.value, (float) bearMov.value, (float) speed.value); // Set background to signal quality } // receiving data, but signal ist not good if ((fix == 0) && (Navigate.gpsPos.getSats() >= 0)) { gpsStatus = YELLOW; } // receiving no data if (fix == -1) { if (gpsStatus != RED) new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(1510, "No data from GPS.\nConnection to serial port/gpsd closed.")).exec(); gpsStatus = RED; MainTab.itself.navigate.stopGps(); } // cannot interpret data if (fix == -2) { if (gpsStatus != RED) new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(1511, "Cannot interpret data from GPS/gpsd!\nPossible reasons:\nWrong port,\nwrong baud rate,\ninvalid protocol (need NMEA/gpsd).\nConnection to serial port closed.\nLast String tried to interpret:\n") + Navigate.gpsPos.lastStrExamined, FormBase.OKB).exec(); gpsStatus = RED; MainTab.itself.navigate.stopGps(); // TODO automatic in myNavigate? } } public void gpsStarted() { btnGPS.setText(MyLocale.getMsg(1505, "Stop")); } public void startGps() { MainTab.itself.navigate.startGps(Preferences.itself().logGPS, Convert.toInt(Preferences.itself().logGPSTimer)); } public void gpsStoped() { btnGPS.setText(MyLocale.getMsg(1504, "Start")); gpsStatus = this.backGround; this.repaintNow(); // without this the change in the background color will not be displayed } private String getGotoBtnText() { if (Navigate.destination == null) return MyLocale.getMsg(999, "Not set"); else return Navigate.destination.toString(CoordsInput.getLocalSystem(currFormatSel)); } /** * Eventhandler */ public void onEvent(Event ev) { if (ev instanceof MenuEvent) { if (ev.type == MenuEvent.SELECTED) { if (((MenuEvent) ev).menu == mnuContextFormt) { mnuContextFormt.close(); mnuContextFormt.getItemAt(currFormatSel).modifiers &= ~MenuItem.Checked; currFormatSel = mnuContextFormt.getInt(); mnuContextFormt.getItemAt(currFormatSel).modifiers |= MenuItem.Checked; lblPosition.setText(Navigate.gpsPos.toString(CoordsInput.getLocalSystem(currFormatSel))); GuiImageBroker.setButtonText(destination, getGotoBtnText()); } // end lat-lon-format context menu if (((MenuEvent) ev).menu == mnuContextRose) { MenuItem action = (MenuItem) mnuContextRose.getSelectedItem(); if (action != null) { for (int i = 0; i < miLuminary.length; i++) { if (action == miLuminary[i]) { MainTab.itself.navigate.setLuminary(i); miLuminary[i].modifiers |= MenuItem.Checked; compassRose.setLuminaryName(SkyOrientation.getLuminaryName(Navigate.luminary)); } else miLuminary[i].modifiers &= ~MenuItem.Checked; } if (action == miNorthCentered) { if (compassRose.isNorthCentered()) { compassRose.setNorthCentered(false); miNorthCentered.modifiers &= ~MenuItem.Checked; } else { compassRose.setNorthCentered(true); miNorthCentered.modifiers |= MenuItem.Checked; } } } } } } if (ev instanceof ControlEvent && ev.type == ControlEvent.PRESSED) { // start/stop GPS connection if (ev.target == btnGPS) { if (btnGPS.getText().equals(MyLocale.getMsg(1504, "Start"))) startGps(); else MainTab.itself.navigate.stopGps(); } // set current position as centre and recalculate distance of caches in MainTab if (ev.target == btnCenter) { if (Navigate.gpsPos.isValid()) { MainForm.itself.setCurCentrePt(Navigate.gpsPos); } else new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(1514, "Cannot recalculate distances, because the GPS position is not set")).wait(FormBase.OKB); } // Start moving map /* if (ev.target == btnMap) { switchToMovingMap(); } */ // create new waypoint with current GPS-position if (ev.target == btnNewWpt) { CacheHolder ch = new CacheHolder(); ch.setWpt(Navigate.gpsPos); ch.setType(CacheType.CW_TYPE_STAGE); // see CacheType.GC_AW_STAGE_OF_MULTI // TODO unfertig MainTab.itself.newWaypoint(ch); } // change destination waypoint if (ev.target == destination) { if (Vm.isMobile()) { CoordsPDAInput InScr = new CoordsPDAInput(CoordsInput.getLocalSystem(currFormatSel)); if (Navigate.destination.isValid()) InScr.setCoords(Navigate.destination); else InScr.setCoords(new CWPoint(0, 0)); if (InScr.execute(null, TOP) == FormBase.IDOK) Navigate.itself.setDestination(InScr.getCoords()); } else { CoordsInput cs = new CoordsInput(); if (Navigate.destination.isValid()) cs.setFields(Navigate.destination, CoordsInput.getLocalSystem(currFormatSel)); else cs.setFields(new CWPoint(0, 0), CoordsInput.getLocalSystem(currFormatSel)); if (cs.execute(null, TOP) == FormBase.IDOK) Navigate.itself.setDestination(cs.getCoords()); } } if (ev.target == this.btnChangeProjection) { Rect rm = mnuContextFormt.getRect(); btnChangeProjection.startDropMenu(new Point(rm.x, rm.y)); } } super.onEvent(ev); } } /** * class for displaying the compass rose including goto, sun and moving direction */ class GotoRose extends AniImage { float gotoDir = -361; float sunDir = -361; float moveDir = -361; float distance = -1; int m_fix = -1; int m_sats = -1; int m_satsInView = 0; double m_hdop = -1; float m_speed = -1; String m_Luminary = MyLocale.getMsg(6100, "Sun"); Font mainFont, bigFont; FontMetrics fm, fb; int lineHeight, lineHeightBig; int roseRadius; boolean northCentered = Preferences.itself().northCenteredGoto; final static Color RED = new Color(255, 0, 0); final static Color YELLOW = new Color(255, 255, 0); final static Color GREEN = new Color(0, 255, 0); final static Color BLUE = new Color(0, 0, 255); final static Color ORANGE = new Color(255, 128, 0); final static Color DARKGREEN = new Color(0, 192, 0); final static Color CYAN = new Color(0, 255, 255); final static Color MAGENTA = new Color(255, 0, 255); final static Color GREY = new Color(150, 150, 150); final static Color LIGHT_GREY = new Color(200, 200, 200); final static Color ROSE_BORDER_COLOR = new Color(150, 150, 150); final static Color ROSE_TEXT_COLOR = new Color(75, 75, 75); /** * @param gd * goto direction * @param sd * sun direction * @param md * moving direction */ public GotoRose(String fn) { super(fn); } public GotoRose() { super(); } public void setWaypointDirectionDist(float wd, float dist) { gotoDir = wd; distance = dist; } public void setSunMoveDirections(float sd, float md, float speed) { sunDir = sd; moveDir = md; m_speed = speed; refresh(); } public void setGpsStatus(int fix, int sats, int satsInView, double hdop) { m_fix = fix; m_sats = sats; m_satsInView = satsInView; m_hdop = hdop; refresh(); } public void setLuminaryName(String Luminary) { m_Luminary = Luminary; refresh(); } /** * draw arrows for the directions of movement and destination waypoint * * @param ctrl * the control to paint on * @param moveDir * degrees of movement * @param destDir * degrees of destination waypoint */ public void doDraw(Graphics g, int options) { g.setColor(Color.White); g.fillRect(0, 0, location.width, location.height); int fontSize = location.width / 17; int fontSizeBig = location.width / 10; mainFont = GetCorrectedFont(g, "Verdana", Font.BOLD, fontSize); bigFont = GetCorrectedFont(g, "Verdana", Font.BOLD, fontSizeBig); g.setFont(mainFont); fm = g.getFontMetrics(mainFont); fb = g.getFontMetrics(bigFont); lineHeight = fm.getHeight(); lineHeightBig = fb.getHeight(); roseRadius = java.lang.Math.min((location.width * 3) / 4, location.height) / 2; if (northCentered) { // scale(location.width, location.height, null, 0); // super.doDraw(g, options); drawFullRose(g, 0, Color.White, LIGHT_GREY, Color.White, LIGHT_GREY, ROSE_BORDER_COLOR, ROSE_TEXT_COLOR, 1.0f, true, true); } else { int radius = (int) (roseRadius * 0.75f); g.setPen(new Pen(ROSE_BORDER_COLOR, Pen.SOLID, 3)); g.drawEllipse(location.width / 2 - radius, location.height / 2 - radius, 2 * radius, 2 * radius); } drawArrows(g); drawWayPointData(g); drawGpsData(g); drawLuminaryData(g); drawGpsStatus(g); } private void drawWayPointData(Graphics g) { String strTemp = MyLocale.getMsg(1512, "Waypoint"); String strTempVal = "?"; g.setColor(Color.DarkBlue); g.fillRect(0, 0, fm.getTextWidth(strTemp) + 4, lineHeight); g.setColor(Color.White); g.drawText(strTemp, 2, 0); g.setColor(Color.Black); int metricSystem = Preferences.itself().metricSystem; Double tmp = new Double(); strTemp = ""; double newDistance = 0; int bigUnit = -1; int smallUnit = -1; double threshold = -1; // Allow for different metric systems if (metricSystem == Metrics.IMPERIAL) { // Why these values? See: http://tinyurl.com/b4nn9m bigUnit = Metrics.MILES; smallUnit = Metrics.FEET; threshold = 0.1; newDistance = Metrics.convertUnit(distance, Metrics.KILOMETER, Metrics.MILES); } else { bigUnit = Metrics.KILOMETER; smallUnit = Metrics.METER; threshold = 1.0; newDistance = distance; } if (newDistance >= 0.0f) { tmp.set(newDistance); if (tmp.value >= threshold) { strTempVal = MyLocale.formatDouble(tmp, "0.000"); strTemp = strTempVal + " " + Metrics.getUnit(bigUnit); } else { tmp.set(Metrics.convertUnit(tmp.value, bigUnit, smallUnit)); strTempVal = tmp.toString(3, 0, 0); strTemp = strTempVal + " " + Metrics.getUnit(smallUnit); } } else { strTempVal = "---"; strTemp = strTempVal + " " + Metrics.getUnit(bigUnit); } // Draw distance inside the compass rose g.setFont(bigFont); g.setPen(new Pen(ROSE_BORDER_COLOR, Pen.SOLID, 1)); g.setBrush(new Brush(Color.White, Brush.SOLID)); int ellipseWidth = fb.getTextWidth(strTempVal) * 5 / 4; int ellipseHeight = lineHeightBig * 5 / 4; g.fillEllipse((location.width - ellipseWidth) / 2, (location.height - ellipseHeight) / 2, ellipseWidth, ellipseHeight); g.setColor(Color.Black); g.drawText(strTempVal, (location.width - fb.getTextWidth(strTempVal)) / 2, (location.height - lineHeightBig) / 2); g.setFont(mainFont); g.drawText(strTemp, 2, lineHeight); tmp.set(gotoDir); if ((tmp.value <= 360) && (tmp.value >= -360)) strTemp = tmp.toString(0, 0, 0) + " " + MyLocale.getMsg(1502, "deg"); else strTemp = "---" + " " + MyLocale.getMsg(1502, "deg"); g.drawText(strTemp, 2, 2 * lineHeight); } private void drawGpsData(Graphics g) { g.setColor(RED); String strHeadline = MyLocale.getMsg(1501, "Current"); Double tmp = new Double(); String strSpeed = null; String unit = null; // Allow for different metric systems if (Preferences.itself().metricSystem == Metrics.IMPERIAL) { tmp.set(Metrics.convertUnit(m_speed, Metrics.KILOMETER, Metrics.MILES)); unit = " mph"; strSpeed = "- mph"; } else { tmp.set(m_speed); unit = " km/h"; strSpeed = "- km/h"; } if (tmp.value >= 0) { if (tmp.value >= 100) { strSpeed = MyLocale.formatDouble(tmp, "0") + unit; } else { strSpeed = MyLocale.formatDouble(tmp, "0.0") + unit; } } tmp.set(moveDir); String strMoveDir = "---" + " " + MyLocale.getMsg(1502, "deg"); if ((tmp.value <= 360) && (tmp.value >= -360)) strMoveDir = tmp.toString(0, 0, 0) + " " + MyLocale.getMsg(1502, "deg"); int textWidth = java.lang.Math.max(fm.getTextWidth(strSpeed), fm.getTextWidth(strMoveDir)); textWidth = java.lang.Math.max(textWidth, fm.getTextWidth(strHeadline)); int startX = location.width - (textWidth + 4); g.fillRect(startX, 0, location.width - startX, lineHeight); g.setColor(Color.Black); g.drawText(strHeadline, startX + 2, 0); g.drawText(strSpeed, startX + 2, lineHeight); g.drawText(strMoveDir, startX + 2, 2 * lineHeight); } private void drawLuminaryData(Graphics g) { g.setColor(YELLOW); String strSunDir = "---" + " " + MyLocale.getMsg(1502, "deg"); if (sunDir < 360 && sunDir > -360) { Double tmp = new Double(); tmp.set(sunDir); strSunDir = tmp.toString(0, 0, 0) + " " + MyLocale.getMsg(1502, "deg"); } int textWidth = java.lang.Math.max(fm.getTextWidth(m_Luminary), fm.getTextWidth(strSunDir)); int startY = location.height - 2 * lineHeight; g.fillRect(0, startY, textWidth + 4, location.height - startY); g.setColor(Color.Black); g.drawText(m_Luminary, 2, startY); g.drawText(strSunDir, 2, startY + lineHeight); } private void drawGpsStatus(Graphics g) { if ((m_fix > 0) && (m_sats >= 0)) { // Set background to signal quality g.setColor(GREEN); } else // receiving data, but signal ist not good if ((m_fix == 0) && (m_sats >= 0)) { g.setColor(YELLOW); } else { g.setColor(RED); } String strSats = "Sats: -"; if (m_sats >= 0) { strSats = "Sats: " + Convert.toString(m_sats) + "/" + Convert.toString(m_satsInView); } String strHdop = "HDOP: -"; if (m_hdop >= 0) strHdop = "HDOP: " + Convert.toString(m_hdop); int textWidth = java.lang.Math.max(fm.getTextWidth(strSats), fm.getTextWidth(strHdop)); int startX = location.width - (textWidth + 4); int startY = location.height - 2 * lineHeight; g.fillRect(startX, startY, location.width - startX, location.height - startY); g.setColor(Color.Black); g.drawText(strSats, startX + 2, startY); g.drawText(strHdop, startX + 2, startY + lineHeight); } private void drawArrows(Graphics g) { if (g != null) { // select moveDirColor according to difference to gotoDir Color moveDirColor = RED; if (gotoDir < 360 && gotoDir > -360 && moveDir < 360 && moveDir > -360) { float diff = java.lang.Math.abs(moveDir - gotoDir); while (diff > 360) { diff -= 360.0f; } if (diff > 180.0f) { diff = 360.0f - diff; } if (diff <= 12.25f) { moveDirColor = GREEN; } else if (diff <= 22.5f) { moveDirColor = CYAN; } else if (diff <= 45.0f) { moveDirColor = ORANGE; } else if (diff <= 90.0f) { moveDirColor = MAGENTA; } } // draw only valid arrows if (northCentered) { if (gotoDir < 360 && gotoDir > -360) drawThickArrow(g, gotoDir, Color.DarkBlue, 1.0f); if (moveDir < 360 && moveDir > -360) drawThinArrow(g, moveDir, RED, moveDirColor, 1.0f); if (sunDir < 360 && sunDir > -360) drawSunArrow(g, sunDir, YELLOW, 0.75f); } else { // moveDir centered if (moveDir < 360 && moveDir > -360) { // drawDoubleArrow(g, 360 - moveDir, BLUE, new Color(175,0,0), 1.0f); // drawRose(g, 360 - moveDir, new Color(100,100,100), new Color(200,200,200), 1.0f); drawFullRose(g, 360 - moveDir, Color.White, LIGHT_GREY, GREY, LIGHT_GREY, ROSE_BORDER_COLOR, ROSE_TEXT_COLOR, 1.0f, false, false); int radius = (int) (roseRadius * 0.75f); g.setPen(new Pen(RED, Pen.SOLID, 3)); g.drawLine(location.width / 2, location.height / 2 - radius, location.width / 2, location.height / 2 + radius); if (gotoDir < 360 && gotoDir > -360) drawThinArrow(g, gotoDir - moveDir, Color.DarkBlue, moveDirColor, 1.0f); if (sunDir < 360 && sunDir > -360) drawSunArrow(g, sunDir - moveDir, YELLOW, 0.75f); } } } } private void drawSunArrow(Graphics g, float angle, Color col, float scale) { float angleRad = (angle) * (float) java.lang.Math.PI / 180; int centerX = location.width / 2, centerY = location.height / 2; float arrowLength = roseRadius * scale; float halfArrowWidth = arrowLength * 0.08f; float circlePos = arrowLength * 0.7f; int circleRadius = (int) (arrowLength * 0.1f); int circleX = centerX + new Float(circlePos * java.lang.Math.sin(angleRad)).intValue(); int circleY = centerY - new Float(circlePos * java.lang.Math.cos(angleRad)).intValue(); int[] pointsX = new int[4]; int[] pointsY = new int[4]; pointsX[0] = centerX + new Float(arrowLength * java.lang.Math.sin(angleRad)).intValue(); pointsY[0] = centerY - new Float(arrowLength * java.lang.Math.cos(angleRad)).intValue(); pointsX[1] = centerX + new Float(halfArrowWidth * java.lang.Math.sin(angleRad + java.lang.Math.PI / 2.0)).intValue(); pointsY[1] = centerY - new Float(halfArrowWidth * java.lang.Math.cos(angleRad + java.lang.Math.PI / 2.0)).intValue(); pointsX[2] = centerX + new Float(halfArrowWidth * java.lang.Math.sin(angleRad + java.lang.Math.PI)).intValue(); pointsY[2] = centerY - new Float(halfArrowWidth * java.lang.Math.cos(angleRad + java.lang.Math.PI)).intValue(); pointsX[3] = centerX + new Float(halfArrowWidth * java.lang.Math.sin(angleRad - java.lang.Math.PI / 2.0)).intValue(); pointsY[3] = centerY - new Float(halfArrowWidth * java.lang.Math.cos(angleRad - java.lang.Math.PI / 2.0)).intValue(); // g.setPen(new Pen(col,Pen.SOLID,3)); // g.drawLine(centerX,centerY,pointX,pointY); g.setPen(new Pen(Color.Black, Pen.SOLID, 1)); g.setBrush(new Brush(col, Brush.SOLID)); g.fillPolygon(pointsX, pointsY, 4); g.fillEllipse(circleX - circleRadius, circleY - circleRadius, 2 * circleRadius, 2 * circleRadius); } private void drawThinArrow(Graphics g, float angle, Color col, Color colPoint, float scale) { float angleRad = (angle) * (float) java.lang.Math.PI / 180; int centerX = location.width / 2, centerY = location.height / 2; float arrowLength = roseRadius * scale; float halfOpeningAngle = (float) (java.lang.Math.PI * 0.03); float sideLineLength = arrowLength * 0.75f; int[] pointsX = new int[4]; int[] pointsY = new int[4]; pointsX[0] = centerX + new Float(sideLineLength * java.lang.Math.sin(angleRad - halfOpeningAngle)).intValue(); pointsY[0] = centerY - new Float(sideLineLength * java.lang.Math.cos(angleRad - halfOpeningAngle)).intValue(); pointsX[1] = centerX + new Float(arrowLength * java.lang.Math.sin(angleRad)).intValue(); pointsY[1] = centerY - new Float(arrowLength * java.lang.Math.cos(angleRad)).intValue(); pointsX[2] = centerX + new Float(sideLineLength * java.lang.Math.sin(angleRad + halfOpeningAngle)).intValue(); pointsY[2] = centerY - new Float(sideLineLength * java.lang.Math.cos(angleRad + halfOpeningAngle)).intValue(); pointsX[3] = centerX; pointsY[3] = centerY; g.setPen(new Pen(Color.Black, Pen.SOLID, 1)); g.setBrush(new Brush(col, Brush.SOLID)); g.fillPolygon(pointsX, pointsY, 4); if (colPoint != null) { g.setBrush(new Brush(colPoint, Brush.SOLID)); g.fillPolygon(pointsX, pointsY, 3); } } private void drawFullRose(Graphics g, float angle, Color colLeft, Color colRight, Color colNorthLeft, Color colNorthRight, Color colBorder, Color colText, float scale, boolean bDrawText, boolean bDrawEightArrows) { float subScale1 = 1.0f; float subScale2 = 0.9f; float innerScale = 0.15f; if (bDrawEightArrows) { innerScale = 0.12f; drawRosePart(g, 45 + angle, colLeft, colRight, colBorder, colText, scale * subScale2, innerScale, "NE", bDrawText); drawRosePart(g, 135 + angle, colLeft, colRight, colBorder, colText, scale * subScale2, innerScale, "SE", bDrawText); drawRosePart(g, 225 + angle, colLeft, colRight, colBorder, colText, scale * subScale2, innerScale, "SW", bDrawText); drawRosePart(g, 315 + angle, colLeft, colRight, colBorder, colText, scale * subScale2, innerScale, "NW", bDrawText); } drawRosePart(g, 0 + angle, colNorthLeft, colNorthRight, colBorder, colText, scale * subScale1, innerScale, "N", bDrawText); drawRosePart(g, 90 + angle, colLeft, colRight, colBorder, colText, scale * subScale1, innerScale, "E", bDrawText); drawRosePart(g, 180 + angle, colLeft, colRight, colBorder, colText, scale * subScale1, innerScale, "S", bDrawText); drawRosePart(g, 270 + angle, colLeft, colRight, colBorder, colText, scale * subScale1, innerScale, "W", bDrawText); } private void drawRosePart(Graphics g, float angle, Color colLeft, Color colRight, Color colBorder, Color colText, float scale, float innerScale, String strDir, boolean bDrawText) { float angleRad = angle * (float) java.lang.Math.PI / 180; float angleRadText = (angle + 7.5f) * (float) java.lang.Math.PI / 180; int centerX = location.width / 2, centerY = location.height / 2; float arrowLength = roseRadius * scale; float halfArrowWidth = arrowLength * innerScale; int[] pointsX = new int[3]; int[] pointsY = new int[3]; pointsX[0] = centerX; pointsY[0] = centerY; pointsX[1] = centerX + new Float(arrowLength * java.lang.Math.sin(angleRad)).intValue(); pointsY[1] = centerY - new Float(arrowLength * java.lang.Math.cos(angleRad)).intValue(); pointsX[2] = centerX + new Float(halfArrowWidth * java.lang.Math.sin(angleRad - java.lang.Math.PI / 4.0)).intValue(); pointsY[2] = centerY - new Float(halfArrowWidth * java.lang.Math.cos(angleRad - java.lang.Math.PI / 4.0)).intValue(); g.setPen(new Pen(colBorder, Pen.SOLID, 1)); g.setBrush(new Brush(colLeft, Brush.SOLID)); g.fillPolygon(pointsX, pointsY, 3); pointsX[2] = centerX + new Float(halfArrowWidth * java.lang.Math.sin(angleRad + java.lang.Math.PI / 4.0)).intValue(); pointsY[2] = centerY - new Float(halfArrowWidth * java.lang.Math.cos(angleRad + java.lang.Math.PI / 4.0)).intValue(); g.setBrush(new Brush(colRight, Brush.SOLID)); g.fillPolygon(pointsX, pointsY, 3); if (bDrawText) { int tempFontSize = new Float(scale * mainFont.getSize()).intValue(); Font tempFont = new Font(mainFont.getName(), Font.BOLD, tempFontSize); g.setFont(tempFont); FontMetrics tempFm = g.getFontMetrics(tempFont); float stringHeight = tempFm.getHeight(); float stringWidth = tempFm.getTextWidth(strDir); float stringGap = (float) java.lang.Math.sqrt(stringHeight * stringHeight + stringWidth * stringWidth); float stringPosition = arrowLength - stringGap / 2.0f; g.setColor(colText); g.drawText(strDir, centerX + new Float(stringPosition * java.lang.Math.sin(angleRadText) - stringWidth / 2.0f).intValue(), centerY - new Float(stringPosition * java.lang.Math.cos(angleRadText) + stringHeight / 2.0f).intValue()); g.setFont(mainFont); } } private void drawThickArrow(Graphics g, float angle, Color col, float scale) { float angleRad = (angle) * (float) java.lang.Math.PI / 180; int centerX = location.width / 2, centerY = location.height / 2; float arrowLength = roseRadius * scale; float halfArrowWidth = arrowLength * 0.1f; int[] pointsX = new int[4]; int[] pointsY = new int[4]; pointsX[0] = centerX + new Float(arrowLength * java.lang.Math.sin(angleRad)).intValue(); pointsY[0] = centerY - new Float(arrowLength * java.lang.Math.cos(angleRad)).intValue(); pointsX[1] = centerX + new Float(halfArrowWidth * java.lang.Math.sin(angleRad + java.lang.Math.PI / 2.0)).intValue(); pointsY[1] = centerY - new Float(halfArrowWidth * java.lang.Math.cos(angleRad + java.lang.Math.PI / 2.0)).intValue(); pointsX[2] = centerX + new Float(halfArrowWidth * java.lang.Math.sin(angleRad + java.lang.Math.PI)).intValue(); pointsY[2] = centerY - new Float(halfArrowWidth * java.lang.Math.cos(angleRad + java.lang.Math.PI)).intValue(); pointsX[3] = centerX + new Float(halfArrowWidth * java.lang.Math.sin(angleRad - java.lang.Math.PI / 2.0)).intValue(); pointsY[3] = centerY - new Float(halfArrowWidth * java.lang.Math.cos(angleRad - java.lang.Math.PI / 2.0)).intValue(); g.setPen(new Pen(Color.Black, Pen.SOLID, 1)); g.setBrush(new Brush(col, Brush.SOLID)); g.fillPolygon(pointsX, pointsY, 4); } public void setNorthCentered(boolean nc) { northCentered = nc; if (northCentered != Preferences.itself().northCenteredGoto) { Preferences.itself().northCenteredGoto = northCentered; Preferences.itself().savePreferences(); } refresh(); } public boolean isNorthCentered() { return northCentered; } public static Font GetCorrectedFont(Graphics g, String name, int style, int size) { Font newFont = new Font(name, style, size); FontMetrics metrics = g.getFontMetrics(newFont); int fontHeight = metrics.getHeight(); float ratio = (float) fontHeight / (float) size; if (ratio < 0.9 || ratio > 1.1) { size = (int) (size / ratio + 0.5); if (size < 5) size = 5; newFont = new Font(name, style, size); } return newFont; } }