Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Astrodex

Astrodex is your personal astrophotography logbook - a Pokédex-style catalogue of every deep-sky object you have imaged.


Where to find it

Astrodex tab -> Astrodex sub-tab


Concepts

Items and pictures

Each item represents one target object (a galaxy, nebula, cluster…). An item can have multiple pictures attached - one per session or per processing attempt.

Items are per-user: each account has its own private collection (database tables astrodex_items and astrodex_pictures, one row per item and per picture). Items from different users sharing the same target are merged into a combined view when browsing.

Catalogue integration

When you add an item by name, Astrodex looks up the name in the SkyTonight catalogue alias table (observation/catalogue_aliases.py) to find the canonical identifier. This allows adding “M31”, “Andromeda Galaxy”, “NGC 224”, or “Andromeda” - and they all resolve to the same record, preventing duplicates.

The check duplicate endpoint (GET /api/astrodex/check/<name>) lets the UI warn before adding a name that already exists.

For names the local SkyTonight dataset doesn’t know (mostly stars, e.g. “Vega”, “HIP 91262”), GET /api/astrodex/catalogue-lookup falls back to a SIMBAD TAP query (CDS first, then its CfA Harvard and IDIA mirrors - object_info.SIMBAD_TAP_ENDPOINTS - each backed off for a few minutes after a failure). The resolved name prefers SIMBAD’s NAME ... proper name when the object has one (surfaced as CommonName, e.g. “Vega” rather than the obscure Flamsteed designation “* 3 Lyr”), then a Bayer-style designation (“* alf Lyr”) over a bare Flamsteed number, then catalogue numbers (HD/HIP/SAO/TYC). The full set of resolved alternate names is captured client-side and submitted as external_aliases when the item is created, so the item detail popup’s “Catalogue names” section - otherwise sourced live from the DSO dataset via catalogue_aliases and empty for any star - has something to show (see enrich_item_with_catalogue_aliases()).


Features

Browsing

The Astrodex grid shows all your objects with cover picture thumbnails. Items without pictures use a default placeholder image. Sort by name, type, date added or photo count, ascending or descending. Name and type sorts are numeric-aware, so catalogue identifiers order the way they read (M 3 before M 31 before M 100, not as plain strings).

Constellation filter: Objects are grouped by constellation, from the IAU list of the constellation-enum package (observation/astrodex.get_constellations_list()); abbreviations are expanded to full names by utils/constellation_names.py. You can filter to show only objects in a specific constellation.

Type filter: Filter by object type (Galaxy, Nebula, Cluster…).

Adding objects

Two ways to add an object:

  1. From SkyTonight: Every target in the SkyTonight report tables has an “Add to Astrodex” button. The name, catalogue, and type are pre-filled.
  2. From Plan My Night: A “Add to Astrodex” button is available for each target in the plan.
  3. Manually: Use the “+” button in the Astrodex tab to add any object by name.

Editing objects

The object editor allows updating:

  • Name and catalogue reference
  • Object type and constellation
  • Notes

Managing pictures

  • Upload pictures with drag-and-drop or file picker.
  • On upload, EXIF/XMP/IPTC metadata (GPS position, camera serial numbers…) is stripped without re-encoding the pixels; the file must be a real JPEG, PNG, WebP or GIF, otherwise the upload is rejected with 400. See Privacy & GDPR.
  • Reorder pictures; set one as the main (cover) picture.
  • Delete individual pictures.
  • Images larger than the display size are served at original resolution for download.
  • Rate your own photos 0-5 stars (half-star steps) - lets you see which equipment combination produces your best results (average rating shown on the combination’s card in the Equipment tab).
  • Add/edit picture modal is organized into four sections: File, Date & Location, Equipment, and Photo information.

Re-processing with MyAstroShine

When the MyAstroShine integration is configured and enabled, each of your own photos gets a Send to MyAstroShine action. It opens MyAstroShine in a new tab with an edit session pre-loaded from the photo and its metadata; when you send the result back, a new picture is added to the same object (never a replacement), carrying the source photo’s metadata snapshot plus the immutable enhanced_* provenance fields. The Edit Photo modal of an enhanced picture shows a discreet “Re-processed with MyAstroShine” note with a view settings disclosure.

Catalogue lookup

GET /api/astrodex/catalogue-lookup?name=<name> queries the SkyTonight target catalogue for coordinates, type, magnitude, and angular size for a given object name. This auto-fills metadata when adding a new item.

Constellation list

