Giter Club home page Giter Club logo

kastro's Introduction

Kastro

Kastro is a pure Kotlin library for calculating astronimcal events for the Moon and Sun (Luna and Sol). What makes Kastro special is the lazily evaluated Sequence-based implementation, which allows you to use the data in powerful ways. Kastro builds on the work of Richard Körber (shred) in his library commons-suncalc. Much of the math in Kastro comes from commons-suncalc but was ported to common Kotlin.

Note

For a pure Java API be sure to check out Richard Körber's project!.

Accuracy

Like commons-suncalc, this library strives on getting "close enough" without using a lot of computational resources. Most calculations should be accurate to within a minute. Moon phase events may be off by as much as five minutes.

Getting Started

This project is deployed on Maven Central. Coordinates are as follows:

groupId: dev.jamesyox

artifactId: kastro

version: 0.1.0

Important

This project depends on kotlinx-datetime, so to use Kastro you must add that as a dependency. This will be corrected in the next release of Kastro where kotlinx-datetime will be made an api dependency.

Gradle

If you use Gradle you should be able to add the following to your dependencies to use Kastro:

implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
implementation("dev.jamesyox:kastro:0.1.0")

Solar Phases

You can calculate the following solar phases:

Phase Description
Astronomical twlight During dawn, the sun is increaing in brightness and transitioning from night toward nautical twilight. During dusk, the sun is decreasing in brightness and is transitioning from nautical twilight toward night
Blue hour Happens during both dawn and dusk, when the sun is below the horizon, causing the light to look mostly blue.
Civil twilight During dawn, the sun is increasing in brightness and is transitioning from nautical twilight toward day. During dusk, the sun is decreasing in brightness and transitioning from day toward nautical dusk
Day The sun is above the horizon
Golden hour Happens during both dawn and dusk, when the sun is close to the horizon, causing the light to look golden. Ideal time to take pictures.
Nadir Opposite of noon. The sun is at its lowest point below the horizon
Nautical twilight During dawn, the sun is increasing in brightness and is transitioning from night toward civil twilight. During dusk, the sun is decreasing in brightness and is transitioning from civil twilight toward astronomical twilight
Night The sun is below the horizon
Noon Opposite of nadir. The sun is at its highest point above the horizon
Sunrise The sun's top edge first rises above the horizon
Sunset The sun's top edge completely disappears below the horizon

Lunar Phases

You can calculate the following lunar phases:

Phase Description
First quarter Moon is increasing in brightness and is transitioning toward full moon
Full moon Moon is fully illuminated
Last quarter Moon is decreasing in brightness and is transitioning toward new moon
New moon Moon is not illuminated
Waning crescent The moon is decreasing in brightness and is transitioning between last quarter and new moon
Waning gibbous The moon is decreasing in brightness and is transitioning between full moon and last quarter
Waxing crescent The moon is increasing in brightness and is transitioning between new moon and first quarter
Waxing gibbous The moon is increasing in brightness and is transitioning between first quarter and full moon

Examples

Kastro leverages Kotlin Sequences to lazily evaluate the algorithms contained within so you can determine anything in a single call, from what time the sun will rise tomorrow, to every sunrise time for the next 10 years. with a single call.

See ReadmeExamples.kt for a compiled full text version of all of the examples.

Warning

first() will throw if your sequence is empty! Kastro can return empty sequences. For example if you set a limit for 1 minute there may not be any solar events in that timeframe and an empty Sequence will be returned.

What time does the sun set next in Denver, CO?

This use case uses Sequence's first() method. Start can be any Instant in the past or future. requestedSolarEvents is an optional field that can make calculations more efficient however you are free to use any Sequence methods to achieve the same results.

val nextSunset = SolarEventSequence(
    start = clock.now(),
    latitude = 39.7348,
    longitude = -104.9653,
    requestedSolarEvents = listOf(SolarEvent.Sunset) // Not required but makes calculations more efficient
).first() // This example is safe, but first() can throw on empty sequences!

When is solar noon on December 31st, 2023 in Denver, CO?

val solarNoon = SolarEventSequence(
    start = LocalDate(2023, 12, 31).atStartOfDayIn(timeZone),
    latitude = 39.7348,
    longitude = -104.9653,
    requestedSolarEvents = listOf(SolarEvent.Noon)
).first() // This example is safe, but first() can throw on empty sequences!

When are the next sunrise and sunset events in the next week in Denver, CO?

val fullWeek = SolarEventSequence(
    start = clock.now(),
    latitude = 39.7348,
    longitude = -104.9653,
    requestedSolarEvents = listOf(SolarEvent.Sunrise, SolarEvent.Sunset),
    limit = 7.days
).toList()

When does the next Golden Hour begin and end in Denver, CO?

