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.
This commit is contained in:
Don Cross
2022-05-02 11:28:16 -04:00
parent 551ce7a249
commit 237bc42084
6 changed files with 145 additions and 29 deletions

View File

@@ -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])
)
)
};
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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.

View File

@@ -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<String>) {
System.exit(runDemo(args))
}
class DemoException(message:String): Exception(message)
internal fun runDemo(args: Array<String>): Int {
if (args.size > 0) {
val verb = args[0];
@@ -43,9 +53,12 @@ internal fun runDemo(args: Array<String>): 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<String>, 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<String>, 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])
)
}
)

View File

@@ -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
}