GET /api/astrodex/constellations returns all constellations represented in the current user’s collection, useful for building the constellation filter dropdown.

Photo Map

Astrodex tab -> Photo Map sub-tab

Shows every geotagged picture (one with latitude/longitude set via a location preset or manually-typed custom coordinates in the Add/Edit Picture modal) on a world map, with marker clustering: bubbles show a photo count and separate into individual pins as you zoom the map in, mirroring the Apple/Google Photos map view. Clicking a bubble opens the existing photo slideshow directly for every photo in that cluster; clicking a single pin opens the slideshow for that one photo.

This does not perform any EXIF GPS extraction - it only visualizes coordinates a user already entered manually. It is powered by a dedicated, lightweight endpoint:

GET /api/astrodex/map returns a flat list of geotagged pictures across all visible users, gated by its own config['astrodex']['map_private'] flag (Configuration -> Astrodex -> “Photo map private”; on by default for new installs, off for installs whose configuration predates the setting) - deliberately independent from the general config['astrodex']['private'] sharing flag used by /api/astrodex. When map_private is off, every shared user’s pictures appear on the map with their real coordinates, even though /api/astrodex itself always strips other users’ exact coordinates for privacy in its own merged view. This app targets small trusted deployments (family/astro-club), so the map keeps its own, separately-labeled switch rather than being tied to the general sharing toggle; since real-world photo locations are often the observer’s home, new installs start with it private.


Catalogue Collection

Astrodex tab -> Catalogue Collection sub-tab

The Pokédex view. Pick a catalogue and see every object in it as a small card, whether you have imaged it or not - captured objects in full colour with your own cover photo, everything else greyed out with a sky-survey preview. Cards are display-only: nothing here adds, edits or deletes anything, it only reflects state already owned by the Astrodex and the SkyTonight catalogue.

What you can browse

Every deep-sky catalogue of the SkyTonight dataset, plus a Solar System pseudo-catalogue:

GroupContents
Solar SystemThe 8 planets, the Moon and the Sun. The Sun is a synthetic entry - SkyTonight tracks it as the source of twilight, never as a target - and the whole group is unrelated to the bodies results table.
Human-scale cataloguesMessier, Caldwell, Famous objects (CommonName), Herschel 400, Pensack 500, Gary Imm, Arp, LBN, Sharpless, Barnard, vdB, Abell PNe
Reference cataloguesAbell galaxy clusters (~2 700), NGC (~13 300), IC (~5 500)

Comets are deliberately absent: they are transient visitors, not a fixed set to complete.

The picker shows a captured count per catalogue (Messier (12/109)), and a progress bar sits above the grid. An object present in several catalogues - M 31 is also NGC 224 - counts as captured in each of them.

Each card shows its catalogue identifier, name, type, constellation, difficulty badge and magnitude.

Card images

SituationImage
Captured, has picturesThe item’s main (cover) picture, full colour
Captured, no pictures yetThe DSS2 preview, full colour
Not captured, deep-sky objectThe DSS2 preview, greyscaled and dimmed
Not captured, solar-system bodyBundled artwork under static/img/bodies/ - planets have no fixed coordinates, so no sky survey can supply their picture

DSS2 previews reuse the existing GET /api/object-image/<filename> proxy, which fetches from CDS hips2fits once (or, when CDS is down, renders it from a mirror of the DSS2 HiPS - see SKYTONIGHT.md) and caches the JPEG in data/cache/object_images/ (trimmed to 200 MiB, oldest first; a trimmed image is fetched again when next asked for). Thumbnails load through an IntersectionObserver as their card nears the viewport, so browsing a 13 000-object catalogue never fires more cold CDS fetches than the objects actually looked at.

Captured detection

An object counts as captured when one of its catalogue identifiers or aliases matches an Astrodex item of the current user. Matching runs through the same _extract_name_candidates() extractor is_item_in_astrodex() uses, so an item saved as "M31 - Andromeda Galaxy" is recognised, and the collection always agrees with the “in Astrodex” badges shown elsewhere in the app.

Filtering, sorting, paging

All three happen server-side (backend/observation/catalogue_collection.py) - the NGC catalogue is far too large to hand to the browser at once. Pages hold 60 cards by default, 120 at most.

  • Filters: free-text search (identifier and name), captured / not captured, difficulty, object type, constellation.
  • Sorts: catalogue identifier (default, ordered naturally so M 9 precedes M 10), name, captured, difficulty, magnitude, constellation, type - each reversible.
  • Objects with no value for the sorted field (no published magnitude, no difficulty) stay grouped at the end in both directions, rather than jumping to the top when the order flips.
  • The type and constellation dropdowns are built over the whole catalogue, not the current page, so they do not shrink as you filter or page. They are re-sorted client-side on the translated label, so the list reads alphabetically in the user’s own language.

