From 237bc42084c331bd739730737ea82d5ae7da723e Mon Sep 17 00:00:00 2001 From: Don Cross Date: Mon, 2 May 2022 11:28:16 -0400 Subject: [PATCH] Added Kotlin demo: positions. I also reworked how the Java and Kotlin demos process errors in the command line arguments. Using exceptions that are caught by main() rather than directly exiting the process where the errors are detected. --- .../cosinekitty/astronomy/demo/Main.java | 65 ++++++++++++------- demo/kotlin/correct/positions.txt | 13 ++++ demo/kotlin/demotest | 2 +- demo/kotlin/demotest.bat | 1 + demo/kotlin/src/main/kotlin/Main.kt | 65 +++++++++++++++++-- demo/kotlin/src/main/kotlin/Positions.kt | 28 ++++++++ 6 files changed, 145 insertions(+), 29 deletions(-) create mode 100644 demo/kotlin/correct/positions.txt create mode 100644 demo/kotlin/src/main/kotlin/Positions.kt diff --git a/demo/java/src/main/java/io/github/cosinekitty/astronomy/demo/Main.java b/demo/java/src/main/java/io/github/cosinekitty/astronomy/demo/Main.java index 0da0fe59..83752f28 100644 --- a/demo/java/src/main/java/io/github/cosinekitty/astronomy/demo/Main.java +++ b/demo/java/src/main/java/io/github/cosinekitty/astronomy/demo/Main.java @@ -23,10 +23,17 @@ public class Main { " current date and time.", "", " seasons year", - " Given an integer year number, display the solstices and equinoxes for that year.", + " Given an integer year number, displays the solstices and equinoxes for that year.", + " The year must be in the range 0000..9999.", "" ); + private static class DemoException extends Exception { + public DemoException(String message) { + super(message); + } + } + public static void main(String[] args) { int rc = 1; if (args.length == 0) { @@ -51,7 +58,10 @@ public class Main { System.out.printf("ERROR: Unknown command '%s'.%n", verb); } } catch (DateTimeParseException e) { - System.out.println("FATAL: Invalid date/time syntax."); + System.out.println("ERROR: Invalid date/time format on command line."); + rc = 1; + } catch (DemoException e) { + System.out.printf("ERROR: %s%n", e.getMessage()); rc = 1; } } @@ -67,44 +77,38 @@ public class Main { return Time.fromMillisecondsSince1970(millis); } - private static double parseNumber(String name, String text, double minValue, double maxValue) { - double value = Double.NaN; + private static double parseNumber(String name, String text, double minValue, double maxValue) throws DemoException { try { - value = Double.parseDouble(text); - if (value < minValue || value > maxValue) { - System.out.printf("ERROR: Value is out of range for %s.%n", name); - System.exit(1); + double value = Double.parseDouble(text); + if (!Double.isFinite(value) || value < minValue || value > maxValue) { + throw new DemoException(String.format("Value is out of range for %s.", name)); } + return value; } catch (NumberFormatException e) { - System.out.printf("ERROR: Invalid numeric format '%s' for %s.%n", text, name); - System.exit(1); + throw new DemoException(String.format("Invalid numeric format '%s' for %s.%n", text, name)); } - return value; } - private static int parseYear(String text) { - int year = -1; + private static int parseYear(String text) throws DemoException { try { - year = Integer.parseInt(text); + int year = Integer.parseInt(text); if (year < 0 || year > 9999) { - System.out.println("ERROR: Year must be in the range 0000..9999."); - System.exit(1); + throw new DemoException("Year must be in the range 0000..9999."); } + return year; } catch (NumberFormatException e) { - System.out.printf("ERROR: Invalid numeric format '%s' for year.%n", text); - System.exit(1); + throw new DemoException(String.format("Invalid numeric format '%s' for year.", text)); } - return year; } - private static Observer parseObserver(String[] args, int index) { + private static Observer parseObserver(String[] args, int index) throws DemoException { double latitude = parseNumber("latitude", args[index], -90.0, +90.0); double longitude = parseNumber("longitude", args[index+1], -180.0, +180.0); return new Observer(latitude, longitude, 0.0); } private static interface DemoRunner { - public int run(String[] args); + public int run(String[] args) throws DemoException; } private static class Demo { @@ -122,8 +126,21 @@ public class Main { } private static Demo[] demoList = new Demo[] { - new Demo("moonphase", 1, 2, args -> MoonPhase.run(parseTime(args, 1))), - new Demo("positions", 3, 4, args -> Positions.run(parseObserver(args, 1), parseTime(args, 3))), - new Demo("seasons", 2, 2, args -> Seasons.run(parseYear(args[1]))) + new Demo("moonphase", 1, 2, args -> + MoonPhase.run( + parseTime(args, 1) + ) + ), + new Demo("positions", 3, 4, args -> + Positions.run( + parseObserver(args, 1), + parseTime(args, 3) + ) + ), + new Demo("seasons", 2, 2, args -> + Seasons.run( + parseYear(args[1]) + ) + ) }; } diff --git a/demo/kotlin/correct/positions.txt b/demo/kotlin/correct/positions.txt new file mode 100644 index 00000000..1197cc3e --- /dev/null +++ b/demo/kotlin/correct/positions.txt @@ -0,0 +1,13 @@ +UTC date = 2018-11-30T17:55:07.234Z + +BODY RA DEC AZ ALT +Sun 16.43 -21.68 180.91 22.72 +Moon 11.30 7.81 266.73 14.07 +Mercury 15.92 -18.41 189.05 25.50 +Venus 13.81 -9.79 224.05 23.85 +Mars 22.74 -9.25 93.82 -8.52 +Jupiter 16.19 -20.43 184.62 23.84 +Saturn 18.54 -22.70 150.51 16.25 +Uranus 1.80 10.57 43.72 -22.60 +Neptune 23.01 -7.41 89.63 -10.05 +Pluto 19.40 -22.09 139.25 11.68 diff --git a/demo/kotlin/demotest b/demo/kotlin/demotest index 7a185238..a8e24dcd 100755 --- a/demo/kotlin/demotest +++ b/demo/kotlin/demotest @@ -18,7 +18,7 @@ mkdir -p test ./gradlew jar || Fail "Error building Kotlin demo application." TestDemo moonphase 2019-06-15T09:15:32.987Z -#TestDemo positions +45.6 -90.7 2018-11-30T17:55:07.234Z +TestDemo positions +45.6 -90.7 2018-11-30T17:55:07.234Z TestDemo seasons 2019 echo "Kotlin demos: PASS" diff --git a/demo/kotlin/demotest.bat b/demo/kotlin/demotest.bat index b17f63d4..08e035b2 100644 --- a/demo/kotlin/demotest.bat +++ b/demo/kotlin/demotest.bat @@ -13,6 +13,7 @@ call gradlew.bat jar || exit /b 1 REM ---------------------------------------------------------------------------------- call :TestDemo moonphase 2019-06-15T09:15:32.987Z || exit /b 1 +call :TestDemo positions +45.6 -90.7 2018-11-30T17:55:07.234Z || exit /b 1 call :TestDemo seasons 2019 || exit /b 1 echo. diff --git a/demo/kotlin/src/main/kotlin/Main.kt b/demo/kotlin/src/main/kotlin/Main.kt index cbff8e68..90f04cda 100644 --- a/demo/kotlin/src/main/kotlin/Main.kt +++ b/demo/kotlin/src/main/kotlin/Main.kt @@ -11,8 +11,16 @@ Command line arguments: time if none is given on the command line. Also finds the dates and times of the subsequent 10 quarter phases. + positions latitude longitude [yyyy-mm-ddThh:mm:ssZ] + Displays the equatorial and horizontal coordinates of + the Sun, Moon, and planets, as seen from a given + geographic location. Uses the date and time specified on + the command line, if present. Otherwise, uses the computer's + current date and time. + seasons year - Given an integer year number, display the solstices and equinoxes for that year. + Given an integer year number, displays the solstices and equinoxes for that year. + The year must be in the range 0000..9999. """ @@ -32,6 +40,8 @@ fun main(args: Array) { System.exit(runDemo(args)) } +class DemoException(message:String): Exception(message) + internal fun runDemo(args: Array): Int { if (args.size > 0) { val verb = args[0]; @@ -43,9 +53,12 @@ internal fun runDemo(args: Array): Int { } try { return demo.func(args) - } catch (e: DateTimeParseException) { + } catch (_: DateTimeParseException) { println("ERROR: Invalid date/time format on command line.") return 1 + } catch (e: DemoException) { + println("ERROR: ${e.message}") + return 1 } } } @@ -63,7 +76,51 @@ internal fun parseTime(args: Array, index: Int): Time { return Time.fromMillisecondsSince1970(millis) } +internal fun parseNumber(name: String, text: String, minValue: Double, maxValue: Double): Double { + try { + val value = text.toDouble() + if (!value.isFinite() || value < minValue || value > maxValue) { + throw DemoException("Value for $name is out of range.") + } + return value + } catch (_: NumberFormatException) { + throw DemoException("Invalid numeric format '$text' for $name.") + } +} + +internal fun parseYear(text: String): Int { + try { + val year = text.toInt() + if (year < 0 || year > 9999) { + throw DemoException("Year must be in the range 0000..9999.") + } + return year + } catch (_: NumberFormatException) { + throw DemoException("Invalid numeric format '$text' for year.") + } +} + +internal fun parseObserver(args: Array, index: Int): Observer { + val latitude = parseNumber("latitude", args[index], -90.0, +90.0) + val longitude = parseNumber("longitude", args[index+1], -180.0, +180.0) + return Observer(latitude, longitude, 0.0) +} + internal val demoList = arrayOf( - Demo("moonphase", 1, 2) { args -> `Moon Phase demo`(parseTime(args, 1)) }, - Demo("seasons", 2, 2) { args -> `Seasons demo`(args[1].toInt()) } + Demo("moonphase", 1, 2) { args -> + `Moon Phase demo`( + parseTime(args, 1) + ) + }, + Demo("positions", 3, 4) { args -> + `Celestial body positions demo`( + parseObserver(args, 1), + parseTime(args, 3) + ) + }, + Demo("seasons", 2, 2) { args -> + `Seasons demo`( + parseYear(args[1]) + ) + } ) diff --git a/demo/kotlin/src/main/kotlin/Positions.kt b/demo/kotlin/src/main/kotlin/Positions.kt new file mode 100644 index 00000000..bf4ab2fa --- /dev/null +++ b/demo/kotlin/src/main/kotlin/Positions.kt @@ -0,0 +1,28 @@ +import io.github.cosinekitty.astronomy.* + +private val bodyList = arrayOf( + Body.Sun, Body.Moon, Body.Mercury, Body.Venus, Body.Mars, + Body.Jupiter, Body.Saturn, Body.Uranus, Body.Neptune, Body.Pluto +) + +/** + * Print a table of solar system body positions in equatorial and horizontal coordinates. + * + * @param observer + * The geographic location for which to calculate apparent celestial body positions. + * + * @param time + * The date and time for which to calculate positions. + */ +internal fun `Celestial body positions demo`(observer: Observer, time: Time): Int { + println("UTC date = $time") + println() + println("BODY RA DEC AZ ALT") + for (body in bodyList) { + val equ_2000: Equatorial = equator(body, time, observer, EquatorEpoch.J2000, Aberration.Corrected) + val equ_ofdate: Equatorial = equator(body, time, observer, EquatorEpoch.OfDate, Aberration.Corrected) + val hor: Topocentric = horizon(time, observer, equ_ofdate.ra, equ_ofdate.dec, Refraction.Normal) + println("%-8s %8.2f %8.2f %8.2f %8.2f".format(body, equ_2000.ra, equ_2000.dec, hor.azimuth, hor.altitude)) + } + return 0 +}