val goldenHour = SolarEventSequence(
    start = clock.now(),
    latitude = 39.7348,
    longitude = -104.9653,
    requestedSolarEvents = listOf(SolarEvent.GoldenHourDusk, SolarEvent.GoldenHourDusk)
).firstOrNull()

Note

SolarEvent.GoldenHourDusk and SolarEvent.GoldenHourDawn are not added by default and must be included in requestedSolarEvents if you want that information.

What is the state of the Sun right now in Denver, CO?

val sunState = clock.now().calculateSolarState(
    latitude = 39.7348,
    longitude = -104.9653,
)

What are the sunrise times for every Tuesday in the next year in Denver, CO?

val tuesdaySunrises = SolarEventSequence(
    start = clock.now(),
    latitude = 39.7348,
    longitude = -104.9653,
    requestedSolarEvents = listOf(SolarEvent.Sunrise),
    limit = 365.days // Omitting leap year shenanigans
).filter {
    it.time.toLocalDateTime(timeZone).dayOfWeek == DayOfWeek.TUESDAY 
}.toList()

Does the sun set in the next hour in Denver, CO?

val doesItSet = SolarEventSequence(
    start = clock.now(),
    latitude = 39.7348,
    longitude = -104.9653,
    requestedSolarEvents = listOf(SolarEvent.Sunrise),
    limit = 1.hours
).any() // Returns true if anything is in the sequence

When is the next moonrise in Denver, CO?

val nextMoonrise = LunarHorizonEventSequence(
    start = clock.now(),
    latitude = 39.7348,
    longitude = -104.9653,
    requestedHorizonEvents = listOf(LunarEvent.HorizonEvent.Moonrise)
).first()

When is the next full moon?

val nextFullMoon = LunarPhaseSequence(
    start = clock.now(),
    requestedLunarPhases = listOf(LunarEvent.PhaseEvent.FullMoon)
).first()

Note

This example does not require location because moon phases are the same across Earth.

What are the next moonrises, moonsets, and moon phases for the next 30 days in Denver, CO?

val moonList = LunarEventSequence(
    start = clock.now(),
    latitude = 39.7348,
    longitude = -104.9653,
    requestedLunarEvents = LunarEvent.all, // Show us everything!
    limit = 30.days
).toList()

Do any full moons happen on Fridays this year?

val fridayFullMoon = LunarPhaseSequence(
    start = clock.now(),
    requestedLunarPhases = listOf(LunarEvent.PhaseEvent.FullMoon),
    limit = 365.days // Omitting leap year shenanigans
).filter { 
    it.time.toLocalDateTime(timeZone).dayOfWeek == DayOfWeek.FRIDAY 
}.any()

Execute some code on every sunset forever

Kastro guarantees all sequences are ordered by time. This means that events closer to the start time come before later events. This means that if you wanted to execute some code on each sunset (maybe to turn off your lights?) the following would work.

Please note you will need to add kotlinx-coroutines as a dependency to do this. Kastro strives to include as few dependencies as possible (really just kotlinx-datetime)

SolarEventSequence(
    start = clock.now(),
    latitude = latitude,
    longitude = longitude,
    requestedSolarEvents = listOf(SolarEvent.Sunset),
    limit = Duration.INFINITE
).asFlow()
    .onEach { delay(it.time) - clock.now() }
    .collect { doSomething() } // The world is your oyster!

Contributing

Pull requests are welcome. Feel free to fork and open a PR. Beforeopening a PR, make sure that both tests detekt static analysis pass.

./gradlew allTest detektAll

Should return successfully

You may run into issues depending on your host OS. At the very least ensure that the following returns successfully:

./gradlew jvmTest detektAll

This project is still alpha so API changes are possible, but we strive for no breaking changes. Run ./gradlew apiCheck to see if your changes maintain binary compatibility! Enhancements to the overall shape of the API are welcome though as this has not yet reached the 1.0 milestone.

Future Work

I ran into some difficulties getting the height offset calculation to work correctly for SolarEventSequence. I hope to eventually resolve that but didn't think it should block an alpha release. It's something something I want for the future 1.0 release.

I would also like to add additional KMP targets. I don't own any Apple products, so I cannot build any Apple targets. I hope to eventually have a solution to that if there is demand. I am also curious to look into WASM and any other KMP targets added in the future.

I am also curious to potentially make the library usable for other languages like Javascript. This library is a Kotlin Multiplatform project, but it would be cool to also have it be on npm for use in Javascript/Typescript projects or even be a Swift package (SPM) for use on iOS/Apple targets. There are some challenges to doing that (such as how the exposed API could be adapted to better fit those languages) but I plan to actively look into this as it's something I am generally curious about.

References

kastro's People

Contributors

amlabby avatar yoxjames avatar

Stargazers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.