Difficulty

Cards carry the same beginner / intermediate / advanced badge SkyTonight and the Astrodex grid use, via the shared createDifficultyBadgeNode().

The label comes from skytonight_calculator.compute_difficulty_score(), called directly on the catalogue record. That scorer derives the label from magnitude and apparent size alone and is explicitly independent of location and sky quality, so it can be computed for the whole catalogue rather than only for the targets a given night’s run happened to evaluate - unlike GET /api/astrodex, which cross-references dso_results.json and therefore leaves items unrated when they were not observable. Solar-system bodies have neither magnitude nor size on record, so they get no badge instead of the scorer’s neutral “intermediate” default, which would be a guess.

The difficulty/difficulty_score fields stored in targets.json are not used here: the offline dataset builder writes a placeholder (0 / intermediate) for every object.

Naming and translation

  • Constellations are stored in the dataset as IAU abbreviations (And, plus PyOngc’s Se1/Se2 for the two halves of Serpens). The endpoint expands them to full names (Andromeda, Serpens Caput) so the frontend’s existing constellations.* keys resolve; all 91 names in the dataset have a translation.
  • Object types go through skytonight.type_*, as everywhere else.
  • Object names are catalogue proper nouns and are shown as-is - SkyTonight does not translate them either. The one exception is the Solar System group, whose members have real translations and so go through the shared planets.* dictionary (Moon -> Lune). planets.sun was added for this view; the namespace previously only covered planets, since its only other consumer is the events aggregator.

Shared items

When multiple users on the same instance image the same object, items are merged in the Astrodex view:

  • Each user’s pictures are listed under their own section.
  • The owner_username field shows who added which picture.
  • Users can only edit or delete their own items and pictures.
  • The is_owned_by_current_user flag drives UI permissions.

Item data model

Each item of a user’s Astrodex has the following fields:

FieldTypeDescription
idUUID stringUnique item identifier
namestringDisplay name (e.g. “M31 - Andromeda Galaxy”)
typestringObject type: Galaxy, Nebula, Cluster, Supernova Remnant, Unknown, …
cataloguestringSource catalogue: NGC, IC, Messier, Sharpless, Barnard, vdB, …
constellationstringConstellation abbreviation (e.g. And, Ori)
notesstringFree-text notes, processing ideas, next steps
picturesarrayList of picture objects (see below)
location_idUUID string or nullv1.2: the location preset active when the item was created - best-effort link, only trusted while the preset still exists
location_namestring or nullv1.2: frozen plain-text snapshot of the location name at creation. The UI always trusts this field for display; it survives preset renames and deletions (Astrodex items are never deleted with a location - see LOCATIONS.md)
external_aliasesobject or absent{catalogue_key: name} alternate names captured at add-time for objects the local DSO dataset doesn’t cover (mainly stars resolved via the SIMBAD fallback, e.g. {"CommonName": "Vega", "HD": "HD 172167", "HIP": "HIP 91262"}). Used as a fallback source for the “Catalogue names” popup section - see Catalogue integration above
created_atISO 8601When the item was added to the collection
updated_atISO 8601Last modification time

Additionally, catalogue_aliases is attached to every item at read time only (never persisted) - either from the live SkyTonight DSO catalogue lookup, or, when that finds nothing, from the item’s own external_aliases. It powers the item popup’s “Catalogue names” section and the “use this name” switch action.

Picture data model

Each picture attached to an item:

