diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 0dd711222..63359c790 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -2,7 +2,7 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar plugins { kotlin("jvm") - id("org.springframework.boot") version "3.1.4" + id("org.springframework.boot") version "3.1.5" id("io.spring.dependency-management") version "1.1.3" kotlin("plugin.spring") kotlin("kapt") @@ -32,8 +32,7 @@ dependencies { implementation(libs.eventbus) implementation(libs.apache.codec) implementation(libs.rx) - // implementation(libs.sqlite) - runtimeOnly(files("$projectDir/libs/sqlite-jdbc-3.43.0.0.jar")) + implementation(libs.sqlite) implementation(libs.flyway) implementation("org.springframework.boot:spring-boot-starter") implementation("org.springframework.boot:spring-boot-starter-web") diff --git a/api/data/dsos.json.gz b/api/data/dsos.json.gz index 14f1f6397..51925f4e8 100644 Binary files a/api/data/dsos.json.gz and b/api/data/dsos.json.gz differ diff --git a/api/data/stars.json.gz b/api/data/stars.json.gz index 37148d73d..d5d7dbb85 100644 Binary files a/api/data/stars.json.gz and b/api/data/stars.json.gz differ diff --git a/api/libs/.gitignore b/api/libs/.gitignore deleted file mode 100644 index 27620d5fd..000000000 --- a/api/libs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!*.jar diff --git a/api/libs/README.md b/api/libs/README.md deleted file mode 100644 index dc33ef0c6..000000000 --- a/api/libs/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# SQLITE-JDBC - -Deleted unsupported native shared libraries from `/org/sqlite/native/`: - -* Mac/ -* FreeBSD/ -* Linux-Musl/ -* Linux-Android/ -* Linux/ppc64/ -* Linux/ppc64/ -* Linux/aarch64/ -* Linux/x86/ -* Linux/arm/ -* Windows/aarch64/ -* Windows/x86/ -* Windows/armv7/ - -Reduction of 81.7% diff --git a/api/libs/sqlite-jdbc-3.43.0.0.jar b/api/libs/sqlite-jdbc-3.43.0.0.jar deleted file mode 100644 index 53aafa994..000000000 Binary files a/api/libs/sqlite-jdbc-3.43.0.0.jar and /dev/null differ diff --git a/api/src/main/kotlin/nebulosa/api/atlas/AtlasController.kt b/api/src/main/kotlin/nebulosa/api/atlas/AtlasController.kt index 8908fcd6a..60ea62d41 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/AtlasController.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/AtlasController.kt @@ -104,7 +104,7 @@ class AtlasController( @GetMapping("stars") fun searchStar( - @RequestParam @Valid @NotBlank text: String, + @RequestParam(required = false, defaultValue = "") text: String, @RequestParam(required = false, defaultValue = "") rightAscension: String, @RequestParam(required = false, defaultValue = "") declination: String, @RequestParam(required = false, defaultValue = "0.0") radius: Double, @@ -117,6 +117,11 @@ class AtlasController( constellation, magnitudeMin, magnitudeMax, type, ) + @GetMapping("stars/types") + fun starTypes(): List { + return atlasService.starTypes + } + @GetMapping("dsos/{dso}/position") fun positionOfDSO( @EntityBy dso: DeepSkyObjectEntity, @@ -138,7 +143,7 @@ class AtlasController( @GetMapping("dsos") fun searchDSO( - @RequestParam @Valid @NotBlank text: String, + @RequestParam(required = false, defaultValue = "") text: String, @RequestParam(required = false, defaultValue = "") rightAscension: String, @RequestParam(required = false, defaultValue = "") declination: String, @RequestParam(required = false, defaultValue = "0.0") radius: Double, @@ -151,6 +156,11 @@ class AtlasController( constellation, magnitudeMin, magnitudeMax, type, ) + @GetMapping("dsos/types") + fun dsoTypes(): List { + return atlasService.dsoTypes + } + @GetMapping("satellites/{satellite}/position") fun positionOfSatellite( @EntityBy satellite: SatelliteEntity, diff --git a/api/src/main/kotlin/nebulosa/api/atlas/AtlasService.kt b/api/src/main/kotlin/nebulosa/api/atlas/AtlasService.kt index 68e00be93..4d9aae469 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/AtlasService.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/AtlasService.kt @@ -46,6 +46,10 @@ class AtlasService( private val positions = HashMap() @Volatile private var sunImage = ByteArray(0) + val starTypes by lazy { starRepository.types() } + + val dsoTypes by lazy { deepSkyObjectRepository.types() } + fun imageOfSun(output: HttpServletResponse) { output.contentType = "image/png" output.outputStream.write(sunImage) @@ -154,7 +158,7 @@ class AtlasService( } fun altitudePointsOfSatellite(location: LocationEntity, satellite: SatelliteEntity, date: LocalDate, stepSize: Int): List { - val ephemeris = bodyEphemeris("TLE@$${satellite.tle}", location, LocalDateTime.of(date, LocalTime.now())) + val ephemeris = bodyEphemeris("TLE@${satellite.tle}", location, LocalDateTime.of(date, LocalTime.now())) return altitudePointsOfBody(ephemeris, stepSize) } @@ -181,10 +185,11 @@ class AtlasService( magnitudeMin: Double = -SkyObject.UNKNOWN_MAGNITUDE, magnitudeMax: Double = SkyObject.UNKNOWN_MAGNITUDE, type: SkyObjectType? = null, ) = starRepository.search( - text, + text.replace(INVALID_DSO_CHARS, "").replace("][", "").ifBlank { null }, rightAscension, declination, radius, constellation, - magnitudeMin.clampMagnitude(), magnitudeMax.clampMagnitude(), type + magnitudeMin.clampMagnitude(), magnitudeMax.clampMagnitude(), type, + Pageable.ofSize(5000), ) fun searchDSO( @@ -194,10 +199,11 @@ class AtlasService( magnitudeMin: Double = -SkyObject.UNKNOWN_MAGNITUDE, magnitudeMax: Double = SkyObject.UNKNOWN_MAGNITUDE, type: SkyObjectType? = null, ) = deepSkyObjectRepository.search( - text, + text.replace(INVALID_DSO_CHARS, "").replace("][", "").ifBlank { null }, rightAscension, declination, radius, constellation, - magnitudeMin.clampMagnitude(), magnitudeMax.clampMagnitude(), type + magnitudeMin.clampMagnitude(), magnitudeMax.clampMagnitude(), type, + Pageable.ofSize(5000), ) @Scheduled(fixedDelay = 15, timeUnit = TimeUnit.MINUTES) @@ -224,6 +230,8 @@ class AtlasService( private const val SUN = "10" private const val MOON = "301" + @JvmStatic private val INVALID_DSO_CHARS = Regex("[^\\w\\-\\s\\[\\].+]+") + @JvmStatic private fun Double.clampMagnitude(): Double { return if (this in -29.9..29.9) this diff --git a/api/src/main/kotlin/nebulosa/api/atlas/DeepSkyObjectRepository.kt b/api/src/main/kotlin/nebulosa/api/atlas/DeepSkyObjectRepository.kt index 3ef16baec..84b0c0c62 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/DeepSkyObjectRepository.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/DeepSkyObjectRepository.kt @@ -28,4 +28,7 @@ interface DeepSkyObjectRepository : JpaRepository { type: SkyObjectType? = null, pageable: Pageable = Pageable.unpaged(), ): List + + @Query("SELECT DISTINCT dso.type FROM DeepSkyObjectEntity dso") + fun types(): List } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/IERSThreadedTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt similarity index 93% rename from api/src/main/kotlin/nebulosa/api/atlas/IERSThreadedTask.kt rename to api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt index eac414961..6d7262835 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/IERSThreadedTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt @@ -14,7 +14,7 @@ import kotlin.io.path.outputStream @Component @ThreadedTask -class IERSThreadedTask( +class IERSUpdateTask( private val dataPath: Path, private val httpClient: OkHttpClient, ) : Runnable { @@ -49,6 +49,6 @@ class IERSThreadedTask( companion object { - @JvmStatic private val LOG = loggerFor() + @JvmStatic private val LOG = loggerFor() } } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteThreadedTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt similarity index 97% rename from api/src/main/kotlin/nebulosa/api/atlas/SatelliteThreadedTask.kt rename to api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt index 8ec534e63..3ea1cc0cf 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteThreadedTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt @@ -10,7 +10,7 @@ import java.util.concurrent.CompletableFuture @Component @ThreadedTask -class SatelliteThreadedTask( +class SatelliteUpdateTask( private val httpClient: OkHttpClient, private val configRepository: ConfigRepository, private val satelliteRepository: SatelliteRepository, @@ -104,6 +104,6 @@ class SatelliteThreadedTask( const val TLE_UPDATED_AT = "TLE_UPDATED_AT" const val UPDATE_INTERVAL = 1000L * 60 * 60 * 24 // 1 day - @JvmStatic private val LOG = loggerFor() + @JvmStatic private val LOG = loggerFor() } } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/AtlasDatabaseThreadedTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt similarity index 92% rename from api/src/main/kotlin/nebulosa/api/atlas/AtlasDatabaseThreadedTask.kt rename to api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt index 253dcc8de..dcfbef515 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/AtlasDatabaseThreadedTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt @@ -16,7 +16,7 @@ import kotlin.io.path.inputStream @Component @ThreadedTask -class AtlasDatabaseThreadedTask( +class SkyAtlasUpdateTask( private val objectMapper: ObjectMapper, private val configRepository: ConfigRepository, private val starsRepository: StarRepository, @@ -29,6 +29,8 @@ class AtlasDatabaseThreadedTask( val databaseVersion = configRepository.text(DATABASE_VERSION_KEY) if (databaseVersion != DATABASE_VERSION) { + LOG.info("Star/DSO database is out of date. currentVersion={}, newVersion={}", databaseVersion, DATABASE_VERSION) + starsRepository.deleteAllInBatch() deepSkyObjectRepository.deleteAllInBatch() @@ -97,9 +99,9 @@ class AtlasDatabaseThreadedTask( companion object { - const val DATABASE_VERSION = "2023.10.05" + const val DATABASE_VERSION = "2023.10.18" const val DATABASE_VERSION_KEY = "DATABASE_VERSION" - @JvmStatic private val LOG = loggerFor() + @JvmStatic private val LOG = loggerFor() } } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/StarRepository.kt b/api/src/main/kotlin/nebulosa/api/atlas/StarRepository.kt index fe8609962..d32dcbb59 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/StarRepository.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/StarRepository.kt @@ -29,4 +29,7 @@ interface StarRepository : JpaRepository { type: SkyObjectType? = null, pageable: Pageable = Pageable.unpaged(), ): List + + @Query("SELECT DISTINCT star.type FROM StarEntity star") + fun types(): List } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/ephemeris/HorizonsEphemerisProvider.kt b/api/src/main/kotlin/nebulosa/api/atlas/ephemeris/HorizonsEphemerisProvider.kt index e8f6034fa..828199166 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/ephemeris/HorizonsEphemerisProvider.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/ephemeris/HorizonsEphemerisProvider.kt @@ -3,6 +3,8 @@ package nebulosa.api.atlas.ephemeris import nebulosa.horizons.HorizonsElement import nebulosa.horizons.HorizonsQuantity import nebulosa.horizons.HorizonsService +import nebulosa.horizons.NonUniqueObjectException +import nebulosa.log.loggerFor import nebulosa.nova.position.GeographicPosition import nebulosa.sbd.SmallBody import org.springframework.stereotype.Service @@ -44,7 +46,7 @@ class HorizonsEphemerisProvider(private val horizonsService: HorizonsService) : startTime, endTime, extraPrecision = true, quantities = QUANTITIES, - ) + ).execute() } is String -> { if (target.startsWith("TLE@")) { @@ -55,24 +57,39 @@ class HorizonsEphemerisProvider(private val horizonsService: HorizonsService) : startTime, endTime, extraPrecision = true, quantities = QUANTITIES, - ) + ).execute() } else { - horizonsService - .observer( - target, - position.longitude, position.latitude, position.elevation, - startTime, endTime, - extraPrecision = true, - quantities = QUANTITIES, - ) + try { + horizonsService + .observer( + target, + position.longitude, position.latitude, position.elevation, + startTime, endTime, + extraPrecision = true, + quantities = QUANTITIES, + ).execute() + } catch (e: NonUniqueObjectException) { + LOG.warn("non unique object. target={}, matches={}", target, e.recordItems) + + horizonsService + .observer( + "$target;CAP;NOFRAG".replace(";;", ";"), + position.longitude, position.latitude, position.elevation, + startTime, endTime, + extraPrecision = true, + quantities = QUANTITIES, + ).execute() + } } } else -> return emptyList() - }.execute().body() ?: emptyList() + }.body() ?: emptyList() } companion object { + @JvmStatic private val LOG = loggerFor() + @JvmStatic private val QUANTITIES = arrayOf( HorizonsQuantity.ASTROMETRIC_RA, HorizonsQuantity.ASTROMETRIC_DEC, HorizonsQuantity.APPARENT_RA, HorizonsQuantity.APPARENT_DEC, diff --git a/api/src/main/kotlin/nebulosa/api/image/CoordinateInterpolation.kt b/api/src/main/kotlin/nebulosa/api/image/CoordinateInterpolation.kt new file mode 100644 index 000000000..5fa45e25a --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/image/CoordinateInterpolation.kt @@ -0,0 +1,11 @@ +package nebulosa.api.image + +import java.time.LocalDateTime + +@Suppress("ArrayInDataClass") +data class CoordinateInterpolation( + val ma: DoubleArray, + val md: DoubleArray, + val x0: Int, val y0: Int, val x1: Int, val y1: Int, + val delta: Int, val date: LocalDateTime?, +) diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageController.kt b/api/src/main/kotlin/nebulosa/api/image/ImageController.kt index 47410900e..a1a1cb63c 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageController.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageController.kt @@ -79,4 +79,9 @@ class ImageController( downsampleFactor, pathOrUrl, apiKey, ) } + + @GetMapping("coordinate-interpolation") + fun coordinateInterpolation(@RequestParam path: Path): CoordinateInterpolation? { + return imageService.coordinateInterpolation(path) + } } diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt index 9e92faded..9d973db7b 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt @@ -5,9 +5,7 @@ import jakarta.servlet.http.HttpServletResponse import nebulosa.api.framing.FramingService import nebulosa.api.framing.HipsSurveyType import nebulosa.astrometrynet.nova.NovaAstrometryNetService -import nebulosa.fits.FitsKeywords -import nebulosa.fits.dec -import nebulosa.fits.ra +import nebulosa.fits.* import nebulosa.imaging.ImageChannel import nebulosa.imaging.algorithms.* import nebulosa.io.transferAndClose @@ -18,7 +16,6 @@ import nebulosa.platesolving.astrometrynet.LocalAstrometryNetPlateSolver import nebulosa.platesolving.astrometrynet.NovaAstrometryNetPlateSolver import nebulosa.platesolving.watney.WatneyPlateSolver import nebulosa.sbd.SmallBodyDatabaseService -import nebulosa.simbad.SimbadCatalogType import nebulosa.simbad.SimbadService import nebulosa.simbad.SimbadSkyCatalog import nebulosa.skycatalog.ClassificationType @@ -30,9 +27,9 @@ import org.springframework.stereotype.Service import org.springframework.web.server.ResponseStatusException import java.nio.file.Path import java.time.Duration -import java.time.LocalDateTime import java.util.* import java.util.concurrent.CompletableFuture +import java.util.concurrent.ExecutorService import javax.imageio.ImageIO import kotlin.io.path.extension import kotlin.io.path.inputStream @@ -45,6 +42,7 @@ class ImageService( private val smallBodyDatabaseService: SmallBodyDatabaseService, private val simbadService: SimbadService, private val imageBucket: ImageBucket, + private val systemExecutorService: ExecutorService, ) { @Synchronized @@ -135,18 +133,20 @@ class ImageService( val annotations = Vector() val tasks = ArrayList>() - val dateTime = image.header - .getStringValue(FitsKeywords.DATE_OBS) - ?.ifBlank { null } - ?.let(LocalDateTime::parse) - ?: LocalDateTime.now() + val dateTime = image.header.observationDate if (minorPlanets && dateTime != null) { - CompletableFuture.runAsync { - LOG.info("finding minor planet annotations. dateTime={}, calibration={}", dateTime, calibration) + CompletableFuture.runAsync({ + val latitude = image.header.latitude.let { if (it.isFinite()) it else 0.0 } + val longitude = image.header.longitude.let { if (it.isFinite()) it else 0.0 } + + LOG.info( + "finding minor planet annotations. dateTime={}, latitude={}, longitude={}, calibration={}", + dateTime, latitude, longitude, calibration + ) val data = smallBodyDatabaseService.identify( - dateTime, 0.0, 0.0, 0.0, + dateTime, latitude, longitude, 0.0, calibration.rightAscension, calibration.declination, calibration.radius, minorPlanetMagLimit, ).execute().body() ?: return@runAsync @@ -169,13 +169,13 @@ class ImageService( } LOG.info("Found {} minor planets", count) - }.whenComplete { _, e -> e?.printStackTrace() }.also(tasks::add) + }, systemExecutorService).whenComplete { _, e -> e?.printStackTrace() }.also(tasks::add) } // val barycentric = VSOP87E.EARTH.at(UTC(TimeYMDHMS(dateTime))) if (stars || dsos) { - CompletableFuture.runAsync { + CompletableFuture.runAsync({ LOG.info("finding star annotations. dateTime={}, calibration={}", dateTime, calibration) val catalog = SimbadSkyCatalog(simbadService) @@ -197,10 +197,6 @@ class ImageService( catalog.search(calibration.rightAscension, calibration.declination, calibration.radius, types) for (entry in catalog) { - if (SimbadCatalogType.entries.none { it.matches(entry.name) }) { - continue - } - val (x, y) = wcs.skyToPix(entry.rightAscensionJ2000, entry.declinationJ2000) val annotation = if (entry.type.classification == ClassificationType.STAR) ImageAnnotation(x, y, star = entry) else ImageAnnotation(x, y, dso = entry) @@ -208,7 +204,7 @@ class ImageService( } LOG.info("Found {} stars/DSOs", catalog.size) - }.whenComplete { _, e -> e?.printStackTrace() }.also(tasks::add) + }, systemExecutorService).whenComplete { _, e -> e?.printStackTrace() }.also(tasks::add) } CompletableFuture.allOf(*tasks.toTypedArray()).join() @@ -267,13 +263,51 @@ class ImageService( val (image, calibration) = framingService .frame(rightAscension, declination, width, height, fov, rotation, hipsSurveyType)!! - val path = Path.of(System.getProperty("java.io.tmpdir"), "framing") + val path = Path.of("@framing") imageBucket.put(path, image, calibration) return path } + fun coordinateInterpolation(path: Path): CoordinateInterpolation? { + val (image, calibration) = imageBucket[path] ?: return null + + if (calibration == null || calibration.isEmpty || !calibration.solved) { + return null + } + + val wcs = try { + WCSTransform(calibration) + } catch (e: WCSException) { + LOG.error("unable to generate annotations for image. path={}", path) + return null + } + + val delta = COORDINATE_INTERPOLATION_DELTA + val width = image.width + (image.width % delta).let { if (it == 0) 0 else delta - it } + val xIter = 0..width step delta + val height = image.height + (image.height % delta).let { if (it == 0) 0 else delta - it } + val yIter = 0..height step delta + + val md = DoubleArray(xIter.count() * yIter.count()) + val ma = DoubleArray(md.size) + var count = 0 + + for (y in yIter) { + for (x in xIter) { + val (rightAscension, declination) = wcs.pixToSky(x.toDouble(), y.toDouble()) + ma[count] = rightAscension.toDegrees + md[count] = declination.toDegrees + count++ + } + } + + return CoordinateInterpolation(ma, md, 0, 0, width, height, delta, image.header.observationDate) + } + companion object { @JvmStatic private val LOG = loggerFor() + + private const val COORDINATE_INTERPOLATION_DELTA = 24 } } diff --git a/api/src/main/kotlin/nebulosa/api/locations/LocationThreadedTask.kt b/api/src/main/kotlin/nebulosa/api/locations/LocationInitializerTask.kt similarity index 81% rename from api/src/main/kotlin/nebulosa/api/locations/LocationThreadedTask.kt rename to api/src/main/kotlin/nebulosa/api/locations/LocationInitializerTask.kt index 26612ee77..bea5ec9b3 100644 --- a/api/src/main/kotlin/nebulosa/api/locations/LocationThreadedTask.kt +++ b/api/src/main/kotlin/nebulosa/api/locations/LocationInitializerTask.kt @@ -5,7 +5,7 @@ import org.springframework.stereotype.Component @Component @ThreadedTask -class LocationThreadedTask(private val locationRepository: LocationRepository) : Runnable { +class LocationInitializerTask(private val locationRepository: LocationRepository) : Runnable { override fun run() { if (locationRepository.count() <= 0) { diff --git a/api/src/main/resources/logback.xml b/api/src/main/resources/logback.xml new file mode 100644 index 000000000..7c0a12e16 --- /dev/null +++ b/api/src/main/resources/logback.xml @@ -0,0 +1,27 @@ + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n + + + + + ${LOG_FILE} + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n + + + + + + + + diff --git a/api/src/test/kotlin/SkyDatabaseGenerator.kt b/api/src/test/kotlin/SkyDatabaseGenerator.kt index daadf77f9..915d14cbf 100644 --- a/api/src/test/kotlin/SkyDatabaseGenerator.kt +++ b/api/src/test/kotlin/SkyDatabaseGenerator.kt @@ -17,6 +17,8 @@ import nebulosa.time.UTC import okhttp3.OkHttpClient import java.io.InputStreamReader import java.nio.file.Path +import java.time.LocalDate +import java.time.ZoneOffset import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import java.util.zip.GZIPOutputStream @@ -26,9 +28,6 @@ import kotlin.math.min typealias CatalogNameProvider = Pair String?> -// TODO: Herschel Catalog -// TODO: Dunlop Catalog: https://www.docdb.net/tutorials/dunlop_catalogue.php - object SkyDatabaseGenerator { @JvmStatic private val STAR_DATABASE_PATH = Path.of("api/data/stars.json.gz") @@ -64,24 +63,30 @@ object SkyDatabaseGenerator { .commentCharacter('#') .skipComments(true) - @JvmStatic private val CALDWELL = resource("Caldwell.csv")!! + @JvmStatic private val CALDWELL = resource("caldwell_catalog.csv")!! .use { stream -> CSV_READER.build(InputStreamReader(stream, Charsets.UTF_8)) - .associate { it.getField("NGC number").ifEmpty { it.getField("Common name") } to it.getField("Caldwell number") } + .associate { it.getField("NGC").ifEmpty { it.getField("Common name") } to it.getField("Caldwell") } } - @JvmStatic private val BENNETT = resource("Bennett.csv")!! + @JvmStatic private val BENNETT = resource("bennett_catalog.csv")!! .use { stream -> CSV_READER.build(InputStreamReader(stream, Charsets.UTF_8)) .associate { it.getField("NGC") to it.getField("Bennett") } } - @JvmStatic private val DUNLOP = resource("Dunlop.csv")!! + @JvmStatic private val DUNLOP = resource("dunlop_catalog.csv")!! .use { stream -> CSV_READER.build(InputStreamReader(stream, Charsets.UTF_8)) .associate { it.getField("NGC") to it.getField("Dunlop") } } + @JvmStatic private val HERSHEL = resource("hershel_catalog.csv")!! + .use { stream -> + CSV_READER.build(InputStreamReader(stream, Charsets.UTF_8)) + .associate { it.getField("NGC") to it.getField("Hershel") } + } + @JvmStatic fun main(args: Array) { val names = LinkedHashSet(8) @@ -131,6 +136,9 @@ object SkyDatabaseGenerator { if (name in DUNLOP) { names.add("Dunlop ${DUNLOP[name]}") } + if (name in HERSHEL) { + names.add("Hershel ${HERSHEL[name]}") + } if (useIAU && type === STAR_CATALOG_TYPES[0] && name in iauNames) { iauNames.remove(name) @@ -143,7 +151,8 @@ object SkyDatabaseGenerator { return magnitude } - val currentTime = UTC(TimeYMDHMS(2023, 10, 5, 12)) + val now = LocalDate.now(ZoneOffset.UTC) + val currentTime = UTC(TimeYMDHMS(now.year, now.monthValue, now.dayOfMonth, 12)) val data = HashMap(32000) val skyObjectTypes = HashSet(SkyObjectType.entries.size) @@ -210,13 +219,13 @@ object SkyDatabaseGenerator { val constellation = SkyObject.computeConstellation(rightAscensionJ2000, declinationJ2000, currentTime) data[id] = if (isDSO) DeepSkyObjectEntity( - id, names.joinToString("|"), magnitude, + id, names.joinToString("") { "[$it]" }, magnitude, rightAscensionJ2000, declinationJ2000, type, majorAxis, minorAxis, orientation, pmRA, pmDEC, parallax.mas, radialVelocity, redshift, distance, constellation, ) else StarEntity( - id, names.joinToString("|"), magnitude, + id, names.joinToString("") { "[$it]" }, magnitude, rightAscensionJ2000, declinationJ2000, type, spType, pmRA, pmDEC, parallax.mas, radialVelocity, redshift, diff --git a/api/src/test/resources/Caldwell.csv b/api/src/test/resources/Caldwell.csv deleted file mode 100644 index 59c962c23..000000000 --- a/api/src/test/resources/Caldwell.csv +++ /dev/null @@ -1,111 +0,0 @@ -Caldwell number,NGC number,Common name,Type,Magnitude -1,NGC 188,,Open Cluster,8.1 -2,NGC 40,Bow-Tie Nebula,Planetary Nebula,11 -3,NGC 4236,,Barred Spiral Galaxy,9.7 -4,NGC 7023,Iris Nebula,Open Cluster and Nebula,7 -5,IC 342,Hidden Galaxy,Spiral Galaxy,9 -6,NGC 6543,Cat's Eye Nebula,Planetary Nebula,9 -7,NGC 2403,,Spiral Galaxy,8.4 -8,NGC 559,,Open Cluster,9.5 -9,Sh2-155,Cave Nebula,Nebula,7.7 -10,NGC 663,,Open Cluster,7.1 -11,NGC 7635,Bubble Nebula,Nebula,10 -12,NGC 6946,Fireworks Galaxy,Spiral Galaxy,8.9 -13,NGC 457,"Owl Cluster, E.T. Cluster",Open Cluster,6.4 -14,NGC 869,"Double Cluster",Open Cluster,4 -14,NGC 884,"Double Cluster",Open Cluster,4 -15,NGC 6826,Blinking Planetary,Planetary Nebula,10 -16,NGC 7243,,Open Cluster,6.4 -17,NGC 147,,Dwarf Spheroidal Galaxy,9.3 -18,NGC 185,,Dwarf Spheroidal Galaxy,9.2 -19,IC 5146,Cocoon Nebula,Open Cluster and Nebula,7.2 -20,NGC 7000,North America Nebula,Nebula,4 -21,NGC 4449,,Irregular galaxy,9.4 -22,NGC 7662,Blue Snowball,Planetary Nebula,9 -23,NGC 891,Silver Sliver Galaxy,Spiral Galaxy,10 -24,NGC 1275,Perseus A,Supergiant Elliptical Galaxy,11.6 -25,NGC 2419,,Globular Cluster,10.4 -26,NGC 4244,,Spiral Galaxy,10.2 -27,NGC 6888,Crescent Nebula,Nebula,7.4 -28,NGC 752,,Open Cluster,5.7 -29,NGC 5005,,Spiral Galaxy,9.8 -30,NGC 7331,,Spiral Galaxy,9.5 -31,IC 405,Flaming Star Nebula,Nebula,13 -32,NGC 4631,Whale Galaxy,Barred Spiral Galaxy,9.3 -33,NGC 6992,East Veil Nebula,Supernova Remnant,7 -34,NGC 6960,West Veil Nebula,Supernova Remnant,7 -35,NGC 4889,Coma B,Supergiant Elliptical Galaxy,11.4 -36,NGC 4559,,Spiral Galaxy,9.9 -37,NGC 6885,,Open Cluster,6 -38,NGC 4565,Needle Galaxy,Spiral Galaxy,9.6 -39,NGC 2392,Eskimo Nebula/Clown Face Nebula,Planetary Nebula,10 -40,NGC 3626,,Lenticular Galaxy,10.9 -41,Melotte 25,Hyades,Open Cluster,0.5 -42,NGC 7006,,Globular Cluster,10.6 -43,NGC 7814,,Spiral Galaxy,10.5 -44,NGC 7479,,Barred Spiral Galaxy,11 -45,NGC 5248,,Spiral Galaxy,10.2 -46,NGC 2261,Hubble's Variable Nebula,Nebula,- -47,NGC 6934,,Globular Cluster,8.9 -48,NGC 2775,,Spiral Galaxy,10.3 -49,NGC 2237,Rosette Nebula,Nebula,9.0 -50,NGC 2244,Satellite Cluster,Open Cluster,4.8 -51,IC 1613,,Irregular galaxy,9.3 -52,NGC 4697,,Elliptical galaxy,9.3 -53,NGC 3115,Spindle Galaxy,Lenticular Galaxy,9.2 -54,NGC 2506,,Open Cluster,7.6 -55,NGC 7009,Saturn Nebula,Planetary Nebula,8 -56,NGC 246,Skull Nebula,Planetary Nebula,8 -57,NGC 6822,Barnard's Galaxy,Barred irregular galaxy,9 -58,NGC 2360,Caroline's Cluster,Open Cluster,7.2 -59,NGC 3242,Ghost of Jupiter,Planetary Nebula,9 -60,NGC 4038,Antennae Galaxies,Interacting galaxy,10.7 -61,NGC 4039,Antennae Galaxies,Interacting galaxy,13 -62,NGC 247,,Spiral Galaxy,8.9 -63,NGC 7293,Helix Nebula,Planetary Nebula,7.3 -64,NGC 2362,Tau Canis Majoris Cluster,Open Cluster and Nebula,4.1 -65,NGC 253,Sculptor Galaxy,Spiral Galaxy,7.1 -66,NGC 5694,,Globular Cluster,10.2 -67,NGC 1097,,Barred Spiral Galaxy,9.3 -68,NGC 6729,R CrA Nebula,Nebula,- -69,NGC 6302,Bug Nebula,Planetary Nebula,13 -70,NGC 300,,Spiral Galaxy,9 -71,NGC 2477,,Open Cluster,5.8 -72,NGC 55,,Barred Spiral Galaxy,8 -73,NGC 1851,,Globular Cluster,7.3 -74,NGC 3132,Eight Burst Nebula,Planetary Nebula,8 -75,NGC 6124,,Open Cluster,5.8 -76,NGC 6231,,Open Cluster and Nebula,2.6 -77,NGC 5128,Centaurus A,Elliptical or Lenticular Galaxy,7 -78,NGC 6541,,Globular Cluster,6.6 -79,NGC 3201,,Globular Cluster,6.8 -80,NGC 5139,Omega Centauri,Globular Cluster,3.7 -81,NGC 6352,,Globular Cluster,8.2 -82,NGC 6193,,Open Cluster,5.2 -83,NGC 4945,,Barred Spiral Galaxy,9 -84,NGC 5286,,Globular Cluster,7.6 -85,IC 2391,Omicron Velorum Cluster,Open Cluster,2.5 -86,NGC 6397,,Globular Cluster,5.7 -87,NGC 1261,,Globular Cluster,8.4 -88,NGC 5823,,Open Cluster,7.9 -89,NGC 6087,S Normae Cluster,Open Cluster,5.4 -90,NGC 2867,,Planetary Nebula,10 -91,NGC 3532,Wishing Well Cluster,Open Cluster,3 -92,NGC 3372,Eta Carinae Nebula,Nebula,3 -93,NGC 6752,Great Peacock Globular,Globular Cluster,5.4 -94,NGC 4755,Jewel Box,Open Cluster,4.2 -95,NGC 6025,,Open Cluster,5.1 -96,NGC 2516,Southern Beehive Cluster,Open Cluster,3.8 -97,NGC 3766,Pearl Cluster,Open Cluster,5.3 -98,NGC 4609,,Open Cluster,6.9 -99,,Coalsack Nebula,Dark Nebula,- -100,IC 2944,Lambda Centauri Nebula,Open Cluster and Nebula,4.5 -101,NGC 6744,,Spiral Galaxy,9 -102,IC 2602,Theta Car Cluster,Open Cluster,1.9 -103,NGC 2070,Tarantula Nebula,Open Cluster and Nebula,8.2 -104,NGC 362,,Globular Cluster,6.6 -105,NGC 4833,,Globular Cluster,7.4 -106,NGC 104,47 Tucanae,Globular Cluster,4 -107,NGC 6101,,Globular Cluster,9.3 -108,NGC 4372,,Globular Cluster,7.8 -109,NGC 3195,,Planetary Nebula,11.6 diff --git a/api/src/test/resources/Bennett.csv b/api/src/test/resources/bennett_catalog.csv similarity index 96% rename from api/src/test/resources/Bennett.csv rename to api/src/test/resources/bennett_catalog.csv index 364d80f3a..f5241ae7b 100644 --- a/api/src/test/resources/Bennett.csv +++ b/api/src/test/resources/bennett_catalog.csv @@ -1,3 +1,4 @@ +# Source: https://www.docdb.net/tutorials/bennett_catalogue.php Bennett,NGC 1,NGC 55 2,NGC 104 diff --git a/api/src/test/resources/caldwell_catalog.csv b/api/src/test/resources/caldwell_catalog.csv new file mode 100644 index 000000000..092d3383b --- /dev/null +++ b/api/src/test/resources/caldwell_catalog.csv @@ -0,0 +1,112 @@ +# Source: https://en.wikipedia.org/wiki/Caldwell_catalogue +Caldwell,NGC,Common name +1,NGC 188, +2,NGC 40,Bow-Tie Nebula +3,NGC 4236, +4,NGC 7023,Iris Nebula +5,IC 342,Hidden Galaxy +6,NGC 6543,Cat's Eye Nebula +7,NGC 2403, +8,NGC 559, +9,Sh2-155,Cave Nebula +10,NGC 663, +11,NGC 7635,Bubble Nebula +12,NGC 6946,Fireworks Galaxy +13,NGC 457,Owl Cluster +14,NGC 869,Double Cluster +14,NGC 884,Double Cluster +15,NGC 6826,Blinking Planetary +16,NGC 7243, +17,NGC 147, +18,NGC 185, +19,IC 5146,Cocoon Nebula +20,NGC 7000,North America Nebula +21,NGC 4449, +22,NGC 7662,Blue Snowball +23,NGC 891,Silver Sliver Galaxy +24,NGC 1275,Perseus A +25,NGC 2419, +26,NGC 4244, +27,NGC 6888,Crescent Nebula +28,NGC 752, +29,NGC 5005, +30,NGC 7331, +31,IC 405,Flaming Star Nebula +32,NGC 4631,Whale Galaxy +33,NGC 6992,East Veil Nebula +34,NGC 6960,West Veil Nebula +35,NGC 4889,Coma B +36,NGC 4559, +37,NGC 6885, +38,NGC 4565,Needle Galaxy +39,NGC 2392,Eskimo Nebula +40,NGC 3626, +41,Melotte 25,Hyades +42,NGC 7006, +43,NGC 7814, +44,NGC 7479, +45,NGC 5248, +46,NGC 2261,Hubble's Variable Nebula +47,NGC 6934, +48,NGC 2775, +49,NGC 2237,Rosette Nebula +50,NGC 2244,Satellite Cluster +51,IC 1613, +52,NGC 4697, +53,NGC 3115,Spindle Galaxy +54,NGC 2506, +55,NGC 7009,Saturn Nebula +56,NGC 246,Skull Nebula +57,NGC 6822,Barnard's Galaxy +58,NGC 2360,Caroline's Cluster +59,NGC 3242,Ghost of Jupiter +60,NGC 4038,Antennae Galaxies +61,NGC 4039,Antennae Galaxies +62,NGC 247, +63,NGC 7293,Helix Nebula +64,NGC 2362,Tau Canis Majoris Cluster +65,NGC 253,Sculptor Galaxy +66,NGC 5694, +67,NGC 1097, +68,NGC 6729,R CrA Nebula +69,NGC 6302,Bug Nebula +70,NGC 300, +71,NGC 2477, +72,NGC 55, +73,NGC 1851, +74,NGC 3132,Eight Burst Nebula +75,NGC 6124, +76,NGC 6231, +77,NGC 5128,Centaurus A +78,NGC 6541, +79,NGC 3201, +80,NGC 5139,Omega Centauri +81,NGC 6352, +82,NGC 6193, +83,NGC 4945, +84,NGC 5286, +85,IC 2391,Omicron Velorum Cluster +86,NGC 6397, +87,NGC 1261, +88,NGC 5823, +89,NGC 6087,S Normae Cluster +90,NGC 2867, +91,NGC 3532,Wishing Well Cluster +92,NGC 3372,Eta Carinae Nebula +93,NGC 6752,Great Peacock Globular +94,NGC 4755,Jewel Box +95,NGC 6025, +96,NGC 2516,Southern Beehive Cluster +97,NGC 3766,Pearl Cluster +98,NGC 4609, +99,,Coalsack Nebula +100,IC 2944,Lambda Centauri Nebula +101,NGC 6744, +102,IC 2602,Theta Car Cluster +103,NGC 2070,Tarantula Nebula +104,NGC 362, +105,NGC 4833, +106,NGC 104,47 Tucanae +107,NGC 6101, +108,NGC 4372, +109,NGC 3195, diff --git a/api/src/test/resources/Dunlop.csv b/api/src/test/resources/dunlop_catalog.csv similarity index 96% rename from api/src/test/resources/Dunlop.csv rename to api/src/test/resources/dunlop_catalog.csv index 9b551cf93..9cf4bfda1 100644 --- a/api/src/test/resources/Dunlop.csv +++ b/api/src/test/resources/dunlop_catalog.csv @@ -1,3 +1,4 @@ +# Source: https://www.docdb.net/tutorials/dunlop_catalogue.php Dunlop,NGC 1,NGC 7590 2,NGC 7599 diff --git a/api/src/test/resources/hershel_catalog.csv b/api/src/test/resources/hershel_catalog.csv new file mode 100644 index 000000000..190866aba --- /dev/null +++ b/api/src/test/resources/hershel_catalog.csv @@ -0,0 +1,404 @@ +# Source: https://www.go-astronomy.com/herschel-objects.htm +Hershel,NGC +1,NGC 40 +2,NGC 129 +3,NGC 136 +4,NGC 157 +5,NGC 185 +6,NGC 205 +7,NGC 225 +8,NGC 246 +9,NGC 247 +10,NGC 253 +11,NGC 278 +12,NGC 288 +13,NGC 381 +14,NGC 404 +15,NGC 436 +16,NGC 457 +17,NGC 488 +18,NGC 524 +19,NGC 559 +20,NGC 584 +21,NGC 596 +22,NGC 598 +23,NGC 613 +24,NGC 615 +25,NGC 637 +26,NGC 650 +26,NGC 651 +27,NGC 654 +28,NGC 659 +29,NGC 663 +30,NGC 720 +31,NGC 752 +32,NGC 772 +33,NGC 779 +34,NGC 869 +35,NGC 884 +36,NGC 891 +37,NGC 908 +38,NGC 936 +39,NGC 1022 +40,NGC 1023 +41,NGC 1027 +42,NGC 1052 +43,NGC 1055 +44,NGC 1084 +45,NGC 1245 +46,NGC 1342 +47,NGC 1407 +48,NGC 1444 +49,NGC 1501 +50,NGC 1502 +51,NGC 1513 +52,NGC 1528 +53,NGC 1535 +54,NGC 1545 +55,NGC 1647 +56,NGC 1664 +57,NGC 1788 +58,NGC 1817 +59,NGC 1857 +60,NGC 1907 +61,NGC 1931 +62,NGC 1961 +63,NGC 1964 +64,NGC 1980 +65,NGC 1999 +66,NGC 2022 +67,NGC 2024 +68,NGC 2126 +69,NGC 2129 +70,NGC 2158 +71,NGC 2169 +72,NGC 2185 +73,NGC 2186 +74,NGC 2194 +75,NGC 2204 +76,NGC 2215 +77,NGC 2232 +78,NGC 2244 +79,NGC 2251 +80,NGC 2264 +81,NGC 2266 +82,NGC 2281 +83,NGC 2286 +84,NGC 2301 +85,NGC 2304 +86,NGC 2311 +87,NGC 2324 +88,NGC 2335 +89,NGC 2343 +90,NGC 2353 +91,NGC 2354 +92,NGC 2355 +93,NGC 2360 +94,NGC 2362 +95,NGC 2371 +96,NGC 2372 +97,NGC 2392 +98,NGC 2395 +99,NGC 2403 +100,NGC 2419 +101,NGC 2420 +102,NGC 2421 +103,NGC 2422 +104,NGC 2423 +105,NGC 2438 +106,NGC 2440 +107,NGC 2479 +108,NGC 2482 +109,NGC 2489 +110,NGC 2506 +111,NGC 2509 +112,NGC 2527 +113,NGC 2539 +114,NGC 2548 +115,NGC 2567 +116,NGC 2571 +117,NGC 2613 +118,NGC 2627 +119,NGC 2655 +120,NGC 2681 +121,NGC 2683 +122,NGC 2742 +123,NGC 2768 +124,NGC 2775 +125,NGC 2782 +126,NGC 2787 +127,NGC 2811 +128,NGC 2841 +129,NGC 2859 +130,NGC 2903 +131,NGC 2950 +132,NGC 2964 +133,NGC 2974 +134,NGC 2976 +135,NGC 2985 +136,NGC 3034 +137,NGC 3077 +138,NGC 3079 +139,NGC 3115 +140,NGC 3147 +141,NGC 3166 +142,NGC 3169 +143,NGC 3184 +144,NGC 3190 +145,NGC 3193 +146,NGC 3198 +147,NGC 3226 +148,NGC 3227 +149,NGC 3242 +150,NGC 3245 +151,NGC 3277 +152,NGC 3294 +153,NGC 3310 +154,NGC 3344 +155,NGC 3377 +156,NGC 3379 +157,NGC 3384 +158,NGC 3395 +159,NGC 3412 +160,NGC 3414 +161,NGC 3432 +162,NGC 3486 +163,NGC 3489 +164,NGC 3504 +165,NGC 3521 +166,NGC 3556 +167,NGC 3593 +168,NGC 3607 +169,NGC 3608 +170,NGC 3610 +171,NGC 3613 +172,NGC 3619 +173,NGC 3621 +174,NGC 3626 +175,NGC 3628 +176,NGC 3631 +177,NGC 3640 +178,NGC 3655 +179,NGC 3665 +180,NGC 3675 +181,NGC 3686 +182,NGC 3726 +183,NGC 3729 +184,NGC 3810 +185,NGC 3813 +186,NGC 3877 +187,NGC 3893 +188,NGC 3898 +189,NGC 3900 +190,NGC 3912 +191,NGC 3912 +192,NGC 3941 +193,NGC 3945 +194,NGC 3949 +195,NGC 3953 +196,NGC 3962 +197,NGC 3982 +198,NGC 3992 +199,NGC 3998 +200,NGC 4026 +201,NGC 4027 +202,NGC 4030 +203,NGC 4036 +204,NGC 4038 +204,NGC 4039 +205,NGC 4041 +206,NGC 4051 +207,NGC 4085 +208,NGC 4088 +209,NGC 4102 +210,NGC 4111 +211,NGC 4143 +212,NGC 4147 +213,NGC 4150 +214,NGC 4151 +215,NGC 4179 +216,NGC 4203 +217,NGC 4214 +218,NGC 4216 +219,NGC 4245 +220,NGC 4251 +221,NGC 4258 +222,NGC 4261 +223,NGC 4273 +224,NGC 4274 +225,NGC 4278 +226,NGC 4281 +227,NGC 4293 +228,NGC 4303 +229,NGC 4314 +230,NGC 4346 +231,NGC 4350 +232,NGC 4361 +233,NGC 4365 +234,NGC 4371 +235,NGC 4394 +236,NGC 4414 +237,NGC 4419 +238,NGC 4429 +239,NGC 4435 +240,NGC 4438 +241,NGC 4442 +242,NGC 4448 +243,NGC 4449 +244,NGC 4450 +245,NGC 4459 +246,NGC 4473 +247,NGC 4477 +248,NGC 4478 +249,NGC 4485 +250,NGC 4490 +251,NGC 4494 +252,NGC 4526 +253,NGC 4527 +254,NGC 4535 +255,NGC 4536 +256,NGC 4546 +257,NGC 4548 +258,NGC 4550 +259,NGC 4559 +260,NGC 4565 +261,NGC 4570 +262,NGC 4594 +263,NGC 4596 +264,NGC 4618 +265,NGC 4631 +266,NGC 4636 +267,NGC 4643 +268,NGC 4654 +269,NGC 4656 +270,NGC 4660 +271,NGC 4665 +272,NGC 4666 +273,NGC 4689 +274,NGC 4697 +275,NGC 4698 +276,NGC 4669 +277,NGC 4725 +278,NGC 4753 +279,NGC 4754 +280,NGC 4762 +281,NGC 4781 +282,NGC 4800 +283,NGC 4845 +284,NGC 4856 +285,NGC 4866 +286,NGC 4900 +287,NGC 4958 +288,NGC 4995 +289,NGC 5005 +290,NGC 5033 +291,NGC 5054 +292,NGC 5195 +293,NGC 5248 +294,NGC 5273 +295,NGC 5322 +296,NGC 5363 +297,NGC 5364 +298,NGC 5466 +299,NGC 5473 +300,NGC 5474 +301,NGC 5557 +302,NGC 5566 +303,NGC 5576 +304,NGC 5631 +305,NGC 5634 +306,NGC 5676 +307,NGC 5689 +308,NGC 5694 +309,NGC 5746 +310,NGC 5846 +311,NGC 5866 +312,NGC 5897 +313,NGC 5907 +314,NGC 5982 +315,NGC 6118 +316,NGC 6144 +317,NGC 6171 +318,NGC 6207 +319,NGC 6217 +320,NGC 6229 +321,NGC 6235 +322,NGC 6284 +323,NGC 6287 +324,NGC 6293 +325,NGC 6304 +326,NGC 6316 +327,NGC 6342 +328,NGC 6355 +329,NGC 6356 +330,NGC 6369 +331,NGC 6401 +332,NGC 6426 +333,NGC 6440 +334,NGC 6445 +335,NGC 6451 +336,NGC 6514 +337,NGC 6517 +338,NGC 6520 +339,NGC 6522 +340,NGC 6528 +341,NGC 6540 +342,NGC 6543 +343,NGC 6544 +344,NGC 6553 +345,NGC 6568 +346,NGC 6569 +347,NGC 6583 +348,NGC 6624 +349,NGC 6629 +350,NGC 6633 +351,NGC 6638 +352,NGC 6642 +353,NGC 6645 +354,NGC 6664 +355,NGC 6712 +356,NGC 6755 +357,NGC 6756 +358,NGC 6781 +359,NGC 6802 +360,NGC 6818 +361,NGC 6823 +362,NGC 6826 +363,NGC 6830 +364,NGC 6834 +365,NGC 6866 +366,NGC 6882 +367,NGC 6885 +368,NGC 6905 +369,NGC 6910 +370,NGC 6934 +371,NGC 6939 +372,NGC 6940 +373,NGC 6946 +374,NGC 7000 +375,NGC 7006 +376,NGC 7008 +377,NGC 7009 +378,NGC 7044 +379,NGC 7062 +380,NGC 7086 +381,NGC 7128 +382,NGC 7142 +383,NGC 7160 +384,NGC 7209 +385,NGC 7217 +386,NGC 7243 +387,NGC 7296 +388,NGC 7331 +389,NGC 7380 +390,NGC 7448 +391,NGC 7479 +392,NGC 7510 +393,NGC 7606 +394,NGC 7662 +395,NGC 7686 +396,NGC 7723 +397,NGC 7727 +398,NGC 7789 +399,NGC 7790 +400,NGC 7814 \ No newline at end of file diff --git a/desktop/README.md b/desktop/README.md index 611f8aa58..f0d9076cc 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -8,8 +8,7 @@ The complete integrated solution for all of your astronomical imaging needs. ## Camera -![](camera.1.png) -![](camera.2.png) +![](camera.png) ## Mount @@ -23,9 +22,9 @@ The complete integrated solution for all of your astronomical imaging needs. ![](filter-wheel.png) -## Guiding +## Guider -![](guiding.png) +![](guider.png) ## Sky Atlas @@ -41,7 +40,6 @@ The complete integrated solution for all of your astronomical imaging needs. ## Image ![](image.png) -![](image.2.png) ## Framing diff --git a/desktop/alignment.darv.png b/desktop/alignment.darv.png index c565d344b..a71936b40 100644 Binary files a/desktop/alignment.darv.png and b/desktop/alignment.darv.png differ diff --git a/desktop/app/main.ts b/desktop/app/main.ts index 7951aecd6..012c13451 100644 --- a/desktop/app/main.ts +++ b/desktop/app/main.ts @@ -146,7 +146,7 @@ function createWindow(data: OpenWindow) { browserWindows.clear() - api?.kill(0) + api?.kill() } else { for (const [key, value] of browserWindows) { if (value === window) { @@ -165,7 +165,7 @@ function createWindow(data: OpenWindow) { function createSplashScreen() { let splashWindow = browserWindows.get('splash') - if (!serve && splashWindow === null) { + if (!serve && !splashWindow) { splashWindow = new BrowserWindow({ width: 512, height: 512, @@ -233,6 +233,8 @@ try { app.on('ready', () => setTimeout(startApp, 400)) app.on('window-all-closed', () => { + api?.kill() + if (process.platform !== 'darwin') { app.quit() } diff --git a/desktop/app/package-lock.json b/desktop/app/package-lock.json index 8bb2cb383..76d47019a 100644 --- a/desktop/app/package-lock.json +++ b/desktop/app/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@stomp/stompjs": "7.0.0", - "cron": "3.1.0", + "cron": "3.1.3", "ws": "8.14.2" } }, @@ -25,18 +25,18 @@ "integrity": "sha512-l5cpE57br4BIjK+9BSkFBOsWtwv6J9bJpC7gdXIzZyI0vuKvNTk0wZZrkQxMGsUAuGW9+WMNWF2IJMD7br2yeQ==" }, "node_modules/cron": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.0.tgz", - "integrity": "sha512-u6Z89TV7zhG7aW7MX7aLQhK5PYjTzFpzjFgiSX5r7qC1vjPvRt1FVfarHRaN/5IokEXM1DRJcXnwXI0e9G0awA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.3.tgz", + "integrity": "sha512-KVxeKTKYj2eNzN4ElnT6nRSbjbfhyxR92O/Jdp6SH3pc05CDJws59jBrZWEMQlxevCiE6QUTrXy+Im3vC3oD3A==", "dependencies": { "@types/luxon": "~3.3.0", - "luxon": "~3.3.0" + "luxon": "~3.4.0" } }, "node_modules/luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz", + "integrity": "sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==", "engines": { "node": ">=12" } diff --git a/desktop/app/package.json b/desktop/app/package.json index b4d99a53f..09e21751f 100644 --- a/desktop/app/package.json +++ b/desktop/app/package.json @@ -13,6 +13,6 @@ "dependencies": { "@stomp/stompjs": "7.0.0", "ws": "8.14.2", - "cron": "3.1.0" + "cron": "3.1.3" } } diff --git a/desktop/atlas.1.png b/desktop/atlas.1.png index ca80bb65d..feaa1e96c 100644 Binary files a/desktop/atlas.1.png and b/desktop/atlas.1.png differ diff --git a/desktop/atlas.2.png b/desktop/atlas.2.png index 19dd92e5b..74f0c75d4 100644 Binary files a/desktop/atlas.2.png and b/desktop/atlas.2.png differ diff --git a/desktop/atlas.3.png b/desktop/atlas.3.png index b03de50b8..29248b6e2 100644 Binary files a/desktop/atlas.3.png and b/desktop/atlas.3.png differ diff --git a/desktop/atlas.4.png b/desktop/atlas.4.png index 326e27e84..be73a183c 100644 Binary files a/desktop/atlas.4.png and b/desktop/atlas.4.png differ diff --git a/desktop/atlas.5.png b/desktop/atlas.5.png index 64e188af8..1f78287a9 100644 Binary files a/desktop/atlas.5.png and b/desktop/atlas.5.png differ diff --git a/desktop/atlas.6.png b/desktop/atlas.6.png index 52c11b58b..24c1a0203 100644 Binary files a/desktop/atlas.6.png and b/desktop/atlas.6.png differ diff --git a/desktop/atlas.7.png b/desktop/atlas.7.png index 8b31a5afb..57592a36a 100644 Binary files a/desktop/atlas.7.png and b/desktop/atlas.7.png differ diff --git a/desktop/atlas.8.png b/desktop/atlas.8.png index c4ea7d71f..39e73f29b 100644 Binary files a/desktop/atlas.8.png and b/desktop/atlas.8.png differ diff --git a/desktop/camera.1.png b/desktop/camera.1.png deleted file mode 100644 index a024c67be..000000000 Binary files a/desktop/camera.1.png and /dev/null differ diff --git a/desktop/camera.2.png b/desktop/camera.2.png deleted file mode 100644 index f84cddc27..000000000 Binary files a/desktop/camera.2.png and /dev/null differ diff --git a/desktop/camera.png b/desktop/camera.png new file mode 100644 index 000000000..6b9ebe01d Binary files /dev/null and b/desktop/camera.png differ diff --git a/desktop/filter-wheel.png b/desktop/filter-wheel.png index b61f814df..083857457 100644 Binary files a/desktop/filter-wheel.png and b/desktop/filter-wheel.png differ diff --git a/desktop/focuser.png b/desktop/focuser.png index 67df06e41..78003ee07 100644 Binary files a/desktop/focuser.png and b/desktop/focuser.png differ diff --git a/desktop/framing.png b/desktop/framing.png index 6fb17f436..c05318918 100644 Binary files a/desktop/framing.png and b/desktop/framing.png differ diff --git a/desktop/guider.png b/desktop/guider.png new file mode 100644 index 000000000..f20e2c348 Binary files /dev/null and b/desktop/guider.png differ diff --git a/desktop/guiding.png b/desktop/guiding.png deleted file mode 100644 index 2fbe8f214..000000000 Binary files a/desktop/guiding.png and /dev/null differ diff --git a/desktop/home.png b/desktop/home.png index 09be6a2a4..fed30a480 100644 Binary files a/desktop/home.png and b/desktop/home.png differ diff --git a/desktop/image.2.png b/desktop/image.2.png deleted file mode 100644 index 3c51898a7..000000000 Binary files a/desktop/image.2.png and /dev/null differ diff --git a/desktop/image.png b/desktop/image.png index a42855789..1937d2f8c 100644 Binary files a/desktop/image.png and b/desktop/image.png differ diff --git a/desktop/indi.png b/desktop/indi.png index a6b58da3c..20508dbc3 100644 Binary files a/desktop/indi.png and b/desktop/indi.png differ diff --git a/desktop/mount.png b/desktop/mount.png index fb9411b6a..3bb14686f 100644 Binary files a/desktop/mount.png and b/desktop/mount.png differ diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 8f0903c6f..243b1dcf4 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -10,16 +10,16 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@angular/animations": "16.2.9", - "@angular/cdk": "16.2.8", - "@angular/common": "16.2.9", - "@angular/compiler": "16.2.9", - "@angular/core": "16.2.9", - "@angular/forms": "16.2.9", - "@angular/language-service": "16.2.9", - "@angular/platform-browser": "16.2.9", - "@angular/platform-browser-dynamic": "16.2.9", - "@angular/router": "16.2.9", + "@angular/animations": "16.2.10", + "@angular/cdk": "16.2.9", + "@angular/common": "16.2.10", + "@angular/compiler": "16.2.10", + "@angular/core": "16.2.10", + "@angular/forms": "16.2.10", + "@angular/language-service": "16.2.10", + "@angular/platform-browser": "16.2.10", + "@angular/platform-browser-dynamic": "16.2.10", + "@angular/router": "16.2.10", "chart.js": "4.4.0", "chartjs-plugin-zoom": "2.0.1", "interactjs": "1.10.19", @@ -28,7 +28,8 @@ "panzoom": "9.4.3", "primeflex": "3.3.1", "primeicons": "6.0.1", - "primeng": "16.5.0", + "primeng": "16.5.1", + "random": "4.1.0", "rxjs": "7.8.1", "tslib": "2.6.2", "uuid": "9.0.1", @@ -36,13 +37,13 @@ }, "devDependencies": { "@angular-builders/custom-webpack": "16.0.1", - "@angular-devkit/build-angular": "16.2.6", - "@angular/cli": "16.2.6", - "@angular/compiler-cli": "16.2.9", - "@types/leaflet": "1.9.6", - "@types/node": "20.8.5", - "@types/uuid": "9.0.5", - "electron": "27.0.0", + "@angular-devkit/build-angular": "16.2.7", + "@angular/cli": "16.2.7", + "@angular/compiler-cli": "16.2.10", + "@types/leaflet": "1.9.7", + "@types/node": "20.8.7", + "@types/uuid": "9.0.6", + "electron": "27.0.1", "electron-builder": "24.6.4", "electron-debug": "3.2.0", "electron-reloader": "1.2.3", @@ -91,12 +92,12 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1602.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.6.tgz", - "integrity": "sha512-b1NNV3yNg6Rt86ms20bJIroWUI8ihaEwv5k+EoijEXLoMs4eNs5PhqL+QE8rTj+q9pa1gSrWf2blXor2JGwf1g==", + "version": "0.1602.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.7.tgz", + "integrity": "sha512-r6+z4jRE+e9VNeTmJCGz5VI5azRagOqE4SIDqaywz75eHOJ9UPSo9MHy8zFw1eLt1WcvCDqk+Pk9+krh2E+B8Q==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.2.6", + "@angular-devkit/core": "16.2.7", "rxjs": "7.8.1" }, "engines": { @@ -106,15 +107,15 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.6.tgz", - "integrity": "sha512-QdU/q77K1P8CPEEZGxw1QqLcnA9ofboDWS7vcLRBmFmk2zydtLTApbK0P8GNDRbnmROOKkoaLo+xUTDJz9gvPA==", + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.7.tgz", + "integrity": "sha512-OTH4qzXmWXifhvH0iXwPUhElWEU9SUcIZyWYbv2NR5ImAw/GE07vDuBljGRJeSEC9MpFbThwEFbHD8oRWiLUag==", "dev": true, "dependencies": { "@ampproject/remapping": "2.2.1", - "@angular-devkit/architect": "0.1602.6", - "@angular-devkit/build-webpack": "0.1602.6", - "@angular-devkit/core": "16.2.6", + "@angular-devkit/architect": "0.1602.7", + "@angular-devkit/build-webpack": "0.1602.7", + "@angular-devkit/core": "16.2.7", "@babel/core": "7.22.9", "@babel/generator": "7.22.9", "@babel/helper-annotate-as-pure": "7.22.5", @@ -126,7 +127,7 @@ "@babel/runtime": "7.22.6", "@babel/template": "7.22.5", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "16.2.6", + "@ngtools/webpack": "16.2.7", "@vitejs/plugin-basic-ssl": "1.0.1", "ansi-colors": "4.1.3", "autoprefixer": "10.4.14", @@ -329,13 +330,26 @@ } } }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack-merge": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", + "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1602.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.6.tgz", - "integrity": "sha512-BJPR6xdq7gRJ6bVWnZ81xHyH75j7lyLbegCXbvUNaM8TWVBkwWsSdqr2NQ717dNLLn5umg58SFpU/pWMq6CxMQ==", + "version": "0.1602.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.7.tgz", + "integrity": "sha512-3+MV9ehn65XUUMSBBgfg5K2zZs2jhif75ypI+BBUfZDUWeKR5MeGJy0aDHZ+2H94kPkmSD3PrkOuitWdnDjTgA==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1602.6", + "@angular-devkit/architect": "0.1602.7", "rxjs": "7.8.1" }, "engines": { @@ -349,9 +363,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.6.tgz", - "integrity": "sha512-iez/8NYXQT6fqVQLlKmZUIRkFUEZ88ACKbTwD4lBmk0+hXW+bQBxI7JOnE3C4zkcM2YeuTXIYsC5SebTKYiR4Q==", + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.7.tgz", + "integrity": "sha512-XskObYrg7NRdEuHnSVZOM7OeinEL8HzugjmKnawAa+dAbFCCoGsVWjMliA/Q8sb1yfGkyL0WW7DZABZj7EGwWA==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -376,12 +390,12 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.6.tgz", - "integrity": "sha512-PhpRYHCJ3WvZXmng6Qk8TXeQf83jeBMAf7AIzI8h0fgeBocOl97Xf7bZpLg6GymiU+rVn15igQ4Rz9rKAay8bQ==", + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.7.tgz", + "integrity": "sha512-zu3xHwA4w+kXHkyyjGl3i7uSU2/kKLPKuyyixw0WLcKUQCYd7TWmu8OC0qCDa42XkxP9gGL091dJFu56exgneA==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.2.6", + "@angular-devkit/core": "16.2.7", "jsonc-parser": "3.2.0", "magic-string": "0.30.1", "ora": "5.4.1", @@ -394,9 +408,9 @@ } }, "node_modules/@angular/animations": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.2.9.tgz", - "integrity": "sha512-J+nsc2x/ZQuh+YwwTzxXUrV+7SBpJq6DDStfTFkZls9PWGRj9fjqQeRCWrfNLllpxopAEjhFkoyK06oSjcwqAw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.2.10.tgz", + "integrity": "sha512-UudunZoyFWWNpuWkwiBxC3cleLCVJGHIfMgypFwC35YjtiIlRJ0r4nVkc96Rq1xd4mT71Dbk1kQHc8urB8A7aw==", "dependencies": { "tslib": "^2.3.0" }, @@ -404,13 +418,13 @@ "node": "^16.14.0 || >=18.10.0" }, "peerDependencies": { - "@angular/core": "16.2.9" + "@angular/core": "16.2.10" } }, "node_modules/@angular/cdk": { - "version": "16.2.8", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.2.8.tgz", - "integrity": "sha512-DvqxH909mgSSxWbc5xM5xKLjDMPXY3pzzSVAllngvc9KGPFw240WCs3tSpPaVJI50Esbzdu5O0CyTBfu9jUy4g==", + "version": "16.2.9", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.2.9.tgz", + "integrity": "sha512-TrLV68YpddUx3t2rs8W29CPk8YkgNGA8PKHwjB4Xvo1yaEH5XUnsw3MQCh42Ee7FKseaqzFgG85USZXAK0IB0A==", "dependencies": { "tslib": "^2.3.0" }, @@ -424,15 +438,15 @@ } }, "node_modules/@angular/cli": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.6.tgz", - "integrity": "sha512-9poPvUEmlufOAW1Cjk+aA5e2x3mInLtbYYSL/EYviDN2ugmavsSIvxAE/WLnxq6cPWqhNDbHDaqvcmqkcFM3Cw==", + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.7.tgz", + "integrity": "sha512-30yBAYzbrj/WM4tLiX4IU5byw0b5Y5LEzcpjYZglv/RXPrnevGlRXmgCulpt8wIdkd668N7kXEQ23nipuJDXMg==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1602.6", - "@angular-devkit/core": "16.2.6", - "@angular-devkit/schematics": "16.2.6", - "@schematics/angular": "16.2.6", + "@angular-devkit/architect": "0.1602.7", + "@angular-devkit/core": "16.2.7", + "@angular-devkit/schematics": "16.2.7", + "@schematics/angular": "16.2.7", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", "ini": "4.1.1", @@ -458,9 +472,9 @@ } }, "node_modules/@angular/common": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.2.9.tgz", - "integrity": "sha512-5Lh5KsxCkaoBDeSAghKNF5lCi0083ug4X2X7wnafsSd6Z3xt/rDjH9hDOP5SF5IDLtCVjJgHfs3cCLSTjRuNwg==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.2.10.tgz", + "integrity": "sha512-cLth66aboInNcWFjDBRmK30jC5KN10nKDDcv4U/r3TDTBpKOtnmTjNFFr7dmjfUmVhHFy/66piBMfpjZI93Rxg==", "dependencies": { "tslib": "^2.3.0" }, @@ -468,14 +482,14 @@ "node": "^16.14.0 || >=18.10.0" }, "peerDependencies": { - "@angular/core": "16.2.9", + "@angular/core": "16.2.10", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-16.2.9.tgz", - "integrity": "sha512-lh799pnbdvzTVShJHOY1JC6c1pwBsZC4UIgB3Itklo9dskGybQma/gP+lE6RhqM4FblNfaaBXGlCMUuY8HkmEQ==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-16.2.10.tgz", + "integrity": "sha512-ty6SfqkZlV2bLU/SSi3wmxrEFgPrK+WVslCNIr3FlTnCBdqpIbadHN2QB3A1d9XaNc7c4Tq5DQKh34cwMwNbuw==", "dependencies": { "tslib": "^2.3.0" }, @@ -483,7 +497,7 @@ "node": "^16.14.0 || >=18.10.0" }, "peerDependencies": { - "@angular/core": "16.2.9" + "@angular/core": "16.2.10" }, "peerDependenciesMeta": { "@angular/core": { @@ -492,12 +506,12 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-16.2.9.tgz", - "integrity": "sha512-ecH2oOlijJdDqioD9IfgdqJGoRRHI6hAx5rwBxIaYk01ywj13KzvXWPrXbCIupeWtV/XUZUlbwf47nlmL5gxZg==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-16.2.10.tgz", + "integrity": "sha512-swgmtm4R23vQV9nJTXdDEFpOyIw3kz80mdT9qo3VId/2rqenOK253JsFypoqEj/fKzjV9gwXtTbmrMlhVyuyxw==", "dev": true, "dependencies": { - "@babel/core": "7.22.5", + "@babel/core": "7.23.2", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", @@ -515,31 +529,31 @@ "node": "^16.14.0 || >=18.10.0" }, "peerDependencies": { - "@angular/compiler": "16.2.9", + "@angular/compiler": "16.2.10", "typescript": ">=4.9.3 <5.2" } }, "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", - "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", + "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -549,6 +563,12 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -558,10 +578,39 @@ "semver": "bin/semver.js" } }, + "node_modules/@angular/compiler-cli/node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@angular/core": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.9.tgz", - "integrity": "sha512-chvPX29ZBcMDuh7rLIgb0Cru6oJ/0FaqRzfOI3wT4W2F9W1HOlCtipovzmPYaUAmXBWfVP4EBO9TOWnpog0S0w==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.10.tgz", + "integrity": "sha512-0XTsPjNflFhOl2CfNEdGeDOklG2t+m/D3g10Y7hg9dBjC1dURUEqTmM4d6J7JNbBURrP+/iP7uLsn3WRSipGUw==", "dependencies": { "tslib": "^2.3.0" }, @@ -574,9 +623,9 @@ } }, "node_modules/@angular/forms": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-16.2.9.tgz", - "integrity": "sha512-rxlg2iNJNBH/uc7b5YqybfYc8BkLzzPv1d/nMsQUlY0O2UV2zwNRpcIiWbWd7+ZaKjcyPynVe9FsXC8wgWIABw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-16.2.10.tgz", + "integrity": "sha512-TZliEtSWIL1UzY8kjed4QcMawWS8gk/H60KVgzCh83NGE0wd1OGv20Z5OR7O8j07dxB9vaxY7CQz/8eCz5KaNQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -584,24 +633,24 @@ "node": "^16.14.0 || >=18.10.0" }, "peerDependencies": { - "@angular/common": "16.2.9", - "@angular/core": "16.2.9", - "@angular/platform-browser": "16.2.9", + "@angular/common": "16.2.10", + "@angular/core": "16.2.10", + "@angular/platform-browser": "16.2.10", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-16.2.9.tgz", - "integrity": "sha512-yYfe6TRiPZ5cPs8a/PRBjzIULzPwnGWp9b+DuVZXja3wkE1PhckXEH9o8qsHRnzuJFq9cqZbo+CSIaJrLQctVA==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-16.2.10.tgz", + "integrity": "sha512-r3KNXizhZDtj5/L68xnrtgHp5iSYf4NPyWHovoyAWClabsZ64cK38fOzMNCT/otrwqJWlz9ELnW/b/pxR+M9sw==", "engines": { "node": "^16.14.0 || >=18.10.0" } }, "node_modules/@angular/platform-browser": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.9.tgz", - "integrity": "sha512-9Je7+Jmx0AOyRzBBumraVJG3M0R6YbT4c9jTUbLGJCcPxwDI3/u2ZzvW3rBqpmrDaqLxN5f1LcZeTZx287QeqQ==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.10.tgz", + "integrity": "sha512-TOZiK7ji550F8G39Ri255NnK1+2Xlr74RiElJdQct4TzfN0lqNf2KRDFFNwDohkP/78FUzcP4qBxs+Nf8M7OuQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -609,9 +658,9 @@ "node": "^16.14.0 || >=18.10.0" }, "peerDependencies": { - "@angular/animations": "16.2.9", - "@angular/common": "16.2.9", - "@angular/core": "16.2.9" + "@angular/animations": "16.2.10", + "@angular/common": "16.2.10", + "@angular/core": "16.2.10" }, "peerDependenciesMeta": { "@angular/animations": { @@ -620,9 +669,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-16.2.9.tgz", - "integrity": "sha512-ztpo0939vTZ/5CWVSvo41Yl6YPoTZ0If+yTrs7dk1ce0vFgaZXMlc+y5ZwjJIiMM5CvHbhL48Uk+HJNIojP98A==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-16.2.10.tgz", + "integrity": "sha512-YVmhAjOmsp2SWRonv6Mr/qXuKroCiew9asd1IlAZ//wqcml9ZrNAcX3WlDa8ZqdmOplQb0LuvvirfNB/6Is/jg==", "dependencies": { "tslib": "^2.3.0" }, @@ -630,16 +679,16 @@ "node": "^16.14.0 || >=18.10.0" }, "peerDependencies": { - "@angular/common": "16.2.9", - "@angular/compiler": "16.2.9", - "@angular/core": "16.2.9", - "@angular/platform-browser": "16.2.9" + "@angular/common": "16.2.10", + "@angular/compiler": "16.2.10", + "@angular/core": "16.2.10", + "@angular/platform-browser": "16.2.10" } }, "node_modules/@angular/router": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-16.2.9.tgz", - "integrity": "sha512-5vrJNMblTDx3WC3dtaqLddWNtR0P9iwpqffeZL1uobBIwP4hbJx+8Dos3TwxGR4hnopFKahoDQ5nC0NOQslyog==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-16.2.10.tgz", + "integrity": "sha512-ndiq2NkGZ8hTsyL/KK8qsiR3UA0NjOFIn1jtGXOKtHryXZ6vSTtkhtkE4h4+G6/QNTL1IKtocFhOQt/xsc7DUA==", "dependencies": { "tslib": "^2.3.0" }, @@ -647,9 +696,9 @@ "node": "^16.14.0 || >=18.10.0" }, "peerDependencies": { - "@angular/common": "16.2.9", - "@angular/core": "16.2.9", - "@angular/platform-browser": "16.2.9", + "@angular/common": "16.2.10", + "@angular/core": "16.2.10", + "@angular/platform-browser": "16.2.10", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -3350,9 +3399,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -3444,9 +3493,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.6.tgz", - "integrity": "sha512-d8ZlZL6dOtWmHdjG9PTGBkdiJMcsXD2tp6WeFRVvTEuvCI3XvKsUXBvJDE+mZOhzn5pUEYt+1TR5DHjDZbME3w==", + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.7.tgz", + "integrity": "sha512-QnVoYpMNMuV387VgmP/c/ylD9qUIZpN02LMg3rQqz7NDej0jboBZaxqLJ+7jQaCoEIFVGIgL/RR/X1kponxJZg==", "dev": true, "engines": { "node": "^16.14.0 || >=18.10.0", @@ -3643,13 +3692,13 @@ } }, "node_modules/@schematics/angular": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.6.tgz", - "integrity": "sha512-fM09WPqST+nhVGV5Q3fhG7WKo96kgSVMsbz3wGS0DmTn4zge7ZWnrW3VvbxnMapmGoKa9DFPqdqNln4ADcdIMQ==", + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.7.tgz", + "integrity": "sha512-sL+7vmwYPdo29rp99XYlm8gibqcjjOL5LKEleVQlv63SRES3HLMt7DeYivUfizcMENu/1hDtX41ig4Mu1SpNzg==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.2.6", - "@angular-devkit/schematics": "16.2.6", + "@angular-devkit/core": "16.2.7", + "@angular-devkit/schematics": "16.2.7", "jsonc-parser": "3.2.0" }, "engines": { @@ -3822,9 +3871,9 @@ } }, "node_modules/@types/body-parser": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", - "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.4.tgz", + "integrity": "sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==", "dev": true, "dependencies": { "@types/connect": "*", @@ -3832,9 +3881,9 @@ } }, "node_modules/@types/bonjour": { - "version": "3.5.11", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.11.tgz", - "integrity": "sha512-isGhjmBtLIxdHBDl2xGwUzEM8AOyOvWsADWq7rqirdi/ZQoHnLWErHvsThcEzTX8juDRiZtzp2Qkv5bgNh6mAg==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.12.tgz", + "integrity": "sha512-ky0kWSqXVxSqgqJvPIkgFkcn4C8MnRog308Ou8xBBIVo39OmUFy+jqNe0nPwLCDFxUpmT9EvT91YzOJgkDRcFg==", "dev": true, "dependencies": { "@types/node": "*" @@ -3853,18 +3902,18 @@ } }, "node_modules/@types/connect": { - "version": "3.4.36", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", - "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", + "version": "3.4.37", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.37.tgz", + "integrity": "sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.1.tgz", - "integrity": "sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.2.tgz", + "integrity": "sha512-gX2j9x+NzSh4zOhnRPSdPPmTepS4DfxES0AvIFv3jGv5QyeAJf6u6dY5/BAoAJU9Qq1uTvwOku8SSC2GnCRl6Q==", "dev": true, "dependencies": { "@types/express-serve-static-core": "*", @@ -3872,18 +3921,18 @@ } }, "node_modules/@types/debug": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.9.tgz", - "integrity": "sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.10.tgz", + "integrity": "sha512-tOSCru6s732pofZ+sMv9o4o3Zc+Sa8l3bxd/tweTQudFn06vAzb13ZX46Zi6m6EJ+RUbRTHvgQJ1gBtSgkaUYA==", "dev": true, "dependencies": { "@types/ms": "*" } }, "node_modules/@types/eslint": { - "version": "8.44.4", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.4.tgz", - "integrity": "sha512-lOzjyfY/D9QR4hY9oblZ76B90MYTB3RrQ4z2vBIJKj9ROCRqdkYl2gSUx1x1a4IWPjKJZLL4Aw1Zfay7eMnmnA==", + "version": "8.44.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.6.tgz", + "integrity": "sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw==", "dev": true, "dependencies": { "@types/estree": "*", @@ -3891,9 +3940,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.5.tgz", - "integrity": "sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==", + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.6.tgz", + "integrity": "sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -3901,15 +3950,15 @@ } }, "node_modules/@types/estree": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", - "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", + "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==", "dev": true }, "node_modules/@types/express": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.19.tgz", - "integrity": "sha512-UtOfBtzN9OvpZPPbnnYunfjM7XCI4jyk1NvnFhTVz5krYAnW4o5DCoIekvms+8ApqhB4+9wSge1kBijdfTSmfg==", + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.20.tgz", + "integrity": "sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw==", "dev": true, "dependencies": { "@types/body-parser": "*", @@ -3919,9 +3968,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.37", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", - "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", + "version": "4.17.39", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz", + "integrity": "sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ==", "dev": true, "dependencies": { "@types/node": "*", @@ -3940,36 +3989,36 @@ } }, "node_modules/@types/geojson": { - "version": "7946.0.11", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.11.tgz", - "integrity": "sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==", + "version": "7946.0.12", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.12.tgz", + "integrity": "sha512-uK2z1ZHJyC0nQRbuovXFt4mzXDwf27vQeUWNhfKGwRcWW429GOhP8HxUHlM6TLH4bzmlv/HlEjpvJh3JfmGsAA==", "dev": true }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", - "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==", "dev": true }, "node_modules/@types/http-errors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", - "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.3.tgz", + "integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==", "dev": true }, "node_modules/@types/http-proxy": { - "version": "1.17.12", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.12.tgz", - "integrity": "sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==", + "version": "1.17.13", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.13.tgz", + "integrity": "sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "node_modules/@types/keyv": { @@ -3982,39 +4031,39 @@ } }, "node_modules/@types/leaflet": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.6.tgz", - "integrity": "sha512-HakGTK5LBBWegNWsAmTlG55zN1zszYec7aG47/z6SzT90bW2vqjmbqk3YKAbrtveO+G7fSTKTYqVbIwAFnTrbg==", + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.7.tgz", + "integrity": "sha512-FOfKB1ALYUDnXkH7LfTFreWiZr9R7GErqGP+8lYQGWr2GFq5+jy3Ih0M7e9j41cvRN65kLALJ4dc43yZwyl/6g==", "dev": true, "dependencies": { "@types/geojson": "*" } }, "node_modules/@types/mime": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", - "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz", + "integrity": "sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==", "dev": true }, "node_modules/@types/ms": { - "version": "0.7.32", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz", - "integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==", + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.33.tgz", + "integrity": "sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==", "dev": true }, "node_modules/@types/node": { - "version": "20.8.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.5.tgz", - "integrity": "sha512-SPlobFgbidfIeOYlzXiEjSYeIJiOCthv+9tSQVpvk4PAdIIc+2SmjNVzWXk9t0Y7dl73Zdf+OgXKHX9XtkqUpw==", + "version": "20.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", + "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", "dev": true, "dependencies": { "undici-types": "~5.25.1" } }, "node_modules/@types/plist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.3.tgz", - "integrity": "sha512-DXkBoKc7jwUR0p439icInmXXMJNhoImdpOrrgA5/nDFK7LVtcJ9MyQNKhJEKpEztnHGWnNWMWLOIR62By0Ln0A==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.4.tgz", + "integrity": "sha512-pTa9xUFQFM9WJGSWHajYNljD+DbVylE1q9IweK1LBhUYJdJ28YNU8j3KZ4Q1Qw+cSl4+QLLLOVmqNjhhvVO8fA==", "dev": true, "optional": true, "dependencies": { @@ -4023,21 +4072,21 @@ } }, "node_modules/@types/qs": { - "version": "6.9.8", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", - "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", + "version": "6.9.9", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", + "integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==", "dev": true }, "node_modules/@types/range-parser": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", - "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.6.tgz", + "integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==", "dev": true }, "node_modules/@types/responselike": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.1.tgz", - "integrity": "sha512-TiGnitEDxj2X0j+98Eqk5lv/Cij8oHd32bU4D/Yw6AOq7vvTk0gSD2GPj0G/HkvhMoVsdlhYF4yqqlyPBTM6Sg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/4YQT5Kp6HxUDb4yhRkm0bJ7TbjvTddqX7PZ5hz6qV3pxSo72f/6YPRo+Mu2DU307tm9IioO69l7uAwn5XNcFA==", "dev": true, "dependencies": { "@types/node": "*" @@ -4050,9 +4099,9 @@ "dev": true }, "node_modules/@types/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz", - "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==", + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.3.tgz", + "integrity": "sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==", "dev": true, "dependencies": { "@types/mime": "^1", @@ -4060,18 +4109,18 @@ } }, "node_modules/@types/serve-index": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.2.tgz", - "integrity": "sha512-asaEIoc6J+DbBKXtO7p2shWUpKacZOoMBEGBgPG91P8xhO53ohzHWGCs4ScZo5pQMf5ukQzVT9fhX1WzpHihig==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.3.tgz", + "integrity": "sha512-4KG+yMEuvDPRrYq5fyVm/I2uqAJSAwZK9VSa+Zf+zUq9/oxSSvy3kkIqyL+jjStv6UCVi8/Aho0NHtB1Fwosrg==", "dev": true, "dependencies": { "@types/express": "*" } }, "node_modules/@types/serve-static": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz", - "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.4.tgz", + "integrity": "sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw==", "dev": true, "dependencies": { "@types/http-errors": "*", @@ -4080,40 +4129,40 @@ } }, "node_modules/@types/sockjs": { - "version": "0.3.34", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.34.tgz", - "integrity": "sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==", + "version": "0.3.35", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.35.tgz", + "integrity": "sha512-tIF57KB+ZvOBpAQwSaACfEu7htponHXaFzP7RfKYgsOS0NoYnn+9+jzp7bbq4fWerizI3dTB4NfAZoyeQKWJLw==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/uuid": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.5.tgz", - "integrity": "sha512-xfHdwa1FMJ082prjSJpoEI57GZITiQz10r3vEJCHa2khEFQjKy91aWKz6+zybzssCvXUwE1LQWgWVwZ4nYUvHQ==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz", + "integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==", "dev": true }, "node_modules/@types/verror": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.7.tgz", - "integrity": "sha512-4c5F4T0qMSoXq1KHx7WV1FMuD2h0xdaFoJ7HSVWUfQ8w5YbqCwLOA8K7/yy1I+Txuzvm417dnPUaLmqazX1F7g==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.8.tgz", + "integrity": "sha512-YhUhnxRYs/NiVUbIs3F/EzviDP/NZCEAE2Mx5DUqLdldUmphOhFCVh7Kc+7zlYEExM0P8dzfbJi0yRlNb2Bw5g==", "dev": true, "optional": true }, "node_modules/@types/ws": { - "version": "8.5.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.7.tgz", - "integrity": "sha512-6UrLjiDUvn40CMrAubXuIVtj2PEfKDffJS7ychvnPU44j+KVeXmdHHTgqcM/dxLUTHxlXHiFM8Skmb8ozGdTnQ==", + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.8.tgz", + "integrity": "sha512-flUksGIQCnJd6sZ1l5dqCEG/ksaoAg/eUwiLAGTJQcfgvZJKF++Ta4bJA6A5aPSJmsr+xlseHn4KLgVlNnvPTg==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/yauzl": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.1.tgz", - "integrity": "sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw==", + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.2.tgz", + "integrity": "sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==", "dev": true, "optional": true, "dependencies": { @@ -5783,13 +5832,14 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5814,9 +5864,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001549", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz", - "integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==", + "version": "1.0.30001551", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz", + "integrity": "sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==", "dev": true, "funding": [ { @@ -7193,9 +7243,9 @@ } }, "node_modules/electron": { - "version": "27.0.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-27.0.0.tgz", - "integrity": "sha512-mr3Zoy82l8XKK/TgguE5FeNeHZ9KHXIGIpUMjbjZWIREfAv+X2Q3vdX6RG0Pmi1K23AFAxANXQezIHBA2Eypwg==", + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-27.0.1.tgz", + "integrity": "sha512-AjDGgpf2thNxVXoNqEG+0GCUK4upAEa2B+IoM5Yk9YrOLd6uUOEMfGI9rhPtj+jC14iKOvBdefY2uAzcDC0qng==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -7646,15 +7696,15 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.554", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.554.tgz", - "integrity": "sha512-Q0umzPJjfBrrj8unkONTgbKQXzXRrH7sVV7D9ea2yBV3Oaogz991yhbpfvo2LMNkJItmruXTEzVpP9cp7vaIiQ==", + "version": "1.4.561", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.561.tgz", + "integrity": "sha512-eS5t4ulWOBfVHdq9SW2dxEaFarj1lPjvJ8PaYMOjY0DecBaj/t4ARziL2IPpDr4atyWwjLFGQ2vo/VCgQFezVQ==", "dev": true }, "node_modules/electron/node_modules/@types/node": { - "version": "18.18.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.5.tgz", - "integrity": "sha512-4slmbtwV59ZxitY4ixUZdy1uRLf9eSIvBWPQxNjhHYWEtn0FryfKpyS2cvADYXTayWdKEIsJengncrVvkI4I6A==", + "version": "18.18.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.6.tgz", + "integrity": "sha512-wf3Vz+jCmOQ2HV1YUJuCWdL64adYxumkrxtc+H1VUQlnQI04+5HtH+qZCOE21lBE7gIrt+CwX2Wv8Acrw5Ak6w==", "dev": true }, "node_modules/elliptic": { @@ -8438,6 +8488,15 @@ "node": ">=8" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, "node_modules/follow-redirects": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", @@ -12004,9 +12063,9 @@ "dev": true }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12886,9 +12945,9 @@ "integrity": "sha512-KDeO94CbWI4pKsPnYpA1FPjo79EsY9I+M8ywoPBSf9XMXoe/0crjbUK7jcQEDHuc0ZMRIZsxH3TYLv4TUtHmAA==" }, "node_modules/primeng": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/primeng/-/primeng-16.5.0.tgz", - "integrity": "sha512-fgtTJZ76YODexoZctqCEg0on4BG4uCcJy1l4iaCoaHhMFzcgP89U4gdQ5debz0oEF4TekmBLLKvfEzGtF5fEgg==", + "version": "16.5.1", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-16.5.1.tgz", + "integrity": "sha512-D1Eu7HAm4DelAGXAncKw39bVRMzV9N2XOl9gbI5bEcfUhqbwDzSldbN57ULSdTaRcHugUMUolbYFJsJHMc/k1g==", "dependencies": { "tslib": "^2.3.0" }, @@ -13088,6 +13147,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/random": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/random/-/random-4.1.0.tgz", + "integrity": "sha512-6Ajb7XmMSE9EFAMGC3kg9mvE7fGlBip25mYYuSMzw/uUSrmGilvZo2qwX3RnTRjwXkwkS+4swse9otZ92VjAtQ==", + "dependencies": { + "seedrandom": "^3.0.5" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -13887,6 +13957,11 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -14139,6 +14214,21 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-function-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", @@ -16003,12 +16093,13 @@ } }, "node_modules/webpack-merge": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", - "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dev": true, "dependencies": { "clone-deep": "^4.0.1", + "flat": "^5.0.2", "wildcard": "^2.0.0" }, "engines": { @@ -16199,13 +16290,13 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" diff --git a/desktop/package.json b/desktop/package.json index 4143a6a8d..3aadef640 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -30,16 +30,16 @@ "lint": "ng lint" }, "dependencies": { - "@angular/animations": "16.2.9", - "@angular/cdk": "16.2.8", - "@angular/common": "16.2.9", - "@angular/compiler": "16.2.9", - "@angular/core": "16.2.9", - "@angular/forms": "16.2.9", - "@angular/language-service": "16.2.9", - "@angular/platform-browser": "16.2.9", - "@angular/platform-browser-dynamic": "16.2.9", - "@angular/router": "16.2.9", + "@angular/animations": "16.2.10", + "@angular/cdk": "16.2.9", + "@angular/common": "16.2.10", + "@angular/compiler": "16.2.10", + "@angular/core": "16.2.10", + "@angular/forms": "16.2.10", + "@angular/language-service": "16.2.10", + "@angular/platform-browser": "16.2.10", + "@angular/platform-browser-dynamic": "16.2.10", + "@angular/router": "16.2.10", "chart.js": "4.4.0", "chartjs-plugin-zoom": "2.0.1", "interactjs": "1.10.19", @@ -48,7 +48,8 @@ "panzoom": "9.4.3", "primeflex": "3.3.1", "primeicons": "6.0.1", - "primeng": "16.5.0", + "primeng": "16.5.1", + "random": "4.1.0", "rxjs": "7.8.1", "tslib": "2.6.2", "uuid": "9.0.1", @@ -56,13 +57,13 @@ }, "devDependencies": { "@angular-builders/custom-webpack": "16.0.1", - "@angular-devkit/build-angular": "16.2.6", - "@angular/cli": "16.2.6", - "@angular/compiler-cli": "16.2.9", - "@types/leaflet": "1.9.6", - "@types/node": "20.8.5", - "@types/uuid": "9.0.5", - "electron": "27.0.0", + "@angular-devkit/build-angular": "16.2.7", + "@angular/cli": "16.2.7", + "@angular/compiler-cli": "16.2.10", + "@types/leaflet": "1.9.7", + "@types/node": "20.8.7", + "@types/uuid": "9.0.6", + "electron": "27.0.1", "electron-builder": "24.6.4", "electron-debug": "3.2.0", "electron-reloader": "1.2.3", diff --git a/desktop/src/app/alignment/alignment.component.html b/desktop/src/app/alignment/alignment.component.html index 9f2b978bf..d9611b818 100644 --- a/desktop/src/app/alignment/alignment.component.html +++ b/desktop/src/app/alignment/alignment.component.html @@ -86,6 +86,8 @@ (onClick)="darvAltitude()" icon="mdi mdi-arrow-up-down-bold" severity="success" /> + diff --git a/desktop/src/app/alignment/alignment.component.ts b/desktop/src/app/alignment/alignment.component.ts index 340bdffa2..b550a9735 100644 --- a/desktop/src/app/alignment/alignment.component.ts +++ b/desktop/src/app/alignment/alignment.component.ts @@ -49,6 +49,19 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy { } }) + electron.on('GUIDE_OUTPUT_ATTACHED', (_, event: GuideOutput) => { + ngZone.run(() => { + this.guideOutputs.push(event) + }) + }) + + electron.on('GUIDE_OUTPUT_DETACHED', (_, event: GuideOutput) => { + ngZone.run(() => { + const index = this.guideOutputs.findIndex(e => e.name === event.name) + if (index >= 0) this.guideOutputs.splice(index, 1) + }) + }) + electron.on('GUIDE_OUTPUT_UPDATED', (_, event: GuideOutput) => { if (event.name === this.guideOutput?.name) { ngZone.run(() => { @@ -153,7 +166,7 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy { private async darvStart(direction: GuideDirection) { // TODO: Horizonte leste e oeste tem um impacto no "reversed"? const reversed = this.darvHemisphere === 'SOUTHERN' - await this.browserWindow.openCameraImage(this.camera!) + await this.openCameraImage() await this.api.darvStart(this.camera!, this.guideOutput!, this.darvDrift, this.darvInitialPause, direction, reversed) } @@ -169,6 +182,10 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy { this.api.darvStop(this.camera!, this.guideOutput!) } + openCameraImage() { + return this.browserWindow.openCameraImage(this.camera!) + } + private async updateCamera() { if (!this.camera) { return diff --git a/desktop/src/app/app.component.html b/desktop/src/app/app.component.html index 3ce2113b1..e7a9005d3 100644 --- a/desktop/src/app/app.component.html +++ b/desktop/src/app/app.component.html @@ -1,6 +1,9 @@
- {{ title }} + +
{{ title }}
+
{{ subTitle }}
+
{ this.maximizable = e.resizable === 'true' }) + + this.night = document.getElementsByTagName('night')[0] + + const nightMode = this.preference.get('settings.nightMode', false) + this.nightMode(nightMode) + } + + nightMode(enabled: boolean) { + if (enabled) { + this.night.classList.remove('hidden') + this.night.classList.add('block') + } else { + this.night.classList.remove('block') + this.night.classList.add('hidden') + } + + this.preference.set('settings.nightMode', enabled) + + // TODO: MAKE TITLEBAR RED IF NIGHT MODE IS ON + // TODO: NOTIFY ALL WINDOWS PREFERENCE_UPDATED(name, oldValue, newValue) } pin() { diff --git a/desktop/src/app/app.module.ts b/desktop/src/app/app.module.ts index dad16113a..c5904e96f 100644 --- a/desktop/src/app/app.module.ts +++ b/desktop/src/app/app.module.ts @@ -35,6 +35,7 @@ import { OpenStreetMapComponent } from '../shared/components/openstreetmap/opens import { LocationDialog } from '../shared/dialogs/location/location.dialog' import { NoDropdownDirective } from '../shared/directives/no-dropdown.directive' import { StopPropagationDirective } from '../shared/directives/stop-propagation.directive' +import { AnglePipe } from '../shared/pipes/angle.pipe' import { EnumPipe } from '../shared/pipes/enum.pipe' import { EnvPipe } from '../shared/pipes/env.pipe' import { ExposureTimePipe } from '../shared/pipes/exposureTime.pipe' @@ -79,6 +80,7 @@ import { MountComponent } from './mount/mount.component' WinPipe, EnumPipe, ExposureTimePipe, + AnglePipe, StopPropagationDirective, NoDropdownDirective, ], diff --git a/desktop/src/app/atlas/atlas.component.html b/desktop/src/app/atlas/atlas.component.html index adfe9bb91..edffe381a 100644 --- a/desktop/src/app/atlas/atlas.component.html +++ b/desktop/src/app/atlas/atlas.component.html @@ -299,6 +299,9 @@ +
+ +
@@ -484,4 +487,6 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/desktop/src/app/atlas/atlas.component.ts b/desktop/src/app/atlas/atlas.component.ts index 162636b83..fd12caa62 100644 --- a/desktop/src/app/atlas/atlas.component.ts +++ b/desktop/src/app/atlas/atlas.component.ts @@ -1,18 +1,18 @@ import { AfterContentInit, Component, ElementRef, HostListener, OnDestroy, OnInit, ViewChild } from '@angular/core' import { Chart, ChartData, ChartOptions } from 'chart.js' import zoomPlugin from 'chartjs-plugin-zoom' +import { MenuItem } from 'primeng/api' import { UIChart } from 'primeng/chart' import { DialogService } from 'primeng/dynamicdialog' import { ListboxChangeEvent } from 'primeng/listbox' -import { EVERY_MINUTE_CRON_TIME } from '../../shared/constants' +import { EVERY_MINUTE_CRON_TIME, ONE_DECIMAL_PLACE_FORMATTER, TWO_DIGITS_FORMATTER } from '../../shared/constants' import { LocationDialog } from '../../shared/dialogs/location/location.dialog' -import { oneDecimalPlaceFormatter, twoDigitsFormatter } from '../../shared/formatters' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' import { PreferenceService } from '../../shared/services/preference.service' import { - CONSTELLATIONS, Constellation, DeepSkyObject, EMPTY_BODY_POSITION, EMPTY_LOCATION, Location, + Angle, CONSTELLATIONS, Constellation, DeepSkyObject, EMPTY_BODY_POSITION, EMPTY_LOCATION, Location, MinorPlanet, SATELLITE_GROUPS, Satellite, SatelliteGroupType, SkyObjectType, Star, Union } from '../../shared/types' import { AppComponent } from '../app.component' @@ -27,8 +27,8 @@ export interface PlanetItem { export interface SearchFilter { text: string - rightAscension: string - declination: string + rightAscension: Angle + declination: Angle radius: number constellation: Union magnitude: [number, number] @@ -127,24 +127,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { type: 'ALL', } - readonly starTypeOptions: Union[] = [ - 'ALL', - 'ALPHA2_CVN_VARIABLE', 'ASYMPTOTIC_GIANT_BRANCH_STAR', 'BETA_CEP_VARIABLE', - 'BE_STAR', 'BLUE_STRAGGLER', 'BLUE_SUPERGIANT', 'BY_DRA_VARIABLE', - 'CARBON_STAR', 'CEPHEID_VARIABLE', 'CHEMICALLY_PECULIAR_STAR', - 'CLASSICAL_CEPHEID_VARIABLE', 'DELTA_SCT_VARIABLE', 'DOUBLE_OR_MULTIPLE_STAR', - 'ECLIPSING_BINARY', 'ELLIPSOIDAL_VARIABLE', 'EMISSION_LINE_STAR', - 'ERUPTIVE_VARIABLE', 'EVOLVED_SUPERGIANT', 'EXTRA_SOLAR_PLANET', - 'GAMMA_DOR_VARIABLE', 'HERBIG_AE_BE_STAR', 'HIGH_MASS_X_RAY_BINARY', - 'HIGH_PROPER_MOTION_STAR', 'HORIZONTAL_BRANCH_STAR', 'IRREGULAR_VARIABLE', - 'LONG_PERIOD_VARIABLE', 'LOW_MASS_STAR', 'MAIN_SEQUENCE_STAR', 'MIRA_VARIABLE', - 'OH_IR_STAR', 'ORION_VARIABLE', 'POST_AGB_STAR', 'PULSAR', 'PULSATING_VARIABLE', - 'RED_GIANT_BRANCH_STAR', 'RED_SUPERGIANT', 'ROTATING_VARIABLE', - 'RR_LYRAE_VARIABLE', 'RS_CVN_VARIABLE', 'RV_TAURI_VARIABLE', 'R_CRB_VARIABLE', - 'SPECTROSCOPIC_BINARY', 'STAR', 'SX_PHE_VARIABLE', 'SYMBIOTIC_STAR', 'S_STAR', - 'TYPE_II_CEPHEID_VARIABLE', 'T_TAURI_STAR', 'VARIABLE_STAR', 'WHITE_DWARF', - 'WOLF_RAYET', 'X_RAY_BINARY', 'YELLOW_SUPERGIANT', 'YOUNG_STELLAR_OBJECT' - ] + readonly starTypeOptions: Union[] = ['ALL'] dso?: DeepSkyObject dsoItems: DeepSkyObject[] = [] @@ -167,45 +150,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { type: 'ALL', } - readonly dsoTypeOptions: Union[] = [ - 'ALL', - 'ACTIVE_GALAXY_NUCLEUS', 'ALPHA2_CVN_VARIABLE', 'ASSOCIATION_OF_STARS', - 'BETA_CEP_VARIABLE', 'BE_STAR', 'BLACK_HOLE', 'BLAZAR', 'BLUE_COMPACT_GALAXY', - 'BLUE_OBJECT', 'BLUE_SUPERGIANT', 'BL_LAC', 'BRIGHTEST_GALAXY_IN_A_CLUSTER_BCG', - 'BUBBLE', 'CENTIMETRIC_RADIO_SOURCE', 'CLASSICAL_CEPHEID_VARIABLE', - 'CLASSICAL_NOVA', 'CLOUD', 'CLUSTER_OF_GALAXIES', 'CLUSTER_OF_STARS', - 'COMETARY_GLOBULE_PILLAR', 'COMPACT_GROUP_OF_GALAXIES', 'COMPOSITE_OBJECT_BLEND', - 'DARK_CLOUD_NEBULA', 'DELTA_SCT_VARIABLE', 'DENSE_CORE', - 'DOUBLE_OR_MULTIPLE_STAR', 'ECLIPSING_BINARY', 'EMISSION_LINE_GALAXY', - 'EMISSION_LINE_STAR', 'EMISSION_OBJECT', 'EXTRA_SOLAR_PLANET', - 'FAR_IR_SOURCE_30_M', 'GALAXY', 'GALAXY_IN_PAIR_OF_GALAXIES', - 'GALAXY_TOWARDS_A_CLUSTER_OF_GALAXIES', 'GALAXY_TOWARDS_A_GROUP_OF_GALAXIES', - 'GAMMA_RAY_BURST', 'GAMMA_RAY_SOURCE', 'GLOBULAR_CLUSTER', - 'GLOBULE_LOW_MASS_DARK_CLOUD', 'GRAVITATIONALLY_LENSED_IMAGE', - 'GRAVITATIONALLY_LENSED_IMAGE_OF_A_GALAXY', - 'GRAVITATIONAL_LENS_SYSTEM_LENS_IMAGES', 'GRAVITATIONAL_WAVE_EVENT', - 'GROUP_OF_GALAXIES', 'HERBIG_AE_BE_STAR', 'HERBIG_HARO_OBJECT', - 'HIGH_PROPER_MOTION_STAR', 'HIGH_VELOCITY_CLOUD', 'HII_GALAXY', 'HII_REGION', - 'HI_21CM_SOURCE', 'INFRA_RED_SOURCE', 'INTERACTING_GALAXIES', - 'INTERSTELLAR_MEDIUM_OBJECT', 'INTERSTELLAR_SHELL', - 'LINER_TYPE_ACTIVE_GALAXY_NUCLEUS', 'LONG_PERIOD_VARIABLE', - 'LOW_SURFACE_BRIGHTNESS_GALAXY', 'MASER', 'MICRO_LENSING_EVENT', - 'MID_IR_SOURCE_3_TO_30_M', 'MILLIMETRIC_RADIO_SOURCE', 'MIRA_VARIABLE', - 'MOLECULAR_CLOUD', 'MOVING_GROUP', 'NEAR_IR_SOURCE_3_M', 'NEBULA', - 'NOT_AN_OBJECT_ERROR_ARTEFACT', 'OBJECT_OF_UNKNOWN_NATURE', 'OPEN_CLUSTER', - 'ORION_VARIABLE', 'PAIR_OF_GALAXIES', 'PART_OF_A_GALAXY', 'PART_OF_CLOUD', - 'PLANETARY_NEBULA', 'POST_AGB_STAR', 'PROTO_CLUSTER_OF_GALAXIES', 'PULSAR', - 'PULSATING_VARIABLE', 'QUASAR', 'RADIO_BURST', 'RADIO_GALAXY', - 'RADIO_SOURCE', 'RED_GIANT_BRANCH_STAR', 'RED_SUPERGIANT', - 'REFLECTION_NEBULA', 'REGION_DEFINED_IN_THE_SKY', 'RR_LYRAE_VARIABLE', - 'SEYFERT_1_GALAXY', 'SEYFERT_2_GALAXY', 'SEYFERT_GALAXY', - 'SPECTROSCOPIC_BINARY', 'STAR', 'STARBURST_GALAXY', 'STAR_FORMING_REGION', - 'STELLAR_STREAM', 'SUB_MILLIMETRIC_SOURCE', 'SUPERCLUSTER_OF_GALAXIES', - 'SUPERNOVA_REMNANT', 'TRANSIENT_EVENT', 'T_TAURI_STAR', - 'ULTRA_LUMINOUS_X_RAY_SOURCE', 'UNDERDENSE_REGION_OF_THE_UNIVERSE', - 'UV_EMISSION_SOURCE', 'VARIABLE_STAR', 'WHITE_DWARF', 'WOLF_RAYET', - 'X_RAY_SOURCE', 'YELLOW_SUPERGIANT', 'YOUNG_STELLAR_OBJECT' - ] + readonly dsoTypeOptions: Union[] = ['ALL'] readonly constellationOptions: Union[] = ['ALL', ...CONSTELLATIONS] @@ -363,8 +308,8 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { const hours = (context.parsed.x + 12) % 24 const minutes = (hours - Math.trunc(hours)) * 60 - const a = twoDigitsFormatter.format(Math.trunc(hours)) - const b = twoDigitsFormatter.format(minutes) + const a = TWO_DIGITS_FORMATTER.format(Math.trunc(hours)) + const b = TWO_DIGITS_FORMATTER.format(minutes) if (context.datasetIndex <= 8) { return `${a}:${b}` @@ -410,7 +355,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { autoSkip: false, count: 10, callback: (value) => { - return oneDecimalPlaceFormatter.format(value as number) + return ONE_DECIMAL_PLACE_FORMATTER.format(value as number) } }, border: { @@ -440,7 +385,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { const hours = (value as number + 12) % 24 const h = Math.trunc(hours) const m = Math.trunc((hours - h) * 60) - return m === 0 ? `${twoDigitsFormatter.format(h)}` : '' + return m === 0 ? `${TWO_DIGITS_FORMATTER.format(h)}` : '' } }, grid: { @@ -457,6 +402,33 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { 'ONEWEB', 'SCIENCE', 'STARLINK', 'STATIONS', 'VISUAL' ] + readonly ephemerisMenuItems: MenuItem[] = [ + { + icon: 'mdi mdi-magnify', + label: 'Find stars around this object', + command: () => { + this.starFilter.rightAscension = this.bodyPosition.rightAscensionJ2000 + this.starFilter.declination = this.bodyPosition.declinationJ2000 + if (this.starFilter.radius <= 0) this.starFilter.radius = 1 + this.tab = 4 + // this.showStarFilterDialog = true + this.filterStar() + }, + }, + { + icon: 'mdi mdi-magnify', + label: 'Find DSOs around this object', + command: () => { + this.dsoFilter.rightAscension = this.bodyPosition.rightAscensionJ2000 + this.dsoFilter.declination = this.bodyPosition.declinationJ2000 + if (this.dsoFilter.radius <= 0) this.dsoFilter.radius = 1 + this.tab = 5 + // this.showDSOFilterDialog = true + this.filterDSO() + }, + }, + ] + constructor( private app: AppComponent, private api: ApiService, @@ -481,8 +453,11 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { // TODO: Refresh graph and twilight if hours past 12 (noon) } - ngOnInit() { + async ngOnInit() { this.electron.registerCron(EVERY_MINUTE_CRON_TIME) + + this.starTypeOptions.push(... await this.api.starTypes()) + this.dsoTypeOptions.push(... await this.api.dsoTypes()) } async ngAfterContentInit() { @@ -707,7 +682,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { this.dateTime.setMinutes(this.dateTimeMinute) } - this.app.title = `Sky Atlas ・ ${this.location.name}` + this.app.subTitle = this.location.name try { // Sun. diff --git a/desktop/src/app/camera/camera.component.ts b/desktop/src/app/camera/camera.component.ts index b64f99dc0..d3711af0a 100644 --- a/desktop/src/app/camera/camera.component.ts +++ b/desktop/src/app/camera/camera.component.ts @@ -235,7 +235,7 @@ export class CameraComponent implements AfterContentInit, OnDestroy { async cameraChanged() { if (this.camera) { - this.app.title = `Camera ・ ${this.camera.name}` + this.app.subTitle = this.camera.name const camera = await this.api.camera(this.camera.name) Object.assign(this.camera, camera) @@ -244,7 +244,7 @@ export class CameraComponent implements AfterContentInit, OnDestroy { this.update() this.savePreference() } else { - this.app.title = 'Camera' + this.app.subTitle = '' } this.electron.send('CAMERA_CHANGED', this.camera) diff --git a/desktop/src/app/filterwheel/filterwheel.component.ts b/desktop/src/app/filterwheel/filterwheel.component.ts index b09fb4e70..37139cc0e 100644 --- a/desktop/src/app/filterwheel/filterwheel.component.ts +++ b/desktop/src/app/filterwheel/filterwheel.component.ts @@ -66,7 +66,7 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy { async wheelChanged() { if (this.wheel) { - this.app.title = `Filter Wheel ・ ${this.wheel.name}` + this.app.subTitle = this.wheel.name const wheel = await this.api.wheel(this.wheel.name) Object.assign(this.wheel, wheel) @@ -75,7 +75,7 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy { this.update() this.savePreference() } else { - this.app.title = 'Filter Wheel' + this.app.subTitle = '' } this.electron.send('WHEEL_CHANGED', this.wheel) diff --git a/desktop/src/app/focuser/focuser.component.ts b/desktop/src/app/focuser/focuser.component.ts index 3365fb6ca..dc52ed55d 100644 --- a/desktop/src/app/focuser/focuser.component.ts +++ b/desktop/src/app/focuser/focuser.component.ts @@ -60,7 +60,7 @@ export class FocuserComponent implements AfterViewInit, OnDestroy { async focuserChanged() { if (this.focuser) { - this.app.title = `Focuser ・ ${this.focuser.name}` + this.app.subTitle = this.focuser.name const focuser = await this.api.focuser(this.focuser.name) Object.assign(this.focuser, focuser) @@ -69,7 +69,7 @@ export class FocuserComponent implements AfterViewInit, OnDestroy { this.update() this.savePreference() } else { - this.app.title = 'Focuser' + this.app.subTitle = '' } this.electron.send('FOCUSER_CHANGED', this.focuser) diff --git a/desktop/src/app/guider/guider.component.ts b/desktop/src/app/guider/guider.component.ts index ebd3023be..929b4425b 100644 --- a/desktop/src/app/guider/guider.component.ts +++ b/desktop/src/app/guider/guider.component.ts @@ -243,7 +243,7 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { electron.on('GUIDE_OUTPUT_DETACHED', (_, event: GuideOutput) => { ngZone.run(() => { const index = this.guideOutputs.findIndex(e => e.name === event.name) - if (index) this.guideOutputs.splice(index, 1) + if (index >= 0) this.guideOutputs.splice(index, 1) }) }) diff --git a/desktop/src/app/image/image.component.html b/desktop/src/app/image/image.component.html index d84511e59..4c92fec08 100644 --- a/desktop/src/app/image/image.component.html +++ b/desktop/src/app/image/image.component.html @@ -1,6 +1,6 @@
+ class="select-none pixelated" (mousemove)="imageMouseMoved($event)" [class.cursor-crosshair]="mouseCoordinate" /> @@ -68,13 +68,15 @@
- +
- +
@@ -319,4 +321,13 @@
- \ No newline at end of file + + +
+
X: {{ mouseCoordinate.x }}
+
Y: {{ mouseCoordinate.y }}
+
α: {{ mouseCoordinate.alpha }}
+
δ: {{ mouseCoordinate.delta }}
+
l: {{ mouseCoordinate.l }}
+
b: {{ mouseCoordinate.b }}
+
\ No newline at end of file diff --git a/desktop/src/app/image/image.component.scss b/desktop/src/app/image/image.component.scss index e7e030efb..29381c256 100644 --- a/desktop/src/app/image/image.component.scss +++ b/desktop/src/app/image/image.component.scss @@ -23,4 +23,15 @@ white-space: nowrap; transform: translate(-50%, 0%); } +} + +.coordinates { + bottom: 8px; + left: 50%; + padding: 8px; + border-radius: 2px; + background: rgba(0, 0, 0, 0.65); + border: 1px solid rgba(255, 255, 255, 0.15); + width: 260px; + transform: translate(-50%, 0px); } \ No newline at end of file diff --git a/desktop/src/app/image/image.component.ts b/desktop/src/app/image/image.component.ts index b7876f4dd..8e6a93885 100644 --- a/desktop/src/app/image/image.component.ts +++ b/desktop/src/app/image/image.component.ts @@ -11,11 +11,11 @@ import { BrowserWindowService } from '../../shared/services/browser-window.servi import { ElectronService } from '../../shared/services/electron.service' import { PreferenceService } from '../../shared/services/preference.service' import { - AstronomicalObject, - Camera, CameraCaptureEvent, DeepSkyObject, EquatorialCoordinateJ2000, FITSHeaderItem, + Angle, AstronomicalObject, Camera, CameraCaptureEvent, DeepSkyObject, EquatorialCoordinateJ2000, FITSHeaderItem, ImageAnnotation, ImageCalibrated, ImageChannel, ImageInfo, ImageSource, PlateSolverType, SCNRProtectionMethod, SCNR_PROTECTION_METHODS, Star } from '../../shared/types' +import { CoordinateInterpolator, InterpolatedCoordinate } from '../../shared/utils/coordinate-interpolation' import { AppComponent } from '../app.component' export interface ImageParams { @@ -273,6 +273,9 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.pointMountHereMenuItem, ] + mouseCoordinate?: InterpolatedCoordinate & Partial<{ x: number, y: number }> + private mouseCoordinateInterpolation?: CoordinateInterpolator + constructor( private app: AppComponent, private route: ActivatedRoute, @@ -396,11 +399,13 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } if (this.imageParams.title) { - this.app.title = `Image ・ ${this.imageParams.title}` + this.app.subTitle = this.imageParams.title } else if (this.imageParams.camera) { - this.app.title = `Image ・ ${this.imageParams.camera.name}` + this.app.subTitle = this.imageParams.camera.name } else if (this.imageParams.path) { - this.app.title = `Image ・ ${path.basename(this.imageParams.path)}` + this.app.subTitle = path.basename(this.imageParams.path) + } else { + this.app.subTitle = '' } } @@ -431,17 +436,27 @@ export class ImageComponent implements AfterViewInit, OnDestroy { if (this.imageURL) window.URL.revokeObjectURL(this.imageURL) this.imageURL = window.URL.createObjectURL(blob) image.src = this.imageURL + + this.retrieveCoordinateInterpolation() } - imageClicked(event: MouseEvent, menu: boolean) { + imageClicked(event: MouseEvent, contextMenu: boolean) { this.imageMouseX = event.offsetX this.imageMouseY = event.offsetY - if (menu) { + if (contextMenu) { this.menu.show(event) } } + imageMouseMoved(event: MouseEvent) { + if (!this.menu.visible()) { + this.mouseCoordinate = this.mouseCoordinateInterpolation?.interpolateAsText(event.offsetX, event.offsetY, true, true, false) + this.mouseCoordinate!.x = event.offsetX + this.mouseCoordinate!.y = event.offsetY + } + } + async annotateImage() { try { this.annotating = true @@ -480,6 +495,21 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.loadImage() } + private async retrieveCoordinateInterpolation() { + const coordinate = await this.api.coordinateInterpolation(this.imageParams.path!) + + if (coordinate) { + const { ma, md, x0, y0, x1, y1, delta } = coordinate + this.mouseCoordinateInterpolation = new CoordinateInterpolator(ma, md, x0, y0, x1, y1, delta) + this.mouseCoordinate = this.mouseCoordinateInterpolation.interpolateAsText(0, 0) + this.mouseCoordinate.x = 0 + this.mouseCoordinate.y = 0 + } else { + this.mouseCoordinateInterpolation = undefined + this.mouseCoordinate = undefined + } + } + async solveImage() { this.solving = true @@ -502,6 +532,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.pointMountHereMenuItem.disabled = true } finally { this.solving = false + this.retrieveCoordinateInterpolation() } } diff --git a/desktop/src/app/mount/mount.component.ts b/desktop/src/app/mount/mount.component.ts index 1fec2b390..7b1dcd6e5 100644 --- a/desktop/src/app/mount/mount.component.ts +++ b/desktop/src/app/mount/mount.component.ts @@ -188,7 +188,7 @@ export class MountComponent implements AfterContentInit, OnDestroy { async mountChanged() { if (this.mount) { - this.app.title = `Mount ・ ${this.mount!.name}` + this.app.subTitle = this.mount!.name const mount = await this.api.mount(this.mount.name) Object.assign(this.mount, mount) @@ -197,7 +197,7 @@ export class MountComponent implements AfterContentInit, OnDestroy { this.update() this.savePreference() } else { - this.app.title = 'Mount' + this.app.subTitle = '' } this.electron.send('MOUNT_CHANGED', this.mount) diff --git a/desktop/src/index.html b/desktop/src/index.html index fae0e97f3..e75cf5e49 100644 --- a/desktop/src/index.html +++ b/desktop/src/index.html @@ -14,6 +14,7 @@ + \ No newline at end of file diff --git a/desktop/src/shared/constants.ts b/desktop/src/shared/constants.ts index 152499212..73aaec7b9 100644 --- a/desktop/src/shared/constants.ts +++ b/desktop/src/shared/constants.ts @@ -1 +1,5 @@ export const EVERY_MINUTE_CRON_TIME = '0 */1 * * * *' + +export const TWO_DIGITS_FORMATTER = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 2, minimumFractionDigits: 0, maximumFractionDigits: 0 }) +export const THREE_DIGITS_FORMATTER = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 3, minimumFractionDigits: 0, maximumFractionDigits: 0 }) +export const ONE_DECIMAL_PLACE_FORMATTER = new Intl.NumberFormat('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) diff --git a/desktop/src/shared/formatters.ts b/desktop/src/shared/formatters.ts deleted file mode 100644 index f46457d4b..000000000 --- a/desktop/src/shared/formatters.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const twoDigitsFormatter = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 2, minimumFractionDigits: 0, maximumFractionDigits: 0 }) -export const threeDigitsFormatter = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 3, minimumFractionDigits: 0, maximumFractionDigits: 0 }) -export const oneDecimalPlaceFormatter = new Intl.NumberFormat('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) diff --git a/desktop/src/shared/pipes/angle.pipe.ts b/desktop/src/shared/pipes/angle.pipe.ts new file mode 100644 index 000000000..10bcd4fcc --- /dev/null +++ b/desktop/src/shared/pipes/angle.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from '@angular/core' +import { Angle } from '../types' +import { AngleRange, formatAngle, radiansToDegrees } from '../utils/angle' + +@Pipe({ name: 'angle' }) +export class AnglePipe implements PipeTransform { + + transform(value: Angle, radians: boolean, range: AngleRange) { + if (typeof value === 'string') return value + value = radians ? radiansToDegrees(value) : value + value = range === 24 ? value / 15.0 : value + return formatAngle(value, range, range !== 24, 1, true) + } +} diff --git a/desktop/src/shared/pipes/exposureTime.pipe.ts b/desktop/src/shared/pipes/exposureTime.pipe.ts index e07e21cf3..d030cbe68 100644 --- a/desktop/src/shared/pipes/exposureTime.pipe.ts +++ b/desktop/src/shared/pipes/exposureTime.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core' -import { threeDigitsFormatter, twoDigitsFormatter } from '../formatters' +import { THREE_DIGITS_FORMATTER, TWO_DIGITS_FORMATTER } from '../constants' @Pipe({ name: 'exposureTime' }) export class ExposureTimePipe implements PipeTransform { @@ -31,10 +31,10 @@ function formatter(format: Intl.NumberFormat, unit: string) { type UnitFormatter = (value: number) => string -const hourFormatter = formatter(twoDigitsFormatter, 'h') -const minuteFormatter = formatter(twoDigitsFormatter, 'm') -const secondFormatter = formatter(twoDigitsFormatter, 's') -const millisecondFormatter = formatter(threeDigitsFormatter, 'ms') +const hourFormatter = formatter(TWO_DIGITS_FORMATTER, 'h') +const minuteFormatter = formatter(TWO_DIGITS_FORMATTER, 'm') +const secondFormatter = formatter(TWO_DIGITS_FORMATTER, 's') +const millisecondFormatter = formatter(THREE_DIGITS_FORMATTER, 'ms') function format(value: number, factors: [number, number], formatters: [UnitFormatter, UnitFormatter]) { const a = value / factors[0] @@ -51,13 +51,13 @@ function minutes(value: number) { } function seconds(value: number) { - return `${twoDigitsFormatter.format(value / 1000000)}s` + return `${TWO_DIGITS_FORMATTER.format(value / 1000000)}s` } function milliseconds(value: number) { - return `${threeDigitsFormatter.format(value / 1000)}ms` + return `${THREE_DIGITS_FORMATTER.format(value / 1000)}ms` } function microseconds(value: number) { - return `${threeDigitsFormatter.format(value)}µs` + return `${THREE_DIGITS_FORMATTER.format(value)}µs` } diff --git a/desktop/src/shared/services/api.service.ts b/desktop/src/shared/services/api.service.ts index 7772811ad..a3f4d82b0 100644 --- a/desktop/src/shared/services/api.service.ts +++ b/desktop/src/shared/services/api.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core' import moment from 'moment' import { - Angle, BodyPosition, Camera, CameraStartCapture, ComputedLocation, Constellation, DeepSkyObject, Device, + Angle, BodyPosition, Camera, CameraStartCapture, ComputedLocation, Constellation, CoordinateInterpolation, DeepSkyObject, Device, FilterWheel, Focuser, GuideDirection, GuideOutput, GuiderStatus, HipsSurvey, HistoryStep, INDIProperty, INDISendProperty, ImageAnnotation, ImageCalibrated, ImageChannel, ImageInfo, ListeningEventType, Location, MinorPlanet, @@ -396,13 +396,13 @@ export class ApiService { positionOfPlanet(location: Location, code: string, dateTime: Date) { const [date, time] = moment(dateTime).format('YYYY-MM-DD HH:mm').split(' ') const query = this.http.query({ location: location.id, date, time }) - return this.http.get(`sky-atlas/planets/${code}/position?${query}`) + return this.http.get(`sky-atlas/planets/${encodeURIComponent(code)}/position?${query}`) } altitudePointsOfPlanet(location: Location, code: string, dateTime: Date) { const date = moment(dateTime).format('YYYY-MM-DD') const query = this.http.query({ location: location.id, date }) - return this.http.get<[number, number][]>(`sky-atlas/planets/${code}/altitude-points?${query}`) + return this.http.get<[number, number][]>(`sky-atlas/planets/${encodeURIComponent(code)}/altitude-points?${query}`) } positionOfStar(location: Location, star: Star, dateTime: Date) { @@ -427,6 +427,10 @@ export class ApiService { return this.http.get(`sky-atlas/stars?${query}`) } + starTypes() { + return this.http.get(`sky-atlas/stars/types`) + } + positionOfDSO(location: Location, dso: DeepSkyObject, dateTime: Date) { const [date, time] = moment(dateTime).format('YYYY-MM-DD HH:mm').split(' ') const query = this.http.query({ location: location.id, date, time }) @@ -449,6 +453,10 @@ export class ApiService { return this.http.get(`sky-atlas/dsos?${query}`) } + dsoTypes() { + return this.http.get(`sky-atlas/dsos/types`) + } + positionOfSatellite(location: Location, satellite: Satellite, dateTime: Date) { const [date, time] = moment(dateTime).format('YYYY-MM-DD HH:mm').split(' ') const query = this.http.query({ location: location.id, date, time }) @@ -502,6 +510,11 @@ export class ApiService { return this.http.put(`image/save-as?${query}`) } + coordinateInterpolation(path: string) { + const query = this.http.query({ path }) + return this.http.get(`image/coordinate-interpolation?${query}`) + } + // FRAMING frame(rightAscension: Angle, declination: Angle, diff --git a/desktop/src/shared/types.ts b/desktop/src/shared/types.ts index 4b270ac96..57e5b6364 100644 --- a/desktop/src/shared/types.ts +++ b/desktop/src/shared/types.ts @@ -459,6 +459,17 @@ export interface DARVPolarAlignmentGuidePulseElapsed extends DARVPolarAlignmentE direction: GuideDirection } +export interface CoordinateInterpolation { + ma: number[] + md: number[] + x0: number + y0: number + x1: number + y1: number + delta: number + date?: string +} + export enum ExposureTimeUnit { MINUTE = 'm', SECOND = 's', diff --git a/desktop/src/shared/utils/angle.ts b/desktop/src/shared/utils/angle.ts new file mode 100644 index 000000000..ab1581822 --- /dev/null +++ b/desktop/src/shared/utils/angle.ts @@ -0,0 +1,84 @@ +export type AngleRange = 24 | 360 | 0 + +export function radiansToDegrees(rad: number) { + return 57.2957795130823208767981548141051700441964 * rad +} + +export function degreesToRadians(deg: number) { + return 0.0174532925199432957692369076848861272222 * deg +} + +export function formatAngle(angle: number, range: AngleRange, sign: boolean, precision: number = 0, units: boolean = true) { + const d = decimalToSexagesimal(angle) + let dd = d[1] + let mm = d[2] + let ss = d[3] + + ss = roundTo(ss, precision) + + if (ss == 60) { + ss = 0 + + if (++mm == 60) { + mm = 0 + + if (++dd == range) dd = 0 + } + } + + let ff = 0 + + if (precision > 0) { + const si = Math.trunc(ss) + ff = Math.round((ss - si) * Math.pow(10, precision)) + ss = si + } + + const dw = (range >= 100) ? 3 : 2 + let du = ' ' + let mu = ' ' + let su = '' + + if (units) { + if (range == 24) { + du = 'h' + mu = 'm' + su = 's' + } else { + du = '\u00B0' + mu = '\u2032' + su = '\u2033' + } + } + + let result = (sign ? ((d[0] < 0) ? '-' : '+') : '') + zeroPadded(dd, dw) + du + zeroPadded(mm, 2) + mu + zeroPadded(ss, 2) + + if (precision > 0) + result += '.' + zeroPadded(ff, precision) + + if (units) + result += su + + return result +} + +function decimalToSexagesimal(d: number) { + const t1 = Math.abs(d) + const t2 = (t1 - Math.trunc(t1)) * 60 + return [(d < 0) ? -1 : +1, Math.trunc(t1), Math.trunc(t2), (t2 - Math.trunc(t2)) * 60] +} + +function roundTo(x: number, n: number) { + const p = Math.pow(10, n) + return Math.round(p * x) / p +} + +function padded(x: number, n: number, text: string) { + let s = x.toString() + while (s.length < n) s = text + s + return s +} + +function zeroPadded(x: number, n: number) { + return padded(x, n, '0') +} diff --git a/desktop/src/shared/utils/bicubic-interpolation.ts b/desktop/src/shared/utils/bicubic-interpolation.ts new file mode 100644 index 000000000..9419dcacb --- /dev/null +++ b/desktop/src/shared/utils/bicubic-interpolation.ts @@ -0,0 +1,88 @@ +export class BicubicInterpolationBase { + + constructor(protected M: number[], protected cols: number, protected rows: number) { + } + + protected initXY(x: number, y: number) { + const i1 = Math.min(Math.max(0, Math.trunc(y)), this.rows - 1) + const j1 = Math.min(Math.max(0, Math.trunc(x)), this.cols - 1) + + const j0 = j1 - 1 + const i0 = i1 - 1 + const j2 = j1 + 1 + const i2 = i1 + 1 + const j3 = j1 + 2 + const i3 = i1 + 2 + + let fp = i0 * this.cols + j0 + + if (i0 < 0) fp += this.cols + const p0 = this.getRow(fp, j0, j2, j3) + + if (i0 >= 0) fp += this.cols + const p1 = this.getRow(fp, j0, j2, j3) + + if (i2 < this.rows) fp += this.cols + const p2 = this.getRow(fp, j0, j2, j3) + + if (i3 < this.rows) fp += this.cols + const p3 = this.getRow(fp, j0, j2, j3) + + return { i1, j1, p0, p1, p2, p3 } + } + + getRow(fp: number, j0: number, j2: number, j3: number) { + const p = new Array(4) + + if (j0 < 0) ++fp + p[0] = this.M[fp] + + if (j0 >= 0) ++fp + p[1] = this.M[fp] + + if (j2 < this.cols) { + p[2] = this.M[++fp] + if (j3 < this.cols) ++fp + p[3] = this.M[fp] + } else { + p[2] = this.M[fp] + p[3] = this.M[fp - 1] + } + + return p + } +} + +export class BicubicSplineInterpolation extends BicubicInterpolationBase { + + private readonly clamp: number + + constructor(M: number[], cols: number, rows: number, clamp?: number) { + super(M, cols, rows) + this.clamp = (clamp === undefined || clamp < 0 || clamp > 1) ? 0.3 : clamp + } + + interpolate(x: number, y: number) { + const { i1, j1, p0, p1, p2, p3 } = this.initXY(x, y) + const C = this.coefficients(x - j1) + const c = [this.spline(p0, C), this.spline(p1, C), this.spline(p2, C), this.spline(p3, C)] + return this.spline(c, this.coefficients(y - i1)) + } + + private coefficients(dx: number) { + const dx2 = dx * dx + const dx3 = dx2 * dx + const dx1_2 = dx / 2 + const dx2_2 = dx2 / 2 + const dx3_2 = dx3 / 2 + const dx22 = dx2 + dx2 + const dx315 = dx3 + dx3_2 + return [dx2 - dx3_2 - dx1_2, dx315 - dx22 - dx2_2 + 1, dx22 - dx315 + dx1_2, dx3_2 - dx2_2] + } + + private spline(p: number[], C: number[]) { + const f12 = p[1] * C[1] + p[2] * C[2] + const f03 = p[0] * C[0] + p[3] * C[3] + return (-f03 < f12 * this.clamp) ? f12 + f03 : f12 / (C[1] + C[2]) + } +} \ No newline at end of file diff --git a/desktop/src/shared/utils/coordinate-interpolation.ts b/desktop/src/shared/utils/coordinate-interpolation.ts new file mode 100644 index 000000000..bdf85f15a --- /dev/null +++ b/desktop/src/shared/utils/coordinate-interpolation.ts @@ -0,0 +1,109 @@ +import { Angle } from '../types' +import { degreesToRadians, formatAngle } from './angle' +import { BicubicSplineInterpolation } from './bicubic-interpolation' +import { TimeRepresentation, longitudeDegreesConstrained, obliquity, rectangularEquatorialToEcliptic, rectangularEquatorialToGalactic, rectangularToSphericalDegreesConstrained, sphericalToRectangular } from './ephemeris' + +export interface InterpolatedCoordinate { + alpha: T + delta: T + l?: T + b?: T + lambda?: T + beta?: T +} + +// https://cdn.astrobin.com/static/astrobin_apps_platesolving/js/CoordinateInterpolation.1733091e5e90.js + +export class CoordinateInterpolator { + + private readonly Ia: BicubicSplineInterpolation + private readonly Id: BicubicSplineInterpolation + private se = 0.0 + private ce = 0.0 + + constructor(Ma: number[], Md: number[], + private x0: number, private y0: number, private x1: number, private y1: number, + private delta: number, private date?: TimeRepresentation, private precision: number = 1) { + + x0 = Math.min(x0, x1) + x1 = Math.max(x0, x1) + y0 = Math.min(y0, y1) + y1 = Math.max(y0, y1) + + if (this.date) { + const eps = obliquity(this.date) + this.se = Math.sin(eps) + this.ce = Math.cos(eps) + } + + const width = this.x1 - this.x0 + const height = this.y1 - this.y0 + const rows = 1 + Math.trunc(height / this.delta) + ((height % this.delta != 0) ? 1 : 0) + const cols = 1 + Math.trunc(width / this.delta) + ((width % this.delta != 0) ? 1 : 0) + + if (rows < 2 || cols < 2) + throw new Error('CoordinateInterpolation: Insufficient interpolation space.') + + if (Ma.length != rows * cols || Md.length != Ma.length) + throw new Error('CoordinateInterpolation: Invalid matrix dimensions.') + + this.Ia = new BicubicSplineInterpolation(Ma, cols, rows) + this.Id = new BicubicSplineInterpolation(Md, cols, rows) + } + + interpolate(x: number, y: number, withGalactic: boolean = false, withEcliptic: boolean = false) { + const fx = (x - this.x0) / this.delta + const fy = (y - this.y0) / this.delta + const alpha = longitudeDegreesConstrained(this.Ia.interpolate(fx, fy)) + const delta = this.Id.interpolate(fx, fy) + + const coordinate: InterpolatedCoordinate = { + alpha: alpha, + delta: delta, + } + + if (withGalactic || withEcliptic) { + const s = { + lon: degreesToRadians(alpha), + lat: degreesToRadians(delta), + } + + const r = sphericalToRectangular(s) + + if (withGalactic) { + const g = rectangularToSphericalDegreesConstrained(rectangularEquatorialToGalactic(r)) + coordinate.l = g.lon + coordinate.b = g.lat + } + + if (withEcliptic && this.date) { + const e = rectangularToSphericalDegreesConstrained(rectangularEquatorialToEcliptic(r, this.se, this.ce)) + coordinate.lambda = e.lon + coordinate.beta = e.lat + } + } + + return coordinate + } + + interpolateAsText(x: number, y: number, units: boolean = true, withGalactic: boolean = false, withEcliptic: boolean = false) { + const q = this.interpolate(x, y, withGalactic, withEcliptic) + + const coordinate: InterpolatedCoordinate = { + alpha: formatAngle(q.alpha / 15, 24, false, this.precision + 1, units), + delta: formatAngle(q.delta, 0, true, this.precision, units) + } + + if (q.l !== undefined && q.b !== undefined) { + coordinate.l = formatAngle(q.l, 360, false, this.precision, units) + coordinate.b = formatAngle(q.b, 0, true, this.precision, units) + } + + if (q.lambda !== undefined && q.beta !== undefined) { + coordinate.lambda = formatAngle(q.lambda, 360, false, this.precision, units) + coordinate.beta = formatAngle(q.beta, 0, true, this.precision, units) + } + + return coordinate + } +} diff --git a/desktop/src/shared/utils/ephemeris.ts b/desktop/src/shared/utils/ephemeris.ts new file mode 100644 index 000000000..6332d6bc8 --- /dev/null +++ b/desktop/src/shared/utils/ephemeris.ts @@ -0,0 +1,147 @@ +import { degreesToRadians, radiansToDegrees } from './angle' + +export type TimeRepresentation = string | Date + +export interface JulianDate { + jdi: number + jdf: number +} + +export interface RectangularRepresentation { + x: number + y: number + z: number +} + +export interface SphericalRepresentation { + lon: number + lat: number +} + +export function julianDate(t: TimeRepresentation): JulianDate { + if (typeof t === 'string') t = new Date(t) + + let year = t.getUTCFullYear() + let month = t.getUTCMonth() + 1 + const day = t.getUTCDate() + 1 + const dayf = (t.getUTCHours() + (t.getUTCMinutes() + (t.getUTCSeconds() + t.getUTCMilliseconds() / 1000) / 60) / 60) / 24 + + if (month <= 2) { + --year + month += 12 + } + + let jdi = Math.trunc(Math.floor(365.25 * (year + 4716))) + Math.trunc(30.6001 * (month + 1)) + day - 1524 + let jdf = dayf - 0.5 + + if (jdi > 0 && jdf < 0) { + jdf += 1 + --jdi + } else if (jdi < 0 && jdf > 0) { + jdf -= 1 + ++jdi + } + + if (jdi > 2299160 || jdi == 2299160 && jdf >= 0.5) { + const a = Math.trunc(0.01 * year) + jdi += 2 - a + (a >> 2) + } + + return { jdi, jdf } +} + +export function centuriesSinceJ2000(jd: JulianDate) { + return (jd.jdi - 2451545 + jd.jdf) / 36525 +} + +export const DELTA_AT = [[2436934.5, 1.4178180, 37300, 0.001296], +[2437300.5, 1.4228180, 37300, 0.001296], [2437512.5, 1.3728180, 37300, 0.001296], +[2437665.5, 1.8458580, 37665, 0.0011232], [2438334.5, 1.9458580, 37665, 0.0011232], +[2438395.5, 3.2401300, 38761, 0.001296], [2438486.5, 3.3401300, 38761, 0.001296], +[2438639.5, 3.4401300, 38761, 0.001296], [2438761.5, 3.5401300, 38761, 0.001296], +[2438820.5, 3.6401300, 38761, 0.001296], [2438942.5, 3.7401300, 38761, 0.001296], +[2439004.5, 3.8401300, 38761, 0.001296], [2439126.5, 4.3131700, 39126, 0.002592], +[2439887.5, 4.2131700, 39126, 0.002592], [2441317.5, 10.0], [2441499.5, 11.0], +[2441683.5, 12.0], [2442048.5, 13.0], [2442413.5, 14.0], [2442778.5, 15.0], +[2443144.5, 16.0], [2443509.5, 17.0], [2443874.5, 18.0], [2444239.5, 19.0], +[2444786.5, 20.0], [2445151.5, 21.0], [2445516.5, 22.0], [2446247.5, 23.0], +[2447161.5, 24.0], [2447892.5, 25.0], [2448257.5, 26.0], [2448804.5, 27.0], +[2449169.5, 28.0], [2449534.5, 29.0], [2450083.5, 30.0], [2450630.5, 31.0], +[2451179.5, 32.0], [2453736.5, 33.0], [2454832.5, 34.0], [2456109.5, 35.0], +[2457204.5, 36.0], [2457754.5, 37.0]] + +export function deltaAT(jd: JulianDate) { + const t = jd.jdi + jd.jdf + + if (t >= 2436934.5) + for (let i = DELTA_AT.length; --i >= 0;) { + const D = DELTA_AT[i] + + if (t >= D[0]) { + if (t >= 2441317.5) return D[1] + return D[1] + (t - 2400000.5 - D[2]) * D[3] + } + } + + return 0 +} + +export function obliquity(t: TimeRepresentation) { + const jd = julianDate(t) + jd.jdf += (deltaAT(jd) + 32.184) / 86400 + const T = centuriesSinceJ2000(jd) + const T2 = T * T + const T3 = T2 * T + const T4 = T3 * T + const T5 = T4 * T + return degreesToRadians((84381.406 - T * 46.836769 - T2 * 0.0001831 + T3 * 0.00200340 - T4 * 0.000000576 - T5 * 0.0000000434) / 3600) +} + +export function longitudeDegreesConstrained(deg: number) { + return (deg < 0) ? deg + 360 : ((deg < 360) ? deg : deg - 360) +} + +export function sphericalToRectangular(s: SphericalRepresentation): RectangularRepresentation { + let slon = Math.sin(s.lon) + let clon = Math.cos(s.lon) + let slat = Math.sin(s.lat) + let clat = Math.cos(s.lat) + + return { + x: clon * clat, + y: slon * clat, + z: slat + } +} + +export function rectangularToSpherical(r: RectangularRepresentation): SphericalRepresentation { + const m2 = r.x * r.x + r.y * r.y + + return { + lon: (m2 == 0) ? 0 : Math.atan2(r.y, r.x), + lat: (r.z == 0) ? 0 : Math.atan2(r.z, Math.sqrt(m2)) + } +} + +export function rectangularToSphericalDegreesConstrained(r: RectangularRepresentation) { + const s = rectangularToSpherical(r) + s.lon = longitudeDegreesConstrained(radiansToDegrees(s.lon)) + s.lat = radiansToDegrees(s.lat) + return s +} + +export function rectangularEquatorialToEcliptic(r: RectangularRepresentation, se: number, ce: number): RectangularRepresentation { + return { + x: r.x, + y: r.y * ce + r.z * se, + z: r.z * ce - r.y * se + } +} + +export function rectangularEquatorialToGalactic(r: RectangularRepresentation): RectangularRepresentation { + return { + x: +0.494055821648 * r.x - 0.054657353964 * r.y - 0.445679169947 * r.z, + y: -0.872844082054 * r.x - 0.484928636070 * r.y + 0.746511167077 * r.z, + z: -0.867710446378 * r.x - 0.198779490637 * r.y + 0.455593344276 * r.z + } +} diff --git a/desktop/src/styles.scss b/desktop/src/styles.scss index 224432313..5665fabf4 100644 --- a/desktop/src/styles.scss +++ b/desktop/src/styles.scss @@ -148,6 +148,14 @@ i.mdi { image-rendering: pixelated; } +.cursor-crosshair { + cursor: crosshair !important; +} + +.monospaced { + font-family: monospace !important; +} + ::-webkit-scrollbar { width: 6px; } diff --git a/nebulosa-erfa/src/main/kotlin/nebulosa/erfa/Erfa.kt b/nebulosa-erfa/src/main/kotlin/nebulosa/erfa/Erfa.kt index 49242cad7..c1ddffdc2 100644 --- a/nebulosa-erfa/src/main/kotlin/nebulosa/erfa/Erfa.kt +++ b/nebulosa-erfa/src/main/kotlin/nebulosa/erfa/Erfa.kt @@ -3,7 +3,7 @@ package nebulosa.erfa import nebulosa.constants.* -import nebulosa.io.bufferedResource +import nebulosa.io.lazyBufferedResource import nebulosa.io.readDoubleArrayLe import nebulosa.io.readDoubleLe import nebulosa.math.* @@ -142,7 +142,7 @@ fun eraTtTcg(tt1: Double, tt2: Double): DoubleArray { // 787 sets of three coefficients. // amplitude (microseconds), frequency (radians per Julian millennium since J2000.0), phase (radians). -private val FAIRHEAD = bufferedResource("FAIRHEAD.dat") { readDoubleArrayLe(787 * 3) } +private val FAIRHEAD by lazyBufferedResource("FAIRHEAD.dat") { readDoubleArrayLe(787 * 3) } /** * An approximation to TDB-TT, the difference between barycentric @@ -432,6 +432,7 @@ private const val CR = LIGHT_TIME_AU_S / DAYSEC * @param ehpy Earth heliocentric position (au) * @param ehpz Earth heliocentric position (au) */ +@Suppress("UnnecessaryVariable") fun eraApcs( tdb1: Double, tdb2: Double, px: Distance, py: Distance, pz: Distance, @@ -762,8 +763,8 @@ private data class PlanetaryNut( } } -private val XLS = bufferedResource("LUNISOLAR-NUT.dat") { (0 until 678).map { LuniSolarNut.from(this) } } -private val XPL = bufferedResource("PLANETARY-NUT.dat") { (0 until 687).map { PlanetaryNut.from(this) } } +private val XLS by lazyBufferedResource("LUNISOLAR-NUT.dat") { Array(678) { LuniSolarNut.from(this) } } +private val XPL by lazyBufferedResource("PLANETARY-NUT.dat") { Array(687) { PlanetaryNut.from(this) } } /** * Nutation, IAU 2000A model (MHB2000 luni-solar and planetary nutation @@ -792,15 +793,17 @@ fun eraNut00a(tt1: Double, tt2: Double): PairOfAngle { var dp = 0.0 var de = 0.0 + val xls = XLS + // Summation of luni-solar nutation series (in reverse order). - for (i in XLS.indices.reversed()) { - val arg = (XLS[i].nl * el + XLS[i].nlp * elp + XLS[i].nf * f + XLS[i].nd * d + XLS[i].nom * om).mod(TAU) + for (i in xls.indices.reversed()) { + val arg = (xls[i].nl * el + xls[i].nlp * elp + xls[i].nf * f + xls[i].nd * d + xls[i].nom * om).mod(TAU) val sarg = sin(arg) val carg = cos(arg) - dp += (XLS[i].sp + XLS[i].spt * t) * sarg + XLS[i].cp * carg - de += (XLS[i].ce + XLS[i].cet * t) * carg + XLS[i].se * sarg + dp += (xls[i].sp + xls[i].spt * t) * sarg + xls[i].cp * carg + de += (xls[i].ce + xls[i].cet * t) * carg + xls[i].se * sarg } val dpls = dp @@ -836,16 +839,18 @@ fun eraNut00a(tt1: Double, tt2: Double): PairOfAngle { dp = 0.0 de = 0.0 - for (i in XPL.indices.reversed()) { - val arg = (XPL[i].nl * al + XPL[i].nf * af + XPL[i].nd * ad + XPL[i].nom * aom + XPL[i].nme * alme + - XPL[i].nve * alve + XPL[i].nea * alea + XPL[i].nma * alma + XPL[i].nju * alju + - XPL[i].nsa * alsa + XPL[i].nur * alur + XPL[i].nne * alne + XPL[i].npa * apa).mod(TAU) + val xpl = XPL + + for (i in xpl.indices.reversed()) { + val arg = (xpl[i].nl * al + xpl[i].nf * af + xpl[i].nd * ad + xpl[i].nom * aom + xpl[i].nme * alme + + xpl[i].nve * alve + xpl[i].nea * alea + xpl[i].nma * alma + xpl[i].nju * alju + + xpl[i].nsa * alsa + xpl[i].nur * alur + xpl[i].nne * alne + xpl[i].npa * apa).mod(TAU) val sarg = sin(arg) val carg = cos(arg) - dp += XPL[i].sp * sarg + XPL[i].cp * carg - de += XPL[i].se * sarg + XPL[i].ce * carg + dp += xpl[i].sp * sarg + xpl[i].cp * carg + de += xpl[i].se * sarg + xpl[i].ce * carg } val dpp = dp @@ -1196,32 +1201,32 @@ private const val AM23 = -0.397776982902 private const val AM32 = 0.397776982902 private const val AM33 = 0.917482137087 -private val E0X = bufferedResource("E0X.dat") { readDoubleArrayLe(1503) } -private val E0Y = bufferedResource("E0Y.dat") { readDoubleArrayLe(1503) } -private val E0Z = bufferedResource("E0Z.dat") { readDoubleArrayLe(411) } -private val E1X = bufferedResource("E1X.dat") { readDoubleArrayLe(237) } -private val E1Y = bufferedResource("E1Y.dat") { readDoubleArrayLe(240) } -private val E1Z = bufferedResource("E1Z.dat") { readDoubleArrayLe(36) } -private val E2X = bufferedResource("E2X.dat") { readDoubleArrayLe(15) } -private val E2Y = bufferedResource("E2Y.dat") { readDoubleArrayLe(15) } -private val E2Z = bufferedResource("E2Z.dat") { readDoubleArrayLe(9) } - -private val S0X = bufferedResource("S0X.dat") { readDoubleArrayLe(636) } -private val S0Y = bufferedResource("S0Y.dat") { readDoubleArrayLe(639) } -private val S0Z = bufferedResource("S0Z.dat") { readDoubleArrayLe(207) } -private val S1X = bufferedResource("S1X.dat") { readDoubleArrayLe(150) } -private val S1Y = bufferedResource("S1Y.dat") { readDoubleArrayLe(150) } -private val S1Z = bufferedResource("S1Z.dat") { readDoubleArrayLe(42) } -private val S2X = bufferedResource("S2X.dat") { readDoubleArrayLe(27) } -private val S2Y = bufferedResource("S2Y.dat") { readDoubleArrayLe(27) } -private val S2Z = bufferedResource("S2Z.dat") { readDoubleArrayLe(6) } - -private val CE0 = arrayOf(E0X, E0Y, E0Z) -private val CE1 = arrayOf(E1X, E1Y, E1Z) -private val CE2 = arrayOf(E2X, E2Y, E2Z) -private val CS0 = arrayOf(S0X, S0Y, S0Z) -private val CS1 = arrayOf(S1X, S1Y, S1Z) -private val CS2 = arrayOf(S2X, S2Y, S2Z) +private val E0X by lazyBufferedResource("E0X.dat") { readDoubleArrayLe(1503) } +private val E0Y by lazyBufferedResource("E0Y.dat") { readDoubleArrayLe(1503) } +private val E0Z by lazyBufferedResource("E0Z.dat") { readDoubleArrayLe(411) } +private val E1X by lazyBufferedResource("E1X.dat") { readDoubleArrayLe(237) } +private val E1Y by lazyBufferedResource("E1Y.dat") { readDoubleArrayLe(240) } +private val E1Z by lazyBufferedResource("E1Z.dat") { readDoubleArrayLe(36) } +private val E2X by lazyBufferedResource("E2X.dat") { readDoubleArrayLe(15) } +private val E2Y by lazyBufferedResource("E2Y.dat") { readDoubleArrayLe(15) } +private val E2Z by lazyBufferedResource("E2Z.dat") { readDoubleArrayLe(9) } + +private val S0X by lazyBufferedResource("S0X.dat") { readDoubleArrayLe(636) } +private val S0Y by lazyBufferedResource("S0Y.dat") { readDoubleArrayLe(639) } +private val S0Z by lazyBufferedResource("S0Z.dat") { readDoubleArrayLe(207) } +private val S1X by lazyBufferedResource("S1X.dat") { readDoubleArrayLe(150) } +private val S1Y by lazyBufferedResource("S1Y.dat") { readDoubleArrayLe(150) } +private val S1Z by lazyBufferedResource("S1Z.dat") { readDoubleArrayLe(42) } +private val S2X by lazyBufferedResource("S2X.dat") { readDoubleArrayLe(27) } +private val S2Y by lazyBufferedResource("S2Y.dat") { readDoubleArrayLe(27) } +private val S2Z by lazyBufferedResource("S2Z.dat") { readDoubleArrayLe(6) } + +private val CE0 by lazy { arrayOf(E0X, E0Y, E0Z) } +private val CE1 by lazy { arrayOf(E1X, E1Y, E1Z) } +private val CE2 by lazy { arrayOf(E2X, E2Y, E2Z) } +private val CS0 by lazy { arrayOf(S0X, S0Y, S0Z) } +private val CS1 by lazy { arrayOf(S1X, S1Y, S1Z) } +private val CS2 by lazy { arrayOf(S2X, S2Y, S2Z) } /** * Earth position and velocity, heliocentric and barycentric, with @@ -1239,25 +1244,32 @@ fun eraEpv00(tdb1: Double, tdb2: Double): Pair) internal fun parse(lines: Stream): HorizonsEphemeris { var start = false var first = false + var matchingSmallBodies = false + var multipleRecords = false + val multipleRecordItems = ArrayList(1) val headerLine = arrayOfNulls(4) val quantities = arrayListOf() val elements = ArrayList(1441) - // TODO: Handle errors. - for (line in lines) { val trimmedLine = line?.trim() ?: break if (trimmedLine.isEmpty()) continue + if (matchingSmallBodies) { + if (multipleRecords) { + if (trimmedLine.contains("enter record #", true)) { + throw NonUniqueObjectException(multipleRecordItems) + } else if (!trimmedLine.startsWith("-")) { + multipleRecordItems.add(trimmedLine) + } + } else if (trimmedLine.contains("Record #", true)) { + multipleRecords = true + } else if (trimmedLine.contains("No matches found", true)) { + throw NoMatchesFoundException + } + + continue + } + headerLine[0] = headerLine[1] headerLine[1] = headerLine[2] headerLine[2] = headerLine[3] @@ -57,6 +74,11 @@ data class HorizonsEphemeris(private val elements: MutableList) if (!start) { start = trimmedLine.startsWith("\$\$SOE") + + if (!start && trimmedLine.contains("Matching small-bodies", true)) { + matchingSmallBodies = true + } + continue } diff --git a/nebulosa-horizons/src/main/kotlin/nebulosa/horizons/NoMatchesFoundException.kt b/nebulosa-horizons/src/main/kotlin/nebulosa/horizons/NoMatchesFoundException.kt new file mode 100644 index 000000000..28a9a89ef --- /dev/null +++ b/nebulosa-horizons/src/main/kotlin/nebulosa/horizons/NoMatchesFoundException.kt @@ -0,0 +1,3 @@ +package nebulosa.horizons + +data object NoMatchesFoundException : Exception() diff --git a/nebulosa-horizons/src/main/kotlin/nebulosa/horizons/NonUniqueObjectException.kt b/nebulosa-horizons/src/main/kotlin/nebulosa/horizons/NonUniqueObjectException.kt new file mode 100644 index 000000000..531178c2f --- /dev/null +++ b/nebulosa-horizons/src/main/kotlin/nebulosa/horizons/NonUniqueObjectException.kt @@ -0,0 +1,3 @@ +package nebulosa.horizons + +data class NonUniqueObjectException(val recordItems: List) : Exception("${recordItems.size} matches found") diff --git a/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt b/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt index 9866bfe39..5c0b476e2 100644 --- a/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt +++ b/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt @@ -1,3 +1,4 @@ +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldNotBeEmpty @@ -7,27 +8,41 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldStartWith import nebulosa.horizons.HorizonsQuantity import nebulosa.horizons.HorizonsService +import nebulosa.horizons.NoMatchesFoundException +import nebulosa.horizons.NonUniqueObjectException import nebulosa.io.source import nebulosa.math.deg import nebulosa.math.km import nebulosa.math.m import nebulosa.nasa.daf.SourceDaf import nebulosa.nasa.spk.Spk +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor import okio.ByteString.Companion.decodeBase64 import java.time.Duration import java.time.LocalDateTime +import java.util.concurrent.TimeUnit class HorizonsServiceTest : StringSpec() { init { - val service = HorizonsService() + val httpClient = OkHttpClient.Builder() + .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) + .callTimeout(1, TimeUnit.MINUTES) + .connectTimeout(1, TimeUnit.MINUTES) + .readTimeout(1, TimeUnit.MINUTES) + .build() - fun observe(command: String) = service + val service = HorizonsService(httpClient = httpClient) + + fun observe( + command: String, + startDate: LocalDateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0), + ) = service .observer( command, 138.73119026648095.deg, 35.36276754848444.deg, 3776.m, - LocalDateTime.of(2022, 12, 25, 22, 0, 0), - LocalDateTime.of(2022, 12, 25, 23, 0, 0), + startDate, startDate.plusDays(1L), extraPrecision = true, ).execute() .body() @@ -101,5 +116,14 @@ class HorizonsServiceTest : StringSpec() { ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "344.45591" ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "14.43086" } + "cap & nofrag" { + observe("DES=1000041;CAP;NOFRAG", LocalDateTime.now().minusDays(2L)) + } + "non unique object" { + shouldThrow { observe("DES=1000041;") }.recordItems.shouldNotBeEmpty() + } + "no matches found" { + shouldThrow { observe("DES=1;CAP;NOFRAG") } + } } } diff --git a/nebulosa-imaging/src/main/kotlin/nebulosa/imaging/Image.kt b/nebulosa-imaging/src/main/kotlin/nebulosa/imaging/Image.kt index 6f27037f7..c65788b23 100644 --- a/nebulosa-imaging/src/main/kotlin/nebulosa/imaging/Image.kt +++ b/nebulosa-imaging/src/main/kotlin/nebulosa/imaging/Image.kt @@ -37,7 +37,8 @@ class Image( @JvmField val g = buffer.g @JvmField val b = buffer.b - val indices = 0 until width * height + val size = width * height + val indices = 0 until size inline fun indexAt(x: Int, y: Int): Int { return y * stride + x @@ -221,7 +222,6 @@ class Image( val data = ImageData(header) val buffer = FloatArray(buffer.size) - val size = width * height r.copyInto(buffer, 0) diff --git a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/DeviceProtocolHandler.kt b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/DeviceProtocolHandler.kt index 5214c3cfe..e1f18e3f1 100644 --- a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/DeviceProtocolHandler.kt +++ b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/DeviceProtocolHandler.kt @@ -3,6 +3,7 @@ package nebulosa.indi.client.device import nebulosa.indi.client.device.AbstractDevice.Companion.create import nebulosa.indi.client.device.camera.AsiCamera import nebulosa.indi.client.device.camera.CameraDevice +import nebulosa.indi.client.device.camera.SVBonyCamera import nebulosa.indi.client.device.camera.SimCamera import nebulosa.indi.client.device.mount.IoptronV3Mount import nebulosa.indi.client.device.mount.MountDevice @@ -349,6 +350,8 @@ abstract class DeviceProtocolHandler : MessageSender, INDIProtocolParser { @JvmStatic private val CAMERAS = mapOf( "indi_asi_ccd" to AsiCamera::class.java, "indi_asi_single_ccd" to AsiCamera::class.java, + "indi_svbony_ccd" to SVBonyCamera::class.java, + "indi_sv305_ccd" to SVBonyCamera::class.java, // legacy name. "indi_simulator_ccd" to SimCamera::class.java, "indi_simulator_guide" to SimCamera::class.java, ) diff --git a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/AsiCamera.kt b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/AsiCamera.kt index 59e8e6501..344df9e8a 100644 --- a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/AsiCamera.kt +++ b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/AsiCamera.kt @@ -1,11 +1,6 @@ package nebulosa.indi.client.device.camera import nebulosa.indi.client.device.DeviceProtocolHandler -import nebulosa.indi.device.camera.CameraGainChanged -import nebulosa.indi.device.camera.CameraGainMinMaxChanged -import nebulosa.indi.device.camera.CameraOffsetChanged -import nebulosa.indi.device.camera.CameraOffsetMinMaxChanged -import nebulosa.indi.protocol.DefNumberVector import nebulosa.indi.protocol.INDIProtocol import nebulosa.indi.protocol.NumberVector @@ -20,32 +15,10 @@ internal class AsiCamera( when (message.name) { "CCD_CONTROLS" -> { if ("Gain" in message) { - val element = message["Gain"]!! - - if (message is DefNumberVector) { - gainMin = element.min.toInt() - gainMax = element.max.toInt() - - handler.fireOnEventReceived(CameraGainMinMaxChanged(this)) - } - - gain = element.value.toInt() - - handler.fireOnEventReceived(CameraGainChanged(this)) + processGain(message, message["Gain"]!!) } if ("Offset" in message) { - val element = message["Offset"]!! - - if (message is DefNumberVector) { - offsetMin = element.min.toInt() - offsetMax = element.max.toInt() - - handler.fireOnEventReceived(CameraOffsetMinMaxChanged(this)) - } - - offset = element.value.toInt() - - handler.fireOnEventReceived(CameraOffsetChanged(this)) + processOffset(message, message["Offset"]!!) } } } diff --git a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/CameraDevice.kt b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/CameraDevice.kt index 83fea4d28..bdd667fc5 100644 --- a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/CameraDevice.kt +++ b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/CameraDevice.kt @@ -352,6 +352,32 @@ internal open class CameraDevice( } } + protected fun processGain(message: NumberVector<*>, element: NumberElement) { + if (message is DefNumberVector) { + gainMin = element.min.toInt() + gainMax = element.max.toInt() + + handler.fireOnEventReceived(CameraGainMinMaxChanged(this)) + } + + gain = element.value.toInt() + + handler.fireOnEventReceived(CameraGainChanged(this)) + } + + protected fun processOffset(message: NumberVector<*>, element: NumberElement) { + if (message is DefNumberVector) { + offsetMin = element.min.toInt() + offsetMax = element.max.toInt() + + handler.fireOnEventReceived(CameraOffsetMinMaxChanged(this)) + } + + offset = element.value.toInt() + + handler.fireOnEventReceived(CameraOffsetChanged(this)) + } + override fun toString() = "Camera(name=$name, connected=$connected, exposuring=$exposuring," + " hasCoolerControl=$hasCoolerControl, cooler=$cooler," + " hasDewHeater=$hasDewHeater, dewHeater=$dewHeater," + diff --git a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/SVBonyCamera.kt b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/SVBonyCamera.kt new file mode 100644 index 000000000..71df208b5 --- /dev/null +++ b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/SVBonyCamera.kt @@ -0,0 +1,53 @@ +package nebulosa.indi.client.device.camera + +import nebulosa.indi.client.device.DeviceProtocolHandler +import nebulosa.indi.protocol.INDIProtocol +import nebulosa.indi.protocol.NumberVector + +internal class SVBonyCamera( + handler: DeviceProtocolHandler, + name: String, +) : CameraDevice(handler, name) { + + @Volatile private var legacyProperties = false + + override fun handleMessage(message: INDIProtocol) { + when (message) { + is NumberVector<*> -> { + when (message.name) { + "CCD_GAIN" -> { + legacyProperties = true + processGain(message, message["GAIN"]!!) + } + "CCD_OFFSET" -> { + legacyProperties = true + processOffset(message, message["OFFSET"]!!) + } + "CCD_CONTROLS" -> { + if ("Gain" in message) { + legacyProperties = false + processGain(message, message["Gain"]!!) + } + if ("Offset" in message) { + legacyProperties = false + processOffset(message, message["Offset"]!!) + } + } + } + } + else -> Unit + } + + super.handleMessage(message) + } + + override fun gain(value: Int) { + if (legacyProperties) sendNewNumber("CCD_GAIN", "GAIN" to value.toDouble()) + else sendNewNumber("CCD_CONTROLS", "Gain" to value.toDouble()) + } + + override fun offset(value: Int) { + if (legacyProperties) sendNewNumber("CCD_OFFSET", "OFFSET" to value.toDouble()) + else sendNewNumber("CCD_CONTROLS", "Offset" to value.toDouble()) + } +} diff --git a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/SimCamera.kt b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/SimCamera.kt index 6f083b219..b505b8560 100644 --- a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/SimCamera.kt +++ b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/camera/SimCamera.kt @@ -1,9 +1,6 @@ package nebulosa.indi.client.device.camera import nebulosa.indi.client.device.DeviceProtocolHandler -import nebulosa.indi.device.camera.CameraGainChanged -import nebulosa.indi.device.camera.CameraGainMinMaxChanged -import nebulosa.indi.protocol.DefNumberVector import nebulosa.indi.protocol.INDIProtocol import nebulosa.indi.protocol.NumberVector @@ -17,32 +14,10 @@ internal class SimCamera( is NumberVector<*> -> { when (message.name) { "CCD_GAIN" -> { - val element = message["GAIN"]!! - - if (message is DefNumberVector) { - gainMin = element.min.toInt() - gainMax = element.max.toInt() - - handler.fireOnEventReceived(CameraGainMinMaxChanged(this)) - } - - gain = element.value.toInt() - - handler.fireOnEventReceived(CameraGainChanged(this)) + processGain(message, message["GAIN"]!!) } "CCD_OFFSET" -> { - val element = message["OFFSET"]!! - - if (message is DefNumberVector) { - offsetMin = element.min.toInt() - offsetMax = element.max.toInt() - - handler.fireOnEventReceived(CameraGainMinMaxChanged(this)) - } - - offset = element.value.toInt() - - handler.fireOnEventReceived(CameraGainChanged(this)) + processOffset(message, message["OFFSET"]!!) } } } diff --git a/nebulosa-io/src/main/kotlin/nebulosa/io/Io.kt b/nebulosa-io/src/main/kotlin/nebulosa/io/Io.kt index 638601f87..2caf6460a 100644 --- a/nebulosa-io/src/main/kotlin/nebulosa/io/Io.kt +++ b/nebulosa-io/src/main/kotlin/nebulosa/io/Io.kt @@ -1,4 +1,5 @@ @file:JvmName("Io") +@file:Suppress("NOTHING_TO_INLINE") package nebulosa.io @@ -13,80 +14,64 @@ import java.util.* val EMPTY_BYTE_ARRAY = ByteArray(0) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readUnsignedByte() = readByte().toInt() and 0xff -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readShort(order: ByteOrder) = if (order.isBigEndian) readShort() else readShortLe() -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readInt(order: ByteOrder) = if (order.isBigEndian) readInt() else readIntLe() -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readLong(order: ByteOrder) = if (order.isBigEndian) readLong() else readLongLe() -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readFloat(order: ByteOrder) = if (order.isBigEndian) readFloat() else readFloatLe() -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readDouble(order: ByteOrder) = if (order.isBigEndian) readDouble() else readDoubleLe() -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readFloat() = Float.fromBits(readInt()) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readFloatLe() = Float.fromBits(readIntLe()) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readDouble() = Double.fromBits(readLong()) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readDoubleLe() = Double.fromBits(readLongLe()) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readDoubleArray(size: Int) = DoubleArray(size) { readDouble() } -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readDoubleArrayLe(size: Int) = DoubleArray(size) { readDoubleLe() } -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readDoubleArray(size: Int, order: ByteOrder) = DoubleArray(size) { readDouble(order) } -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readAscii() = readString(Charsets.US_ASCII) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readAscii(byteCount: Long) = readString(byteCount, Charsets.US_ASCII) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readLatin1() = readString(Charsets.ISO_8859_1) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSource.readLatin1(byteCount: Long) = readString(byteCount, Charsets.ISO_8859_1) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSink.writeFloat(f: Float) = writeInt(f.toBits()) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSink.writeFloatLe(f: Float) = writeIntLe(f.toBits()) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSink.writeDouble(d: Double) = writeLong(d.toBits()) -@Suppress("NOTHING_TO_INLINE") inline fun BufferedSink.writeDoubleLe(d: Double) = writeLongLe(d.toBits()) -@Suppress("NOTHING_TO_INLINE") -inline fun resource(name: String): InputStream? = Thread.currentThread().contextClassLoader.getResourceAsStream(name) +inline fun ClassLoader.resource(name: String): InputStream? = getResourceAsStream(name) -@Suppress("NOTHING_TO_INLINE") -inline fun resourceUrl(name: String): URL? = Thread.currentThread().contextClassLoader.getResource(name) +inline fun resource(name: String): InputStream? = Thread.currentThread().contextClassLoader.resource(name) + +inline fun ClassLoader.resourceUrl(name: String): URL? = getResource(name) + +inline fun resourceUrl(name: String): URL? = Thread.currentThread().contextClassLoader.resourceUrl(name) -@Suppress("NOTHING_TO_INLINE") inline fun bufferedResource(name: String) = resource(name)?.source()?.buffer() +inline fun lazyBufferedResource(name: String) = lazy { bufferedResource(name) } + inline fun bufferedResource(name: String, block: BufferedSource.() -> R) = bufferedResource(name)!!.use(block) +inline fun lazyBufferedResource(name: String, crossinline block: BufferedSource.() -> R) = lazy { bufferedResource(name, block) } + inline val Buffer.UnsafeCursor.remaining get() = end - start @@ -130,11 +115,8 @@ fun File.seekableSink( timeout: Timeout = Timeout.NONE, ): SeekableSink = RandomAccessFile(this, "rw").sink(timeout) -@Suppress("NOTHING_TO_INLINE") inline fun InputStream.transferAndCloseInput(output: OutputStream) = use { transferTo(output) } -@Suppress("NOTHING_TO_INLINE") inline fun InputStream.transferAndCloseOutput(output: OutputStream) = output.use { transferTo(it) } -@Suppress("NOTHING_TO_INLINE") inline fun InputStream.transferAndClose(output: OutputStream) = use { output.use { transferTo(it) } } diff --git a/nebulosa-json/src/main/kotlin/nebulosa/json/FromJsonDeserializer.kt b/nebulosa-json/src/main/kotlin/nebulosa/json/FromJsonDeserializer.kt new file mode 100644 index 000000000..6b26c2d63 --- /dev/null +++ b/nebulosa-json/src/main/kotlin/nebulosa/json/FromJsonDeserializer.kt @@ -0,0 +1,12 @@ +package nebulosa.json + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.deser.std.StdDeserializer + +data class FromJsonDeserializer(private val deserializer: FromJson) : StdDeserializer(deserializer.type) { + + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): T? { + return deserializer.deserialize(p, ctxt) + } +} diff --git a/nebulosa-json/src/main/kotlin/nebulosa/json/Module.kt b/nebulosa-json/src/main/kotlin/nebulosa/json/Module.kt index 464d7eac5..832b0e712 100644 --- a/nebulosa-json/src/main/kotlin/nebulosa/json/Module.kt +++ b/nebulosa-json/src/main/kotlin/nebulosa/json/Module.kt @@ -1,27 +1,6 @@ package nebulosa.json -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.module.SimpleModule -import com.fasterxml.jackson.databind.ser.std.StdSerializer - -data class ToJsonSerializer(private val serializer: ToJson) : StdSerializer(serializer.type) { - - override fun serialize(value: T?, gen: JsonGenerator, provider: SerializerProvider) { - if (value != null) serializer.serialize(value, gen, provider) - else gen.writeNull() - } -} - -data class FromJsonDeserializer(private val deserializer: FromJson) : StdDeserializer(deserializer.type) { - - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): T? { - return deserializer.deserialize(p, ctxt) - } -} @Suppress("NOTHING_TO_INLINE") inline fun SimpleModule.addSerializer(serializer: ToJson) = apply { diff --git a/nebulosa-json/src/main/kotlin/nebulosa/json/ToJsonSerializer.kt b/nebulosa-json/src/main/kotlin/nebulosa/json/ToJsonSerializer.kt new file mode 100644 index 000000000..cfb573b1e --- /dev/null +++ b/nebulosa-json/src/main/kotlin/nebulosa/json/ToJsonSerializer.kt @@ -0,0 +1,13 @@ +package nebulosa.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.ser.std.StdSerializer + +data class ToJsonSerializer(private val serializer: ToJson) : StdSerializer(serializer.type) { + + override fun serialize(value: T?, gen: JsonGenerator, provider: SerializerProvider) { + if (value != null) serializer.serialize(value, gen, provider) + else gen.writeNull() + } +} diff --git a/nebulosa-sbd/src/main/kotlin/nebulosa/sbd/SmallBody.kt b/nebulosa-sbd/src/main/kotlin/nebulosa/sbd/SmallBody.kt index d3256a453..4e8b46244 100644 --- a/nebulosa-sbd/src/main/kotlin/nebulosa/sbd/SmallBody.kt +++ b/nebulosa-sbd/src/main/kotlin/nebulosa/sbd/SmallBody.kt @@ -33,7 +33,7 @@ data class SmallBody( @field:JsonProperty("pha") val pha: Boolean = false, @field:JsonProperty("neo") val neo: Boolean = false, @field:JsonProperty("kind") val kind: BodyKind = BodyKind.ASTEROID, - @field:JsonProperty("orbitId") @field:JsonAlias("orbit_id") val orbitId: Int = 0, + @field:JsonProperty("orbitId") @field:JsonAlias("orbit_id") val orbitId: String = "", @field:JsonProperty("prefix") val prefix: String? = null, @field:JsonProperty("spkId") @field:JsonAlias("spkid") val spkId: Int = 0, @field:JsonProperty("shortname") val shortname: String = "", diff --git a/nebulosa-simbad/src/main/kotlin/nebulosa/simbad/SimbadCatalogType.kt b/nebulosa-simbad/src/main/kotlin/nebulosa/simbad/SimbadCatalogType.kt index 1576726d1..a1d74faad 100644 --- a/nebulosa-simbad/src/main/kotlin/nebulosa/simbad/SimbadCatalogType.kt +++ b/nebulosa-simbad/src/main/kotlin/nebulosa/simbad/SimbadCatalogType.kt @@ -37,7 +37,16 @@ enum class SimbadCatalogType( provider: MatchResult.() -> String, ) : this(regex.toRegex(), isStar, isDSO, provider) - fun matches(text: String) = regex.matches(text) + fun matches(name: String) = regex.matches(name) - fun match(text: String) = regex.matchEntire(text)?.let { provider(it) } + fun match(name: String) = regex.matchEntire(name)?.let { provider(it) } + + companion object { + + @JvmStatic + fun none(name: String) = entries.none { it.matches(name) } + + @JvmStatic + fun any(name: String) = entries.any { it.matches(name) } + } } diff --git a/nebulosa-simbad/src/main/kotlin/nebulosa/simbad/SimbadSkyCatalog.kt b/nebulosa-simbad/src/main/kotlin/nebulosa/simbad/SimbadSkyCatalog.kt index 2c0cfaf7c..2303ad9a7 100644 --- a/nebulosa-simbad/src/main/kotlin/nebulosa/simbad/SimbadSkyCatalog.kt +++ b/nebulosa-simbad/src/main/kotlin/nebulosa/simbad/SimbadSkyCatalog.kt @@ -24,13 +24,14 @@ open class SimbadSkyCatalog( val builder = QueryBuilder() - val join: Table = LeftJoin(BASIC_TABLE, FLUX_TABLE, arrayOf(OID equal FLUX_TABLE.column("oidref"))) + var join: Table = LeftJoin(BASIC_TABLE, FLUX_TABLE, arrayOf(OID equal FLUX_TABLE.column("oidref"))) + join = LeftJoin(join, IDS_TABLE, arrayOf(OID equal Column("i.oidref"))) builder.add(Distinct) builder.add(Limit(max(1, min(limit, 10000)))) builder.addAll(arrayOf(OID, MAIN_ID, OTYPE, RA, DEC, PM_RA, PM_DEC, PLX, RAD_VEL, REDSHIFT)) builder.addAll(arrayOf(MAG_V, MAG_B, MAG_U, MAG_R, MAG_I, MAG_J, MAG_H, MAG_K)) - builder.addAll(arrayOf(MAJOR_AXIS, MINOR_AXIS, ORIENT, SP_TYPE)) + builder.addAll(arrayOf(MAJOR_AXIS, MINOR_AXIS, ORIENT, SP_TYPE, IDS_TABLE.column("ids"))) builder.addAll(arrayOf(RA.isNotNull, DEC.isNotNull)) builder.add(join) if (radius > 0.0) builder.add(SkyPoint(RA, DEC) contains Circle(rightAscension, declination, radius)) @@ -41,10 +42,21 @@ open class SimbadSkyCatalog( } protected fun search(query: Query) { + val rows = service.query(query).execute().body() + if (rows.isNullOrEmpty()) return val currentTime = UTC.now() - val rows = service.query(query).execute().body() ?: emptyList() + + fun matchName(name: String): String? { + for (type in SimbadCatalogType.entries) { + return type.match(name) ?: continue + } + + return null + } for (row in rows) { + val name = row.getField("ids").split("|").mapNotNull(::matchName).joinToString("|") + if (name.isEmpty()) continue val id = row.getField("oid").toLong() val type = SkyObjectType.parse(row.getField("otype"))!! val rightAscensionJ2000 = row.getField("ra").deg @@ -58,7 +70,6 @@ open class SimbadSkyCatalog( val minorAxis = row.getField("galdim_minaxis").toDoubleOrNull()?.arcmin ?: 0.0 val orientation = row.getField("galdim_angle").toDoubleOrNull()?.deg ?: 0.0 val spType = row.getField("sp_type") ?: "" - val name = row.getField("main_id") var magnitude = row.getField("V").toDoubleOrNull() ?: row.getField("B").toDoubleOrNull() @@ -92,6 +103,7 @@ open class SimbadSkyCatalog( @JvmStatic private val BASIC_TABLE = From("basic").alias("b") @JvmStatic private val FLUX_TABLE = From("allfluxes").alias("f") + @JvmStatic private val IDS_TABLE = From("ids").alias("i") @JvmStatic private val OID = BASIC_TABLE.column("oid") @JvmStatic private val MAIN_ID = BASIC_TABLE.column("main_id") @JvmStatic private val OTYPE = BASIC_TABLE.column("otype") diff --git a/settings.gradle.kts b/settings.gradle.kts index 87d91809d..b11b810ca 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,7 +33,7 @@ dependencyResolutionManagement { library("apache-collections", "org.apache.commons:commons-collections4:4.4") library("oshi", "com.github.oshi:oshi-core:6.4.6") library("timeshape", "net.iakovlev:timeshape:2022g.17") - library("sqlite", "org.xerial:sqlite-jdbc:3.43.0.0") + library("sqlite", "org.xerial:sqlite-jdbc:3.43.2.1") library("flyway", "org.flywaydb:flyway-core:9.22.3") library("jna", "net.java.dev.jna:jna:5.13.0") library("kotest-assertions-core", "io.kotest:kotest-assertions-core:5.7.2")