From b5cebe9a6bf2976a1d95d86aeb507324b94cf735 Mon Sep 17 00:00:00 2001 From: sonora Date: Tue, 4 Sep 2012 11:19:56 +0200 Subject: [PATCH] Try solution for Issue 1313 --- .../src/net/osmand/SunriseSunset.java | 272 +++--- .../osmand/SunriseSunset.java_EPA-original | 827 ++++++++++++++++++ .../development/OsmandDevelopmentPlugin.java | 12 +- 3 files changed, 950 insertions(+), 161 deletions(-) create mode 100644 DataExtractionOSM/src/net/osmand/SunriseSunset.java_EPA-original diff --git a/DataExtractionOSM/src/net/osmand/SunriseSunset.java b/DataExtractionOSM/src/net/osmand/SunriseSunset.java index 9f65226cb0..8b7bc25d9f 100644 --- a/DataExtractionOSM/src/net/osmand/SunriseSunset.java +++ b/DataExtractionOSM/src/net/osmand/SunriseSunset.java @@ -171,11 +171,7 @@ package net.osmand; * *----------------------------------------------------------------------------*/ -// Import required classes and packages -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.text.NumberFormat; -import java.text.DecimalFormat; +import java.util.Calendar; import java.util.Date; import java.util.TimeZone; @@ -218,15 +214,10 @@ public class SunriseSunset private int iDay; // day of date of interest private int iCount; // a simple counter private int iSign; // SUNUP.BAS: S - private double dfHourRise, dfHourSet; // hour of event: SUNUP.BAS H3 - private double dfMinRise, dfMinSet; // minute of event: SUNUP.BAS M3 + private int dfHourRise, dfHourSet; // hour of event: SUNUP.BAS H3 + private int dfMinRise, dfMinSet; // minute of event: SUNUP.BAS M3 private double dfSinLat, dfCosLat; // sin and cos of latitude private double dfZenith; // SUNUP.BAS Z: Zenith -// private SimpleDateFormat dfmtDate; // formatting for date alone - private SimpleDateFormat dfmtDateTime; // formatting for date and time - private SimpleDateFormat dfmtYear; // formatting for year - private SimpleDateFormat dfmtMonth; // formatting for month - private SimpleDateFormat dfmtDay; // formatting for day // Many variables in SUNUP.BAS have undocumented meanings, // and so are translated rather directly to avoid confusion: private double dfAA1 = 0, dfAA2 = 0; // SUNUP.BAS A(2) @@ -245,9 +236,6 @@ public class SunriseSunset private double dfT, dfT0, dfTT; // SUNUP.BAS T, T0, TT private double dfV0, dfV1, dfV2; // SUNUP.BAS V0, V1, V2 - private TimeZone tz = TimeZone.getTimeZone( "GMT" ); - private double origTimeZone; - /****************************************************************************** * method: SunriseSunset @@ -273,7 +261,6 @@ public class SunriseSunset dfLon = dfLonIn; dateInput = dateInputIn; dfTimeZone = dfTimeZoneIn; - origTimeZone= dfTimeZoneIn; // Call the method to do the calculations. doCalculations(); @@ -290,30 +277,18 @@ public class SunriseSunset *----------------------------------------------------------------------------*/ private void doCalculations() { - try - { - // Break out day, month, and year from date provided. - // (This is necesary for the math algorithms.) - - dfmtYear = new SimpleDateFormat( "yyyy" ); - dfmtYear.setLenient( false ); - dfmtYear.setTimeZone( tz ); - - dfmtMonth = new SimpleDateFormat( "M" ); - dfmtMonth.setLenient( false ); - dfmtMonth.setTimeZone( tz ); - - dfmtDay = new SimpleDateFormat( "d" ); - dfmtDay.setLenient( false ); - dfmtDay.setTimeZone( tz ); - - iYear = Integer.parseInt( dfmtYear.format( dateInput ) ); - iMonth = Integer.parseInt( dfmtMonth.format( dateInput ) ); - iDay = Integer.parseInt( dfmtDay.format( dateInput ) ); - + // Break out day, month, and year from date provided using local time zone. + // (This is necessary for the math algorithms.) + Calendar cin = Calendar.getInstance(); + cin.setTime(dateInput); + + iYear = cin.get(Calendar.YEAR); + iMonth = cin.get(Calendar.MONTH) + 1; + iDay = cin.get(Calendar.DAY_OF_MONTH); + // Convert time zone hours to decimal days (SUNUP.BAS line 50) dfTimeZone = dfTimeZone / 24.0; - + // NOTE: (7 Feb 2001) Here is a non-standard part of SUNUP.BAS: // It (and this algorithm) assumes that the time zone is // positive west, instead of the standard negative west. @@ -321,10 +296,10 @@ public class SunriseSunset // times zones are specified in negative west, so here the // sign is changed so that the SUNUP algorithm works: dfTimeZone = -dfTimeZone; - + // Convert longitude to fraction (SUNUP.BAS line 50) dfLon = dfLon / 360.0; - + // Convert calendar date to Julian date: // Check to see if it's later than 1583: Gregorian calendar // When declared, bGregorian is initialized to false. @@ -332,18 +307,18 @@ public class SunriseSunset if( iYear >= 1583 ) bGregorian = true; // SUNUP.BAS 1210 dfJ = -Math.floor( 7.0 // SUNUP used INT, not floor - * ( Math.floor( - ( iMonth + 9.0 ) - / 12.0 - ) + iYear - ) / 4.0 - ) - // add SUNUP.BAS 1240 and 1250 for G = 0 - + Math.floor( iMonth * 275.0 / 9.0 ) - + iDay - + 1721027.0 - + iYear * 367.0; - + * ( Math.floor( + ( iMonth + 9.0 ) + / 12.0 + ) + iYear + ) / 4.0 + ) + // add SUNUP.BAS 1240 and 1250 for G = 0 + + Math.floor( iMonth * 275.0 / 9.0 ) + + iDay + + 1721027.0 + + iYear * 367.0; + if ( bGregorian ) { // SUNUP.BAS 1230 @@ -352,22 +327,22 @@ public class SunriseSunset dfA = Math.abs( iMonth - 9.0 ); // SUNUP.BAS 1240 and 1250 dfJ3 = -Math.floor( - ( - Math.floor( - Math.floor( iYear - + (double)iSign - * Math.floor( dfA / 7.0 ) - ) - / 100.0 - ) + 1.0 - ) * 0.75 - ); + ( + Math.floor( + Math.floor( iYear + + (double)iSign + * Math.floor( dfA / 7.0 ) + ) + / 100.0 + ) + 1.0 + ) * 0.75 + ); // correct dfJ as in SUNUP.BAS 1240 and 1250 for G = 1 dfJ = dfJ + dfJ3 + 2.0; } // SUNUP.BAS 1290 iJulian = (int)dfJ - 1; - + // SUNUP.BAS 60 and 70 (see also line 1290) dfT = (double)iJulian - 2451545.0 + 0.5; dfTT = dfT / 36525.0 + 1.0; // centuries since 1900 @@ -378,13 +353,13 @@ public class SunriseSunset + 24110.5 + dfTimeZone * 86636.6 + dfLon * 86400.0 - ) - / 86400.0; + ) + / 86400.0; dfT0 = dfT0 - Math.floor( dfT0 ); // NOTE: SUNUP.BAS uses INT() dfT0 = dfT0 * 2.0 * Math.PI; // SUNUP.BAS 90 dfT = dfT + dfTimeZone; - + // SUNUP.BAS 110: Get Sun's position for( iCount=0; iCount<=1; iCount++ ) // Loop thru only twice { @@ -392,7 +367,7 @@ public class SunriseSunset // at the start and end of each day. // SUNUP.BAS 910 - 1160: Fundamental arguments // from van Flandern and Pulkkinen, 1979 - + // declare local temporary doubles for calculations double dfGG; // SUNUP.BAS G double dfLL; // SUNUP.BAS L @@ -413,27 +388,27 @@ public class SunriseSunset - 0.01000 * Math.sin( dfLL - dfGG ) + 0.00333 * Math.sin( dfLL + dfGG ) - 0.00021 * Math.sin( dfLL ) * dfTT; - + dfUU = 1 - - 0.03349 * Math.cos( dfGG ) + - 0.03349 * Math.cos( dfGG ) - 0.00014 * Math.cos( dfLL * 2.0 ) + 0.00008 * Math.cos( dfLL ); - + dfWW = - 0.00010 - 0.04129 * Math.sin( dfLL * 2.0 ) + 0.03211 * Math.sin( dfGG ) - 0.00104 * Math.sin( 2.0 * dfLL - dfGG ) - 0.00035 * Math.sin( 2.0 * dfLL + dfGG ) - 0.00008 * Math.sin( dfGG ) * dfTT; - + // Compute Sun's RA and Dec; SUNUP.BAS 1120 - 1140 dfSS = dfWW / Math.sqrt( dfUU - dfVV * dfVV ); dfA5 = dfLL - + Math.atan( dfSS / Math.sqrt( 1.0 - dfSS * dfSS )); - + + Math.atan( dfSS / Math.sqrt( 1.0 - dfSS * dfSS )); + dfSS = dfVV / Math.sqrt( dfUU ); dfD5 = Math.atan( dfSS / Math.sqrt( 1 - dfSS * dfSS )); - + // Set values and increment t if ( iCount == 0 ) // SUNUP.BAS 125 { @@ -447,30 +422,30 @@ public class SunriseSunset } dfT = dfT + 1.0; // SUNUP.BAS 130 } // end of Get Sun's Position for loop - + if ( dfAA2 < dfAA1 ) dfAA2 = dfAA2 + 2.0 * Math.PI; - // SUNUP.BAS 150 + // SUNUP.BAS 150 dfZenith = Math.PI * 90.833 / 180.0; // SUNUP.BAS 160 dfSinLat = Math.sin( dfLat * Math.PI / 180.0 ); // SUNUP.BAS 170 dfCosLat = Math.cos( dfLat * Math.PI / 180.0 ); // SUNUP.BAS 170 - + dfA0 = dfAA1; // SUNUP.BAS 190 dfD0 = dfDD1; // SUNUP.BAS 190 dfDA = dfAA2 - dfAA1; // SUNUP.BAS 200 dfDD = dfDD2 - dfDD1; // SUNUP.BAS 200 - + dfK1 = 15.0 * 1.0027379 * Math.PI / 180.0; // SUNUP.BAS 330 // Initialize sunrise and sunset times, and other variables // hr and min are set to impossible times to make errors obvious - dfHourRise = 99.0; - dfMinRise = 99.0; - dfHourSet = 99.0; - dfMinSet = 99.0; + dfHourRise = 99; + dfMinRise = 99; + dfHourSet = 99; + dfMinSet = 99; dfV0 = 0.0; // initialization implied by absence in SUNUP.BAS dfV2 = 0.0; // initialization implied by absence in SUNUP.BAS - + // Test each hour to see if the Sun crosses the horizon // and which way it is heading. for( iCount=0; iCount<24; iCount++ ) // SUNUP.BAS 210 @@ -479,7 +454,7 @@ public class SunriseSunset double tempB; // SUNUP.BAS B double tempD; // SUNUP.BAS D double tempE; // SUNUP.BAS E - + dfC0 = (double)iCount; dfP = ( dfC0 + 1.0 ) / 24.0; // SUNUP.BAS 220 dfA2 = dfAA1 + dfP * dfDA; // SUNUP.BAS 230 @@ -492,44 +467,44 @@ public class SunriseSunset dfH1 = ( dfH2 + dfH0 ) / 2.0; // SUNUP.BAS 520 // declination at half hour dfD1 = ( dfD2 + dfD0 ) / 2.0; // SUNUP.BAS 530 - + // Set value of dfV0 only if this is the first hour, // otherwise, it will get set to the last dfV2 (SUNUP.BAS 250) if ( iCount == 0 ) // SUNUP.BAS 550 { dfV0 = dfSinLat * Math.sin( dfD0 ) - + dfCosLat * Math.cos( dfD0 ) * Math.cos( dfH0 ) - - Math.cos( dfZenith ); // SUNUP.BAS 560 + + dfCosLat * Math.cos( dfD0 ) * Math.cos( dfH0 ) + - Math.cos( dfZenith ); // SUNUP.BAS 560 } else dfV0 = dfV2; // That is, dfV2 from the previous hour. - + dfV2 = dfSinLat * Math.sin( dfD2 ) - + dfCosLat * Math.cos( dfD2 ) * Math.cos( dfH2 ) - - Math.cos( dfZenith ); // SUNUP.BAS 570 - + + dfCosLat * Math.cos( dfD2 ) * Math.cos( dfH2 ) + - Math.cos( dfZenith ); // SUNUP.BAS 570 + // if dfV0 and dfV2 have the same sign, then proceed to next hr if ( - ( dfV0 >= 0.0 && dfV2 >= 0.0 ) // both are positive - || // or - ( dfV0 < 0.0 && dfV2 < 0.0 ) // both are negative - ) + ( dfV0 >= 0.0 && dfV2 >= 0.0 ) // both are positive + || // or + ( dfV0 < 0.0 && dfV2 < 0.0 ) // both are negative + ) { // Break iteration and proceed to test next hour dfA0 = dfA2; // SUNUP.BAS 250 dfD0 = dfD2; // SUNUP.BAS 250 continue; // SUNUP.BAS 610 } - + dfV1 = dfSinLat * Math.sin( dfD1 ) - + dfCosLat * Math.cos( dfD1 ) * Math.cos( dfH1 ) - - Math.cos( dfZenith ); // SUNUP.BAS 590 + + dfCosLat * Math.cos( dfD1 ) * Math.cos( dfH1 ) + - Math.cos( dfZenith ); // SUNUP.BAS 590 tempA = 2.0 * dfV2 - 4.0 * dfV1 + 2.0 * dfV0; - // SUNUP.BAS 600 + // SUNUP.BAS 600 tempB = 4.0 * dfV1 - 3.0 * dfV0 - dfV2; // SUNUP.BAS 600 tempD = tempB * tempB - 4.0 * tempA * dfV0; // SUNUP.BAS 610 - + if ( tempD < 0.0 ) { // Break iteration and proceed to test next hour @@ -537,11 +512,11 @@ public class SunriseSunset dfD0 = dfD2; // SUNUP.BAS 250 continue; // SUNUP.BAS 610 } - + tempD = Math.sqrt( tempD ); // SUNUP.BAS 620 // Determine occurence of sunrise or sunset. - + // Flags to identify occurrence during this day are // bSunriseToday and bSunsetToday, and are initialized false. // These are set true only if sunrise or sunset occurs @@ -550,7 +525,7 @@ public class SunriseSunset // Flags to identify occurrence during this hour: bSunrise = false; // reset before test bSunset = false; // reset before test - + if ( dfV0 < 0.0 && dfV2 > 0.0 ) // sunrise occurs this hour { bSunrise = true; // SUNUP.BAS 640 @@ -562,41 +537,41 @@ public class SunriseSunset bSunset = true; // SUNUP.BAS 660 bSunsetToday = true; // sunset occurred today } - + tempE = ( tempD - tempB ) / ( 2.0 * tempA ); if ( tempE > 1.0 || tempE < 0.0 ) // SUNUP.BAS 670, 680 tempE = ( -tempD - tempB ) / ( 2.0 * tempA ); - + // Set values of hour and minute of sunset or sunrise // only if sunrise/set occurred this hour. if ( bSunrise ) { - dfHourRise = Math.floor( dfC0 + tempE + 1.0/120.0 ); - dfMinRise = Math.floor( - ( dfC0 + tempE + 1.0/120.0 - - dfHourRise - ) - * 60.0 - ); + dfHourRise = (int)( dfC0 + tempE + 1.0/120.0 ); + dfMinRise = (int) ( + ( dfC0 + tempE + 1.0/120.0 + - dfHourRise + ) + * 60.0 + ); } if ( bSunset ) { - dfHourSet = Math.floor( dfC0 + tempE + 1.0/120.0 ); - dfMinSet = Math.floor( - ( dfC0 + tempE + 1.0/120.0 - - dfHourSet - ) - * 60.0 - ); + dfHourSet = (int) ( dfC0 + tempE + 1.0/120.0 ); + dfMinSet = (int)( + ( dfC0 + tempE + 1.0/120.0 + - dfHourSet + ) + * 60.0 + ); } // Change settings of variables for next loop dfA0 = dfA2; // SUNUP.BAS 250 dfD0 = dfD2; // SUNUP.BAS 250 - + } // end of loop testing each hour for an event - + // After having checked all hours, set flags if no rise or set // bSunUpAllDay and bSundownAllDay are initialized as false if ( !bSunriseToday && !bSunsetToday ) @@ -608,48 +583,29 @@ public class SunriseSunset } // Load dateSunrise with data - dfmtDateTime = new SimpleDateFormat( "d M yyyy HH:mm z" ); - - // Timezone signal is reversed in SunriseSunset class - String tz_signal = origTimeZone <= 0?"-":"+"; - double abs_tz = Math.abs(origTimeZone); - NumberFormat formatter = new DecimalFormat("00"); - - String tz_offset_hours = formatter.format((int)abs_tz); - String tz_offset_minutes = formatter.format((int)(60 * (abs_tz - (int)abs_tz))); - + if( bSunriseToday ) { - dateSunrise = dfmtDateTime.parse( iDay - + " " + iMonth - + " " + iYear - + " " + (int)dfHourRise - + ":" + (int)dfMinRise - + " GMT" - + tz_signal + tz_offset_hours - +":" + tz_offset_minutes ); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, iYear); + c.set(Calendar.MONTH, iMonth-1); + c.set(Calendar.DAY_OF_MONTH, iDay); + c.set(Calendar.HOUR_OF_DAY, dfHourRise); + c.set(Calendar.MINUTE, dfMinRise); + dateSunrise = c.getTime(); } - + // Load dateSunset with data if( bSunsetToday ) { - dateSunset = dfmtDateTime.parse( iDay - + " " + iMonth - + " " + iYear - + " " + (int)dfHourSet - + ":" + (int)dfMinSet - + " GMT" - + tz_signal + tz_offset_hours - +":" + tz_offset_minutes ); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, iYear); + c.set(Calendar.MONTH, iMonth-1); + c.set(Calendar.DAY_OF_MONTH, iDay); + c.set(Calendar.HOUR_OF_DAY, dfHourSet); + c.set(Calendar.MINUTE, dfMinSet); + dateSunset = c.getTime(); } - } // end of try - - // Catch errors - catch( ParseException e ) - { - e.printStackTrace(); - } // end of catch - } @@ -819,9 +775,9 @@ public class SunriseSunset return( bDaytime ); } - } // end of class /*----------------------------------------------------------------------------- * end of class -*----------------------------------------------------------------------------*/ \ No newline at end of file +*----------------------------------------------------------------------------*/ + diff --git a/DataExtractionOSM/src/net/osmand/SunriseSunset.java_EPA-original b/DataExtractionOSM/src/net/osmand/SunriseSunset.java_EPA-original new file mode 100644 index 0000000000..9f65226cb0 --- /dev/null +++ b/DataExtractionOSM/src/net/osmand/SunriseSunset.java_EPA-original @@ -0,0 +1,827 @@ +package net.osmand; +/****************************************************************************** +* +* SunriseSunset.java +* +******************************************************************************* +* +* Java Class: SunriseSunset +* +* This Java class is part of a collection of classes developed for the +* reading and processing of oceanographic and meterological data collected +* since 1970 by environmental buoys and stations. This dataset is +* maintained by the National Oceanographic Data Center and is publicly +* available. These Java classes were written for the US Environmental +* Protection Agency's National Exposure Research Laboratory under Contract +* No. GS-10F-0073K with Neptune and Company of Los Alamos, New Mexico. +* +* Purpose: +* +* This Java class performs calculations to determine the time of +* sunrise and sunset given lat, long, and date. +* +* Inputs: +* +* Latitude, longitude, date/time, and time zone. +* +* Outputs: +* +* Local time of sunrise and sunset as calculated by the +* program. +* If no sunrise or no sunset occurs, or if the sun is up all day +* or down all day, appropriate boolean values are set. +* A boolean is provided to identify if the time provided is during the day. +* +* The above values are accessed by the following methods: +* +* Date getSunrise() returns date/time of sunrise +* Date getSunset() returns date/time of sunset +* boolean isSunrise() returns true if there was a sunrise, else false +* boolean isSunset() returns true if there was a sunset, else false +* boolean isSunUp() returns true if sun is up all day, else false +* boolean isSunDown() returns true if sun is down all day, else false +* boolean isDaytime() returns true if sun is up at the time +* specified, else false +* +* Required classes from the Java library: +* +* java.util.Date +* java.text.SimpleDateFormat +* java.text.ParseException; +* java.math.BigDecimal; +* +* Package of which this class is a member: +* +* default +* +* Known limitations: +* +* It is assumed that the data provided are within value ranges +* (i.e. latitude between -90 and +90, longitude between 0 and 360, +* a valid date, and time zone between -14 and +14. +* +* Compatibility: +* +* Java 1.1.8 +* +* References: +* +* The mathematical algorithms used in this program are patterned +* after those debveloped by Roger Sinnott in his BASIC program, +* SUNUP.BAS, published in Sky & Telescope magazine: +* Sinnott, Roger W. "Sunrise and Sunset: A Challenge" +* Sky & Telescope, August, 1994 p.84-85 +* +* The following is a cross-index of variables used in SUNUP.BAS. +* A single definition from multiple reuse of variable names in +* SUNUP.BAS was clarified with various definitions in this program. +* +* SUNUP.BAS this class +* +* A dfA +* A(2) dfAA1, dfAA2 +* A0 dfA0 +* A2 dfA2 +* A5 dfA5 +* AZ Not used +* C dfCosLat +* C0 dfC0 +* D iDay +* D(2) dfDD1, dfDD2 +* D0 dfD0 +* D1 dfD1 +* D2 dfD2 +* D5 dfD5 +* D7 Not used +* DA dfDA +* DD dfDD +* G bGregorian, dfGG +* H dfTimeZone +* H0 dfH0 +* H1 dfH1 +* H2 dfH2 +* H3 dfHourRise, dfHourSet +* H7 Not used +* J dfJ +* J3 dfJ3 +* K1 dfK1 +* L dfLL +* L0 dfL0 +* L2 dfL2 +* L5 dfLon +* M iMonth +* M3 dfMinRise, dfMinSet +* N7 Not used +* P dfP +* S iSign, dfSinLat, dfSS +* T dfT +* T0 dfT0 +* T3 not used +* TT dfTT +* U dfUU +* V dfVV +* V0 dfV0 +* V1 dfV1 +* V2 dfV2 +* W dfWW +* Y iYear +* Z dfZenith +* Z0 dfTimeZone +* +* +* Author/Company: +* +* JDT: John Tauxe, Neptune and Company +* JMG: Jo Marie Green +* +* Change log: +* +* date ver by description of change +* _________ _____ ___ ______________________________________________ +* 5 Jan 01 0.006 JDT Excised from ssapp.java v. 0.005. +* 11 Jan 01 0.007 JDT Minor modifications to comments based on +* material from Sinnott, 1994. +* 7 Feb 01 0.008 JDT Fixed backwards time zone. The standard is that +* local time zone is specified in hours EAST of +* Greenwich, so that EST would be -5, for example. +* For some reason, SUNUP.BAS does this backwards +* (probably an americocentric perspective) and +* SunriseSunset adopted that convention. Oops. +* So the sign in the math is changed. +* 7 Feb 01 0.009 JDT Well, that threw off the azimuth calculation... +* Removed the azimuth calculations. +* 14 Feb 01 0.010 JDT Added ability to accept a time (HH:mm) in +* dateInput, and decide if that time is daytime +* or nighttime. +* 27 Feb 01 0.011 JDT Added accessor methods in place of having public +* variables to get results. +* 28 Feb 01 0.012 JDT Cleaned up list of imported classes. +* 28 Mar 01 1.10 JDT Final version accompanying deliverable 1b. +* 4 Apr 01 1.11 JDT Moved logic supporting .isDaytime into method. +* Moved calculations out of constructor. +* 01 May 01 1.12 JMG Added 'GMT' designation and testing lines. +* 16 May 01 1.13 JDT Added setLenient( false ) and setTimeZone( tz ) +* to dfmtDay, dfmtMonth, and dfmtYear in +* doCalculations. +* 27 Jun 01 1.14 JDT Removed reliance on StationConstants (GMT). +* 13 Aug 01 1.20 JDT Final version accompanying deliverable 1c. +* 6 Sep 01 1.21 JDT Thorough code and comment review. +* 21 Sep 01 1.30 JDT Final version accompanying deliverable 2. +* 17 Dec 01 1.40 JDT Version accompanying final deliverable. +* +*----------------------------------------------------------------------------*/ + +// Import required classes and packages +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.text.NumberFormat; +import java.text.DecimalFormat; +import java.util.Date; +import java.util.TimeZone; + + +/****************************************************************************** +* class: SunriseSunset class +******************************************************************************* +* +* This Java class performs calculations to determine the time of +* sunrise and sunset given lat, long, and date. +* +* It is assumed that the data provided are within value ranges +* (i.e. latitude between -90 and +90, longitude between 0 and 360, +* a valid date, and time zone between -14 and +14. +* +*----------------------------------------------------------------------------*/ +public class SunriseSunset +{ + + // Declare and initialize variables + private double dfLat; // latitude from user + private double dfLon; // latitude from user + private Date dateInput; // date/time from user + private double dfTimeZone; // time zone from user + + private Date dateSunrise; // date and time of sunrise + private Date dateSunset; // date and time of sunset + private boolean bSunriseToday = false; // flag for sunrise on this date + private boolean bSunsetToday = false; // flag for sunset on this date + private boolean bSunUpAllDay = false; // flag for sun up all day + private boolean bSunDownAllDay = false; // flag for sun down all day + private boolean bDaytime = false; // flag for daytime, given + // hour and min in dateInput + private boolean bSunrise = false; // sunrise during hour checked + private boolean bSunset = false; // sunset during hour checked + private boolean bGregorian = false; // flag for Gregorian calendar + private int iJulian; // Julian day + private int iYear; // year of date of interest + private int iMonth; // month of date of interest + private int iDay; // day of date of interest + private int iCount; // a simple counter + private int iSign; // SUNUP.BAS: S + private double dfHourRise, dfHourSet; // hour of event: SUNUP.BAS H3 + private double dfMinRise, dfMinSet; // minute of event: SUNUP.BAS M3 + private double dfSinLat, dfCosLat; // sin and cos of latitude + private double dfZenith; // SUNUP.BAS Z: Zenith +// private SimpleDateFormat dfmtDate; // formatting for date alone + private SimpleDateFormat dfmtDateTime; // formatting for date and time + private SimpleDateFormat dfmtYear; // formatting for year + private SimpleDateFormat dfmtMonth; // formatting for month + private SimpleDateFormat dfmtDay; // formatting for day + // Many variables in SUNUP.BAS have undocumented meanings, + // and so are translated rather directly to avoid confusion: + private double dfAA1 = 0, dfAA2 = 0; // SUNUP.BAS A(2) + private double dfDD1 = 0, dfDD2 = 0; // SUNUP.BAS D(2) + private double dfC0; // SUNUP.BAS C0 + private double dfK1; // SUNUP.BAS K1 + private double dfP; // SUNUP.BAS P + private double dfJ; // SUNUP.BAS J + private double dfJ3; // SUNUP.BAS J3 + private double dfA; // SUNUP.BAS A + private double dfA0, dfA2, dfA5; // SUNUP.BAS A0, A2, A5 + private double dfD0, dfD1, dfD2, dfD5; // SUNUP.BAS D0, D1, D2, D5 + private double dfDA, dfDD; // SUNUP.BAS DA, DD + private double dfH0, dfH1, dfH2; // SUNUP.BAS H0, H1, H2 + private double dfL0, dfL2; // SUNUP.BAS L0, L2 + private double dfT, dfT0, dfTT; // SUNUP.BAS T, T0, TT + private double dfV0, dfV1, dfV2; // SUNUP.BAS V0, V1, V2 + + private TimeZone tz = TimeZone.getTimeZone( "GMT" ); + private double origTimeZone; + + +/****************************************************************************** +* method: SunriseSunset +******************************************************************************* +* +* Constructor for SunriseSunset class. +* +*----------------------------------------------------------------------------*/ + public SunriseSunset( + double dfLatIn, // latitude + double dfLonIn, // longitude + Date dateInputIn, // date + TimeZone tzIn // time zone + ) + { + // Calculate internal representation of timezone offset as fraction of hours from GMT + // Our calculations consider offsets to the West as positive, so we must invert + // the signal of the values provided by the standard library + double dfTimeZoneIn = 1.0 * tzIn.getOffset(dateInputIn.getTime()) / 3600000; + + // Copy values supplied as agruments to local variables. + dfLat = dfLatIn; + dfLon = dfLonIn; + dateInput = dateInputIn; + dfTimeZone = dfTimeZoneIn; + origTimeZone= dfTimeZoneIn; + + // Call the method to do the calculations. + doCalculations(); + + } // end of class constructor + + +/****************************************************************************** +* method: doCalculations +******************************************************************************* +* +* Method for performing the calculations done in SUNUP.BAS. +* +*----------------------------------------------------------------------------*/ + private void doCalculations() + { + try + { + // Break out day, month, and year from date provided. + // (This is necesary for the math algorithms.) + + dfmtYear = new SimpleDateFormat( "yyyy" ); + dfmtYear.setLenient( false ); + dfmtYear.setTimeZone( tz ); + + dfmtMonth = new SimpleDateFormat( "M" ); + dfmtMonth.setLenient( false ); + dfmtMonth.setTimeZone( tz ); + + dfmtDay = new SimpleDateFormat( "d" ); + dfmtDay.setLenient( false ); + dfmtDay.setTimeZone( tz ); + + iYear = Integer.parseInt( dfmtYear.format( dateInput ) ); + iMonth = Integer.parseInt( dfmtMonth.format( dateInput ) ); + iDay = Integer.parseInt( dfmtDay.format( dateInput ) ); + + // Convert time zone hours to decimal days (SUNUP.BAS line 50) + dfTimeZone = dfTimeZone / 24.0; + + // NOTE: (7 Feb 2001) Here is a non-standard part of SUNUP.BAS: + // It (and this algorithm) assumes that the time zone is + // positive west, instead of the standard negative west. + // Classes calling SunriseSunset will be assuming that + // times zones are specified in negative west, so here the + // sign is changed so that the SUNUP algorithm works: + dfTimeZone = -dfTimeZone; + + // Convert longitude to fraction (SUNUP.BAS line 50) + dfLon = dfLon / 360.0; + + // Convert calendar date to Julian date: + // Check to see if it's later than 1583: Gregorian calendar + // When declared, bGregorian is initialized to false. + // ** Consider making a separate class of this function. ** + if( iYear >= 1583 ) bGregorian = true; + // SUNUP.BAS 1210 + dfJ = -Math.floor( 7.0 // SUNUP used INT, not floor + * ( Math.floor( + ( iMonth + 9.0 ) + / 12.0 + ) + iYear + ) / 4.0 + ) + // add SUNUP.BAS 1240 and 1250 for G = 0 + + Math.floor( iMonth * 275.0 / 9.0 ) + + iDay + + 1721027.0 + + iYear * 367.0; + + if ( bGregorian ) + { + // SUNUP.BAS 1230 + if ( ( iMonth - 9.0 ) < 0.0 ) iSign = -1; + else iSign = 1; + dfA = Math.abs( iMonth - 9.0 ); + // SUNUP.BAS 1240 and 1250 + dfJ3 = -Math.floor( + ( + Math.floor( + Math.floor( iYear + + (double)iSign + * Math.floor( dfA / 7.0 ) + ) + / 100.0 + ) + 1.0 + ) * 0.75 + ); + // correct dfJ as in SUNUP.BAS 1240 and 1250 for G = 1 + dfJ = dfJ + dfJ3 + 2.0; + } + // SUNUP.BAS 1290 + iJulian = (int)dfJ - 1; + + // SUNUP.BAS 60 and 70 (see also line 1290) + dfT = (double)iJulian - 2451545.0 + 0.5; + dfTT = dfT / 36525.0 + 1.0; // centuries since 1900 + + // Calculate local sidereal time at 0h in zone time + // SUNUP.BAS 410 through 460 + dfT0 = ( dfT * 8640184.813 / 36525.0 + + 24110.5 + + dfTimeZone * 86636.6 + + dfLon * 86400.0 + ) + / 86400.0; + dfT0 = dfT0 - Math.floor( dfT0 ); // NOTE: SUNUP.BAS uses INT() + dfT0 = dfT0 * 2.0 * Math.PI; + // SUNUP.BAS 90 + dfT = dfT + dfTimeZone; + + // SUNUP.BAS 110: Get Sun's position + for( iCount=0; iCount<=1; iCount++ ) // Loop thru only twice + { + // Calculate Sun's right ascension and declination + // at the start and end of each day. + // SUNUP.BAS 910 - 1160: Fundamental arguments + // from van Flandern and Pulkkinen, 1979 + + // declare local temporary doubles for calculations + double dfGG; // SUNUP.BAS G + double dfLL; // SUNUP.BAS L + double dfSS; // SUNUP.BAS S + double dfUU; // SUNUP.BAS U + double dfVV; // SUNUP.BAS V + double dfWW; // SUNUP.BAS W + + dfLL = 0.779072 + 0.00273790931 * dfT; + dfLL = dfLL - Math.floor( dfLL ); + dfLL = dfLL * 2.0 * Math.PI; + + dfGG = 0.993126 + 0.0027377785 * dfT; + dfGG = dfGG - Math.floor( dfGG ); + dfGG = dfGG * 2.0 * Math.PI; + + dfVV = 0.39785 * Math.sin( dfLL ) + - 0.01000 * Math.sin( dfLL - dfGG ) + + 0.00333 * Math.sin( dfLL + dfGG ) + - 0.00021 * Math.sin( dfLL ) * dfTT; + + dfUU = 1 + - 0.03349 * Math.cos( dfGG ) + - 0.00014 * Math.cos( dfLL * 2.0 ) + + 0.00008 * Math.cos( dfLL ); + + dfWW = - 0.00010 + - 0.04129 * Math.sin( dfLL * 2.0 ) + + 0.03211 * Math.sin( dfGG ) + - 0.00104 * Math.sin( 2.0 * dfLL - dfGG ) + - 0.00035 * Math.sin( 2.0 * dfLL + dfGG ) + - 0.00008 * Math.sin( dfGG ) * dfTT; + + // Compute Sun's RA and Dec; SUNUP.BAS 1120 - 1140 + dfSS = dfWW / Math.sqrt( dfUU - dfVV * dfVV ); + dfA5 = dfLL + + Math.atan( dfSS / Math.sqrt( 1.0 - dfSS * dfSS )); + + dfSS = dfVV / Math.sqrt( dfUU ); + dfD5 = Math.atan( dfSS / Math.sqrt( 1 - dfSS * dfSS )); + + // Set values and increment t + if ( iCount == 0 ) // SUNUP.BAS 125 + { + dfAA1 = dfA5; + dfDD1 = dfD5; + } + else // SUNUP.BAS 145 + { + dfAA2 = dfA5; + dfDD2 = dfD5; + } + dfT = dfT + 1.0; // SUNUP.BAS 130 + } // end of Get Sun's Position for loop + + if ( dfAA2 < dfAA1 ) dfAA2 = dfAA2 + 2.0 * Math.PI; + // SUNUP.BAS 150 + + dfZenith = Math.PI * 90.833 / 180.0; // SUNUP.BAS 160 + dfSinLat = Math.sin( dfLat * Math.PI / 180.0 ); // SUNUP.BAS 170 + dfCosLat = Math.cos( dfLat * Math.PI / 180.0 ); // SUNUP.BAS 170 + + dfA0 = dfAA1; // SUNUP.BAS 190 + dfD0 = dfDD1; // SUNUP.BAS 190 + dfDA = dfAA2 - dfAA1; // SUNUP.BAS 200 + dfDD = dfDD2 - dfDD1; // SUNUP.BAS 200 + + dfK1 = 15.0 * 1.0027379 * Math.PI / 180.0; // SUNUP.BAS 330 + + // Initialize sunrise and sunset times, and other variables + // hr and min are set to impossible times to make errors obvious + dfHourRise = 99.0; + dfMinRise = 99.0; + dfHourSet = 99.0; + dfMinSet = 99.0; + dfV0 = 0.0; // initialization implied by absence in SUNUP.BAS + dfV2 = 0.0; // initialization implied by absence in SUNUP.BAS + + // Test each hour to see if the Sun crosses the horizon + // and which way it is heading. + for( iCount=0; iCount<24; iCount++ ) // SUNUP.BAS 210 + { + double tempA; // SUNUP.BAS A + double tempB; // SUNUP.BAS B + double tempD; // SUNUP.BAS D + double tempE; // SUNUP.BAS E + + dfC0 = (double)iCount; + dfP = ( dfC0 + 1.0 ) / 24.0; // SUNUP.BAS 220 + dfA2 = dfAA1 + dfP * dfDA; // SUNUP.BAS 230 + dfD2 = dfDD1 + dfP * dfDD; // SUNUP.BAS 230 + dfL0 = dfT0 + dfC0 * dfK1; // SUNUP.BAS 500 + dfL2 = dfL0 + dfK1; // SUNUP.BAS 500 + dfH0 = dfL0 - dfA0; // SUNUP.BAS 510 + dfH2 = dfL2 - dfA2; // SUNUP.BAS 510 + // hour angle at half hour + dfH1 = ( dfH2 + dfH0 ) / 2.0; // SUNUP.BAS 520 + // declination at half hour + dfD1 = ( dfD2 + dfD0 ) / 2.0; // SUNUP.BAS 530 + + // Set value of dfV0 only if this is the first hour, + // otherwise, it will get set to the last dfV2 (SUNUP.BAS 250) + if ( iCount == 0 ) // SUNUP.BAS 550 + { + dfV0 = dfSinLat * Math.sin( dfD0 ) + + dfCosLat * Math.cos( dfD0 ) * Math.cos( dfH0 ) + - Math.cos( dfZenith ); // SUNUP.BAS 560 + } + else + dfV0 = dfV2; // That is, dfV2 from the previous hour. + + dfV2 = dfSinLat * Math.sin( dfD2 ) + + dfCosLat * Math.cos( dfD2 ) * Math.cos( dfH2 ) + - Math.cos( dfZenith ); // SUNUP.BAS 570 + + // if dfV0 and dfV2 have the same sign, then proceed to next hr + if ( + ( dfV0 >= 0.0 && dfV2 >= 0.0 ) // both are positive + || // or + ( dfV0 < 0.0 && dfV2 < 0.0 ) // both are negative + ) + { + // Break iteration and proceed to test next hour + dfA0 = dfA2; // SUNUP.BAS 250 + dfD0 = dfD2; // SUNUP.BAS 250 + continue; // SUNUP.BAS 610 + } + + dfV1 = dfSinLat * Math.sin( dfD1 ) + + dfCosLat * Math.cos( dfD1 ) * Math.cos( dfH1 ) + - Math.cos( dfZenith ); // SUNUP.BAS 590 + + tempA = 2.0 * dfV2 - 4.0 * dfV1 + 2.0 * dfV0; + // SUNUP.BAS 600 + tempB = 4.0 * dfV1 - 3.0 * dfV0 - dfV2; // SUNUP.BAS 600 + tempD = tempB * tempB - 4.0 * tempA * dfV0; // SUNUP.BAS 610 + + if ( tempD < 0.0 ) + { + // Break iteration and proceed to test next hour + dfA0 = dfA2; // SUNUP.BAS 250 + dfD0 = dfD2; // SUNUP.BAS 250 + continue; // SUNUP.BAS 610 + } + + tempD = Math.sqrt( tempD ); // SUNUP.BAS 620 + + // Determine occurence of sunrise or sunset. + + // Flags to identify occurrence during this day are + // bSunriseToday and bSunsetToday, and are initialized false. + // These are set true only if sunrise or sunset occurs + // at any point in the hourly loop. Never set to false. + + // Flags to identify occurrence during this hour: + bSunrise = false; // reset before test + bSunset = false; // reset before test + + if ( dfV0 < 0.0 && dfV2 > 0.0 ) // sunrise occurs this hour + { + bSunrise = true; // SUNUP.BAS 640 + bSunriseToday = true; // sunrise occurred today + } + + if ( dfV0 > 0.0 && dfV2 < 0.0 ) // sunset occurs this hour + { + bSunset = true; // SUNUP.BAS 660 + bSunsetToday = true; // sunset occurred today + } + + tempE = ( tempD - tempB ) / ( 2.0 * tempA ); + if ( tempE > 1.0 || tempE < 0.0 ) // SUNUP.BAS 670, 680 + tempE = ( -tempD - tempB ) / ( 2.0 * tempA ); + + // Set values of hour and minute of sunset or sunrise + // only if sunrise/set occurred this hour. + if ( bSunrise ) + { + dfHourRise = Math.floor( dfC0 + tempE + 1.0/120.0 ); + dfMinRise = Math.floor( + ( dfC0 + tempE + 1.0/120.0 + - dfHourRise + ) + * 60.0 + ); + } + + if ( bSunset ) + { + dfHourSet = Math.floor( dfC0 + tempE + 1.0/120.0 ); + dfMinSet = Math.floor( + ( dfC0 + tempE + 1.0/120.0 + - dfHourSet + ) + * 60.0 + ); + } + + // Change settings of variables for next loop + dfA0 = dfA2; // SUNUP.BAS 250 + dfD0 = dfD2; // SUNUP.BAS 250 + + } // end of loop testing each hour for an event + + // After having checked all hours, set flags if no rise or set + // bSunUpAllDay and bSundownAllDay are initialized as false + if ( !bSunriseToday && !bSunsetToday ) + { + if ( dfV2 < 0.0 ) + bSunDownAllDay = true; + else + bSunUpAllDay = true; + } + + // Load dateSunrise with data + dfmtDateTime = new SimpleDateFormat( "d M yyyy HH:mm z" ); + + // Timezone signal is reversed in SunriseSunset class + String tz_signal = origTimeZone <= 0?"-":"+"; + double abs_tz = Math.abs(origTimeZone); + NumberFormat formatter = new DecimalFormat("00"); + + String tz_offset_hours = formatter.format((int)abs_tz); + String tz_offset_minutes = formatter.format((int)(60 * (abs_tz - (int)abs_tz))); + + if( bSunriseToday ) + { + dateSunrise = dfmtDateTime.parse( iDay + + " " + iMonth + + " " + iYear + + " " + (int)dfHourRise + + ":" + (int)dfMinRise + + " GMT" + + tz_signal + tz_offset_hours + +":" + tz_offset_minutes ); + } + + // Load dateSunset with data + if( bSunsetToday ) + { + dateSunset = dfmtDateTime.parse( iDay + + " " + iMonth + + " " + iYear + + " " + (int)dfHourSet + + ":" + (int)dfMinSet + + " GMT" + + tz_signal + tz_offset_hours + +":" + tz_offset_minutes ); + } + } // end of try + + // Catch errors + catch( ParseException e ) + { + e.printStackTrace(); + } // end of catch + + } + + +/****************************************************************************** +* method: getSunrise() +******************************************************************************* +* +* Gets the date and time of sunrise. If there is no sunrise, returns null. +* +* Member of SunriseSunset class +* +* -------------------------------------------------------------------------- */ + public Date getSunrise() + { + if ( bSunriseToday ) + return( dateSunrise ); + else + return( null ); + } + + +/****************************************************************************** +* method: getSunset() +******************************************************************************* +* +* Gets the date and time of sunset. If there is no sunset, returns null. +* +* Member of SunriseSunset class +* +* -------------------------------------------------------------------------- */ + public Date getSunset() + { + if ( bSunsetToday ) + return( dateSunset ); + else + return( null ); + } + + +/****************************************************************************** +* method: isSunrise() +******************************************************************************* +* +* Returns a boolean identifying if there was a sunrise. +* +* Member of SunriseSunset class +* +* -------------------------------------------------------------------------- */ + public boolean isSunrise() + { + return( bSunriseToday ); + } + + +/****************************************************************************** +* method: isSunset() +******************************************************************************* +* +* Returns a boolean identifying if there was a sunset. +* +* Member of SunriseSunset class +* +* -------------------------------------------------------------------------- */ + public boolean isSunset() + { + return( bSunsetToday ); + } + + +/****************************************************************************** +* method: isSunUp() +******************************************************************************* +* +* Returns a boolean identifying if the sun is up all day. +* +* Member of SunriseSunset class +* +* -------------------------------------------------------------------------- */ + public boolean isSunUp() + { + return( bSunUpAllDay ); + } + + +/****************************************************************************** +* method: isSunDown() +******************************************************************************* +* +* Returns a boolean identifying if the sun is down all day. +* +* Member of SunriseSunset class +* +* -------------------------------------------------------------------------- */ + public boolean isSunDown() + { + return( bSunDownAllDay ); + } + + +/****************************************************************************** +* method: isDaytime() +******************************************************************************* +* +* Returns a boolean identifying if it is daytime at the hour contained in +* the Date object passed to SunriseSunset on construction. +* +* Member of SunriseSunset class +* +* -------------------------------------------------------------------------- */ + public boolean isDaytime() + { + // Determine if it is daytime (at sunrise or later) + // or nighttime (at sunset or later) at the location of interest + // but expressed in the time zone requested. + if ( bSunriseToday && bSunsetToday ) // sunrise and sunset + { + if ( dateSunrise.before( dateSunset ) ) // sunrise < sunset + { + if ( + ( + dateInput.after( dateSunrise ) + || + dateInput.equals( dateSunrise ) + ) + && + dateInput.before( dateSunset ) + ) + bDaytime = true; + else + bDaytime = false; + } + else // sunrise comes after sunset (in opposite time zones) + { + if ( + ( + dateInput.after( dateSunrise ) + || + dateInput.equals( dateSunrise ) + ) + || // use OR rather than AND + dateInput.before( dateSunset ) + ) + bDaytime = true; + else + bDaytime = false; + } + } + else if ( bSunUpAllDay ) // sun is up all day + bDaytime = true; + else if ( bSunDownAllDay ) // sun is down all day + bDaytime = false; + else if ( bSunriseToday ) // sunrise but no sunset + { + if ( dateInput.before( dateSunrise ) ) + bDaytime = false; + else + bDaytime = true; + } + else if ( bSunsetToday ) // sunset but no sunrise + { + if ( dateInput.before( dateSunset ) ) + bDaytime = true; + else + bDaytime = false; + } + else bDaytime = false; // this should never execute + + return( bDaytime ); + } + +} // end of class + +/*----------------------------------------------------------------------------- +* end of class +*----------------------------------------------------------------------------*/ \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java b/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java index 987fd0352f..4f5bae7df7 100644 --- a/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java +++ b/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java @@ -1,5 +1,6 @@ package net.osmand.plus.development; +import java.text.DateFormat; import java.text.SimpleDateFormat; import net.osmand.SunriseSunset; @@ -130,9 +131,14 @@ public class OsmandDevelopmentPlugin extends OsmandPlugin { pref = new Preference(app); pref.setTitle(R.string.day_night_info); if (sunriseSunset != null) { - SimpleDateFormat prt = new SimpleDateFormat("dd.MM.yyyy HH:mm"); - pref.setSummary(activity.getString(R.string.day_night_info_description, prt.format(sunriseSunset.getSunrise()), - prt.format(sunriseSunset.getSunset()))); + //SimpleDateFormat prt = new SimpleDateFormat("dd.MM.yyyy HH:mm"); + //pref.setSummary(activity.getString(R.string.day_night_info_description, prt.format(sunriseSunset.getSunrise()), + // prt.format(sunriseSunset.getSunset()))); + //new approach for previous 3 lines from Issue 1313 + DateFormat format = SimpleDateFormat.getDateTimeInstance(); + String sunrise = format.format(sunriseSunset.getSunrise()); + String sunset = format.format(sunriseSunset.getSunset()); + pref.setSummary(activity.getString(R.string.day_night_info_description, sunrise, sunset)); } else { pref.setSummary(activity.getString(R.string.day_night_info_description, "null", "null"));