FieldTypeDescription
idUUID stringUnique picture identifier
filenamestringStored filename under data/astrodex/images/
is_mainbooleanWhether this is the primary/cover picture
captionstringOptional caption or session note
owner_user_idUUIDThe user who uploaded this picture
created_atISO 8601Upload timestamp
combination_idUUID string or nullLinked equipment combination, resolved and access-checked server-side. Mutually exclusive with the free-text device/filters fields (“Other equipment” path) - never both.
combination_used_componentsobject or nullFrozen snapshot of which parts of the combination were actually used for this photo (e.g. {"telescope": true, "camera": true, "filter_ids": [...]}) - purely informational, set once at save time and never recomputed even if the combination is edited later.
ratingnumber or nullUser’s own 0.0-5.0 rating in 0.5 steps, or null if not yet rated. Feeds a combination’s average-rating badge in the Equipment tab (see EQUIPMENT.md).
exposition_timeinteger (seconds) or nullSub-exposure length. v1.3+: a validated whole number of seconds, entered through a dedicated number input - a picture created before v1.3 may still carry a free-text legacy value (e.g. "1h (10sec)"), shown as-is but never reinterpreted or accepted back from the edit form.
framesinteger or nullNumber of sub-exposures. Validated server-side (v1.3+).
integration_minutesnumber or nullTotal integration time in minutes (v1.3+). Any two of frames/exposition_time/integration_minutes determine the third - the Add/Edit Picture modal computes the one you leave blank, mirroring the Observation Log entry model’s own frame_count/sub_exposure_seconds/integration_minutes trio.
enhanced_bystring or absent"myastroshine" when this picture was produced by re-processing another one through MyAstroShine. Absent otherwise.
enhanced_atISO 8601 or absentWhen the MyAstroShine round-trip completed.
enhanced_from_picture_idUUID or absentThe source picture this one was derived from.
enhanced_parametersobject or absentThe MyAstroShine parameter dict, verbatim. Shown (pretty-printed) behind a disclosure in the Edit Photo modal.
enhanced_source_versionstring or absentMyAstroShine version that produced it.

The five enhanced_* fields are immutable - set once when the duplicate is created and absent from update_picture()’s allowed fields.

Images are stored in data/astrodex/images/ and served by GET /api/astrodex/images/<filename>.

Equipment on a picture

The Add/Edit Picture modal’s Equipment section is a single select: your enabled combinations (own + shared) plus an “Other equipment” option.

  • Picking a combination saves combination_id and reveals a checklist of that combination’s actual components (telescope / camera / guide camera / each filter) so you can mark which ones were used for this specific photo - purely optional, stored as combination_used_components.
  • Picking “Other equipment” falls back to free-text device/filters fields (with autocomplete drawn from your equipment names and previously-typed values).
  • If a picture’s linked combination is later disabled, it still appears in that picture’s edit dropdown (and stays selected) - the same “stays visible if already selected” rule the Equipment tab itself uses.
  • A combination can’t be deleted while any picture (yours or another user’s) still references it.

Storage and backup

  • Per-user collection: database tables astrodex_items and astrodex_pictures (one row per item and per picture; astrodex/<user_id>_astrodex.json inside a backup ZIP)
  • Images directory: data/astrodex/images/
  • Both are included in the Backup / Restore ZIP (see CONFIGURATION.md).

Write safety

save_user_astrodex() uses an atomic write pattern:

  1. Write to a temporary file (.json.tmp)
  2. Validate the temporary file’s JSON
  3. os.replace() - atomic rename on POSIX filesystems
  4. Delete the backup on success; restore it on failure

Per-user write locks (threading.Lock per user_id) prevent concurrent save race conditions across Gunicorn workers.


API endpoints

MethodPathAuthDescription
GET/api/astrodexloginList all items (merged across users sharing same objects)
GET/api/astrodex/maploginFlat list of geotagged pictures for the Photo Map sub-tab, gated by its own map_private flag (never strips coordinates when off)
POST/api/astrodex/itemsloginCreate a new item
GET/api/astrodex/items/<item_id>loginGet a single item
PUT/api/astrodex/items/<item_id>loginUpdate item fields
DELETE/api/astrodex/items/<item_id>loginDelete item and its pictures
POST/api/astrodex/items/<item_id>/picturesloginUpload a picture
PUT/api/astrodex/items/<item_id>/pictures/<picture_id>loginUpdate picture caption
DELETE/api/astrodex/items/<item_id>/pictures/<picture_id>loginDelete a picture
POST/api/astrodex/items/<item_id>/pictures/<picture_id>/mainloginSet picture as main
GET/api/astrodex/images/<filename>loginServe a picture file
GET/api/astrodex/check/<item_name>loginCheck if name already exists
GET/api/astrodex/collection/cataloguesloginList browsable catalogues for the Catalogue Collection sub-tab, with per-catalogue object and captured counts
GET/api/astrodex/collectionloginOne filtered/sorted/paginated page of a catalogue’s collection cards
GET/api/astrodex/constellationsloginList constellations in collection
GET/api/astrodex/catalogue-lookuploginLook up object in SkyTonight catalogue
POST/api/astrodex/items/<item_id>/catalogue-nameloginSet canonical catalogue name
POST/api/astrodex/uploadloginUpload image (alternative endpoint)

Last updated 2026-11-13