skymap 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +133 -1
  3. data/README.md +570 -31
  4. data/data/LICENSE-deep_sky_objects.txt +30 -0
  5. data/data/LICENSE-stars.txt +35 -0
  6. data/data/deep_sky_objects.csv +1742 -0
  7. data/data/stars.bin +0 -0
  8. data/lib/skymap/canvas.rb +12 -3
  9. data/lib/skymap/catalog/deep_sky_objects.rb +60 -0
  10. data/lib/skymap/catalog/star_records.rb +322 -0
  11. data/lib/skymap/catalog/stars.rb +75 -12
  12. data/lib/skymap/chart.rb +18 -4
  13. data/lib/skymap/cone.rb +21 -0
  14. data/lib/skymap/context.rb +326 -8
  15. data/lib/skymap/deep_sky_object.rb +140 -0
  16. data/lib/skymap/eyepiece.rb +22 -0
  17. data/lib/skymap/field.rb +20 -0
  18. data/lib/skymap/geometry.rb +47 -0
  19. data/lib/skymap/horizon.rb +33 -3
  20. data/lib/skymap/label_placement.rb +263 -0
  21. data/lib/skymap/layers/cardinal_directions.rb +2 -0
  22. data/lib/skymap/layers/constellation_lines.rb +6 -1
  23. data/lib/skymap/layers/constellation_names.rb +5 -2
  24. data/lib/skymap/layers/deep_sky_objects.rb +128 -0
  25. data/lib/skymap/layers/ecliptic.rb +1 -7
  26. data/lib/skymap/layers/field_circles.rb +169 -0
  27. data/lib/skymap/layers/grid.rb +153 -0
  28. data/lib/skymap/layers/moon.rb +22 -12
  29. data/lib/skymap/layers/planets.rb +4 -0
  30. data/lib/skymap/layers/scale.rb +75 -0
  31. data/lib/skymap/layers/star_names.rb +21 -4
  32. data/lib/skymap/layers/stars.rb +31 -8
  33. data/lib/skymap/layers/sun.rb +13 -9
  34. data/lib/skymap/magnitude_limit.rb +15 -0
  35. data/lib/skymap/print_size.rb +50 -0
  36. data/lib/skymap/projection/oblique_stereographic.rb +87 -0
  37. data/lib/skymap/projection/stereographic.rb +11 -0
  38. data/lib/skymap/renderer/caption.rb +35 -3
  39. data/lib/skymap/renderer/deep_sky_symbol.rb +67 -0
  40. data/lib/skymap/renderer/field_circle.rb +7 -0
  41. data/lib/skymap/renderer/grid.rb +7 -0
  42. data/lib/skymap/renderer/horizon_line.rb +7 -0
  43. data/lib/skymap/renderer/label.rb +3 -1
  44. data/lib/skymap/renderer/scale_bar.rb +7 -0
  45. data/lib/skymap/renderer/svg.rb +204 -15
  46. data/lib/skymap/setup.rb +48 -0
  47. data/lib/skymap/star.rb +127 -2
  48. data/lib/skymap/star_list.rb +25 -0
  49. data/lib/skymap/telescope.rb +17 -0
  50. data/lib/skymap/theme.rb +54 -20
  51. data/lib/skymap/version.rb +1 -1
  52. data/lib/skymap/view.rb +49 -0
  53. data/lib/skymap.rb +24 -0
  54. metadata +31 -2
  55. data/data/stars.csv +0 -9097
data/README.md CHANGED
@@ -48,7 +48,7 @@ observer = Astronoby::Observer.new(
48
48
  )
49
49
  instant = Astronoby::Instant.from_time(Time.utc(2026, 9, 23, 20))
50
50
  canvas = Skymap::Canvas.new(size: 800, padding: 24)
51
- stars = Skymap::Catalog::Stars.new.to_a
51
+ stars = Skymap::Catalog::Stars.new
52
52
  lines = Skymap::Catalog::ConstellationLines.new.to_a
53
53
 
54
54
  chart = Skymap::Chart.new(observer: observer, instant: instant, canvas: canvas)
@@ -87,6 +87,145 @@ The canvas is the square SVG the map is drawn on. `size` is its width and
87
87
  height, and `padding` is the space between the horizon and the edge of the
88
88
  SVG.
89
89
 
90
+ #### Print size
91
+
92
+ The SVG is written in pixels by default. To print it at a known size, give the
93
+ canvas a `print_size:`: the length of the side of the square, in millimeters
94
+ (`:mm`), centimeters (`:cm`) or inches (`:in`):
95
+
96
+ ```ruby
97
+ canvas = Skymap::Canvas.new(
98
+ size: 800,
99
+ padding: 24,
100
+ print_size: Skymap::PrintSize.new(length: 190, unit: :mm)
101
+ )
102
+ ```
103
+
104
+ The SVG gets `width="190mm"` and `height="190mm"`, and its `viewBox` stays in
105
+ the units of the canvas, so the map is the same and a browser or a print dialog
106
+ prints it at 190 mm.
107
+
108
+ `Skymap::PrintSize.paper` gives the largest square that fits in the width of a
109
+ sheet of paper, minus a margin of 10 mm on each side by default (`margin:`, in
110
+ millimeters). The papers are `:a5`, `:a4`, `:a3`, `:letter` and `:tabloid`:
111
+
112
+ ```ruby
113
+ Skymap::PrintSize.paper(:a4) # 190 mm
114
+ Skymap::PrintSize.paper(:letter, margin: 5) # 205.9 mm
115
+ ```
116
+
117
+ Add a [scale](#scale) to the map to read angles on the printed chart.
118
+
119
+ ### View
120
+
121
+ By default, the map is the whole sky. Pass a `view:` to zoom on an area of it:
122
+ the center of the area, as the equatorial coordinates of a point of the sky, at
123
+ J2000 or at the date of the chart (or horizontal coordinates), and the field of
124
+ view, the angle between the opposite sides of the map:
125
+
126
+ ```ruby
127
+ view = Skymap::View.new(
128
+ center: Astronoby::Coordinates::Equatorial.new(
129
+ right_ascension: Astronoby::Angle.from_hours(5.6),
130
+ declination: Astronoby::Angle.zero,
131
+ epoch: Astronoby::JulianDate::J2000
132
+ ),
133
+ field_of_view: Astronoby::Angle.from_degrees(50)
134
+ )
135
+
136
+ chart = Skymap::Chart.new(
137
+ observer: observer,
138
+ instant: instant,
139
+ canvas: canvas,
140
+ view: view
141
+ )
142
+ ```
143
+
144
+ Equatorial coordinates at any other epoch raise an `ArgumentError`. With
145
+ horizontal coordinates, only the altitude and the azimuth are used, as seen by
146
+ the observer of the chart. The field of view is an `Astronoby::Angle` of more
147
+ than 0 and up to 180 degrees.
148
+
149
+ The sky is still seen from the observer. By default, the zenith is at the top of
150
+ the map, except when the zenith is in the view: the north is at the top then, as
151
+ in the whole sky. The next section changes that. The layers draw what is in the
152
+ view only. The horizon is drawn as a line when it is in the view, and the edge
153
+ of the map is a thin outline. It is no longer the horizon, so the cardinal
154
+ directions are not drawn. The Sun and the Moon are drawn at their size in the
155
+ sky once it is larger than their usual size on the map. The stars come from a
156
+ catalog that goes down to magnitude 9: draw them with `magnitude_limit: 9` in a
157
+ zoomed view to fill it.
158
+
159
+ #### Orientation
160
+
161
+ A map in a telescope is turned in a way that depends on the telescope and on
162
+ its mount. Three options of `View` give the map the same orientation:
163
+
164
+ - `orientation: :zenith_up` (the default) keeps the zenith at the top, like an
165
+ alt-az mount does at the meridian.
166
+ - `orientation: :equatorial` puts the north celestial pole at the top and the
167
+ east on the left, like the sky seen through an equatorial mount.
168
+ - `rotation:` turns the map clockwise, by an `Astronoby::Angle`.
169
+ - `mirrored: true` swaps the left and the right, before the rotation.
170
+
171
+ On an equatorial mount, a refractor or a catadioptric telescope with a
172
+ diagonal shows a mirrored image, and a Newtonian shows the sky turned by half a
173
+ turn:
174
+
175
+ ```ruby
176
+ field_of_view = Astronoby::Angle.from_degrees(4)
177
+
178
+ Skymap::View.new(
179
+ center: center,
180
+ field_of_view: field_of_view,
181
+ orientation: :equatorial
182
+ )
183
+ Skymap::View.new(
184
+ center: center,
185
+ field_of_view: field_of_view,
186
+ orientation: :equatorial,
187
+ mirrored: true
188
+ )
189
+ Skymap::View.new(
190
+ center: center,
191
+ field_of_view: field_of_view,
192
+ orientation: :equatorial,
193
+ rotation: Astronoby::Angle.from_degrees(180)
194
+ )
195
+ ```
196
+
197
+ The north is the one of the celestial pole of the date, in both hemispheres.
198
+ The lit side of the Moon follows the orientation too.
199
+
200
+ ### Accuracy
201
+
202
+ Stars are drawn where they are at the date of the chart, not at their catalog
203
+ position: their proper motion is applied from J2000 to the instant of the chart,
204
+ then the position is precessed and nutated to the true equator and equinox of
205
+ date, and seen from the observer. `Skymap::Star#coordinates_at(instant)` gives
206
+ the first step, as `Astronoby::Coordinates::Equatorial` at J2000. A star without
207
+ a proper motion stays where it is, at any epoch; with a proper motion, its
208
+ coordinates must be at J2000, or `Skymap::Star` raises an `ArgumentError`. For a
209
+ star that has a proper motion but no parallax or no radial velocity, which is
210
+ the case of two thirds of the stars of the Bright Star Catalogue for the
211
+ parallax and of a fifth of the others for the radial velocity, a parallax of 1
212
+ milliarcsecond and a radial velocity of 0 are assumed: the position changes by
213
+ less than 0.1 arcsecond. To keep the charts fast, the proper motion is left out
214
+ of a star that it moves by less than 0.005 pixel on the chart, so a star is
215
+ drawn within 0.005 pixel of where the full propagation puts it.
216
+
217
+ These are not applied:
218
+
219
+ - the annual aberration of light, up to about 20 arcseconds
220
+ - the annual parallax, under 1 arcsecond
221
+ - the atmospheric refraction, which lifts a star near the horizon by up to half
222
+ a degree
223
+ - the rounding of the positions of the Bright Star Catalogue, about 1 arcsecond
224
+
225
+ The proper motion is visible in charts with a field of view under a degree:
226
+ Arcturus moves by 1 arcminute in 27 years. In a larger field, it is smaller
227
+ than a pixel for most stars.
228
+
90
229
  ### Themes
91
230
 
92
231
  A theme gives the colors of the map. `Chart` draws the dark theme by default,
@@ -134,30 +273,41 @@ theme = Skymap::Theme.dark.with(
134
273
  )
135
274
  ```
136
275
 
137
- The colors are `background` (the square behind the sky, which the cardinal
138
- directions are written on), `sky`, `horizon`, `star`, `constellation_line`, `boundary`,
139
- `ecliptic`, `planet`, `sun`, `moon`, `moon_shadow` (with its
140
- `moon_shadow_opacity`, between 0 and 1), `constellation_name`, `star_name`,
141
- `planet_name` and `label` (the cardinal directions). `font` is the font family
142
- of every text.
143
-
144
276
  Use `"none"` as a color to leave something out. The dark theme has
145
277
  `background: "none"`, so the SVG is transparent around the sky, and with
146
278
  `sky: "none"` the sky is transparent too, to put the map on a background of
147
279
  your own. With `horizon: "none"`, the edge of the sky is not outlined.
148
280
 
281
+ The [theme reference][themes-reference] lists every color and shows how to
282
+ build a theme from scratch.
283
+
149
284
  ### Layers
150
285
 
151
286
  A map is the sky, with the layers given to `Chart#render` drawn on top of it,
152
287
  in order: each layer is drawn over the previous ones. Without layers,
153
288
  `Chart#render` draws an empty sky. Leave a layer out to hide what it draws.
154
289
 
290
+ Labels never overlap. A label is written above or below its object, and moves
291
+ to another side of it when that place is taken by another label, a deep-sky
292
+ symbol, a planet, the Sun, the Moon or a bright star. A label with no room
293
+ around its object is left out, so a crowded field shows fewer labels, and a
294
+ larger map or a smaller field shows more. Labels of fields, planets, stars and
295
+ deep-sky objects are placed in this order, and the brighter objects come first
296
+ among deep-sky objects. The names of the constellations, the scale and the
297
+ cardinal directions stay where they are.
298
+
155
299
  #### Stars
156
300
 
157
301
  `Skymap::Layers::Stars` draws stars, bigger the brighter they are.
158
- `Skymap::Catalog::Stars` gives the 9,096 stars of the Yale Bright Star
159
- Catalogue (see [Data sources](#data-sources)), but the layer accepts any list
160
- of `Skymap::Star`.
302
+ `Skymap::Catalog::Stars` gives the 130,556 stars down to magnitude 9 (see
303
+ [Data sources](#data-sources)), but the layer accepts any list of
304
+ `Skymap::Star`. Pass the catalog itself rather than `to_a`: it builds only the
305
+ stars the layer asks for, the ones brighter than `magnitude_limit:` and, in a
306
+ zoomed view, near the view. Building the 130,556 of them takes seconds and
307
+ hundreds of megabytes.
308
+
309
+ Stars are drawn at their position at the date of the chart, with their proper
310
+ motion applied (see [Accuracy](#accuracy)).
161
311
 
162
312
  By default, only stars of magnitude 5 or brighter are drawn, which is about
163
313
  what you can see from a suburban sky. Use `magnitude_limit:` to draw more or
@@ -276,12 +426,78 @@ Skymap::Layers::ConstellationNames.new(
276
426
  )
277
427
  ```
278
428
 
429
+ #### Deep-sky objects
430
+
431
+ `Skymap::Layers::DeepSkyObjects` draws galaxies, clusters, nebulae and other
432
+ deep-sky objects. `Skymap::Catalog::DeepSkyObjects` gives the 109 Messier
433
+ objects and the NGC and IC objects of magnitude 12 or brighter, 1,741 in all
434
+ (see [Data sources](#data-sources)), but the layer accepts any list of
435
+ `Skymap::DeepSkyObject`, such as the objects you pick from the catalog:
436
+
437
+ ```ruby
438
+ objects = Skymap::Catalog::DeepSkyObjects.new
439
+
440
+ Skymap::Layers::DeepSkyObjects.new(objects: objects)
441
+ ```
442
+
443
+ `messier?` tells the Messier objects from the others, so you can draw only
444
+ those:
445
+
446
+ ```ruby
447
+ Skymap::Layers::DeepSkyObjects.new(objects: objects.select(&:messier?))
448
+ ```
449
+
450
+ Each type of object has its own symbol, drawn as an outline in the `deep_sky`
451
+ color of the [theme](#themes), as in most atlases:
452
+
453
+ | `type` | Symbol |
454
+ | ------------------- | ----------------------------------------- |
455
+ | `galaxy` | an ellipse |
456
+ | `globular_cluster` | a circle with a cross |
457
+ | `open_cluster` | a dashed circle |
458
+ | `planetary_nebula` | a circle with four ticks around it |
459
+ | `nebula` | a dashed square with rounded corners |
460
+ | `supernova_remnant` | a dashed circle with a dot at its center |
461
+ | `asterism` | a dotted circle |
462
+ | `double_star` | a small circle with a bar through it |
463
+ | `other` | a small cross |
464
+
465
+ By default, objects of magnitude 11 or brighter are drawn, which keeps every
466
+ Messier object, and an object without a magnitude is always drawn. Use
467
+ `magnitude_limit:` to draw fewer objects, or up to 12 to draw all of the
468
+ catalog, or select the objects you want. The `magnitude` of an object is a
469
+ visual one unless its `magnitude_band` is `:blue`, as for 65 faint NGC and IC
470
+ objects that only have a blue magnitude, which is about a magnitude fainter for
471
+ most objects:
472
+
473
+ ```ruby
474
+ galaxies = objects.select { |object| object.type == :galaxy }
475
+
476
+ Skymap::Layers::DeepSkyObjects.new(objects: galaxies, magnitude_limit: 9.5)
477
+ ```
478
+
479
+ An object has the size of the symbol on the map, which is small, until its
480
+ size in the sky is larger: it is then drawn at its real size, which you see
481
+ when you zoom in with a [view](#view). A small object keeps its proportions in
482
+ the symbol, and an object with no known size, such as M40, is drawn with the
483
+ smallest symbol. A round object is a circle. An elongated object is an ellipse
484
+ turned the way the object is, as `position_angle` gives it, from the north
485
+ through the east: the layer follows the [orientation](#orientation) of the
486
+ view, its rotation and its mirror, and the whole sky. An elongated object
487
+ without a position angle (M1, M8, M16, M42 and M43) is not turned, as nothing
488
+ says which way: it is drawn as a circle with the same area as the ellipse,
489
+ whose diameter is the square root of the product of its axes.
490
+
491
+ With `labels: true`, the designation of each object (such as `M31`) is written
492
+ next to its symbol, in the `deep_sky` color too. Objects below the horizon or
493
+ outside the view are left out. Give the layer after the stars to draw its
494
+ symbols over them.
495
+
279
496
  #### Moon
280
497
 
281
498
  `Skymap::Layers::Moon` draws the Moon where it is in the sky, with its phase.
282
- The Moon is drawn upright, as the observer sees it from the ground, so a
283
- crescent over the western horizon is tilted as it is in the real sky. On the
284
- chart, this means the lit side does not always face the Sun's position.
499
+ The lit side of the Moon faces the Sun on the map: the Moon is turned with the
500
+ projection, wherever it is on the map and whatever the orientation of the view.
285
501
 
286
502
  Astronoby needs an ephemeris to compute where the Moon is: download one once,
287
503
  load it and give it to the layer (see the
@@ -362,11 +578,184 @@ data, and it stops at the horizon:
362
578
  Skymap::Layers::Ecliptic.new
363
579
  ```
364
580
 
581
+ #### Grid
582
+
583
+ `Skymap::Layers::Grid` draws the celestial coordinate grid: the lines of right
584
+ ascension and the circles of declination, including the celestial equator. They
585
+ are the coordinates of the stars in a catalog, and the grid shows where the
586
+ sky turns around the celestial pole. It needs no data, and it stops at the
587
+ horizon:
588
+
589
+ ```ruby
590
+ Skymap::Layers::Grid.new
591
+ ```
592
+
593
+ The lines are 30 degrees apart (2 hours of right ascension) by default. Use
594
+ `step:`, an `Astronoby::Angle`, to draw them closer or further apart. It is
595
+ between 1 and 90 degrees and divides 360 degrees. For a line of right ascension
596
+ every hour:
597
+
598
+ ```ruby
599
+ Skymap::Layers::Grid.new(step: Astronoby::Angle.from_hours(1))
600
+ ```
601
+
602
+ By default the grid is in the equator and equinox of J2000, like the
603
+ coordinates of atlases and catalogs. Use `epoch: :of_date` to draw it in the
604
+ true equator and equinox of the date of the chart instead. The declination
605
+ circles are then centered on the true celestial pole of that date, which is
606
+ what a polar-aligned equatorial mount shows:
607
+
608
+ ```ruby
609
+ Skymap::Layers::Grid.new(epoch: :of_date)
610
+ ```
611
+
612
+ The two grids differ by a fraction of a degree today, which only shows when the
613
+ map is zoomed on a small area of the sky. For a view that follows a
614
+ polar-aligned equatorial mount, use `:of_date`, which pairs with
615
+ `orientation: :equatorial` (north celestial pole at the top). Keep `:j2000` to
616
+ read coordinates off the grid and look them up in an atlas or a catalog.
617
+
618
+ Give the grid first to `Chart#render` to draw the stars and the other layers
619
+ over it.
620
+
621
+ #### Field circles
622
+
623
+ `Skymap::Layers::FieldCircles` draws circles that show what a finder scope or an
624
+ eyepiece covers in the sky. Give it the fields to draw, as `Skymap::Field` or
625
+ `Skymap::Setup` objects (see
626
+ [Telescope, eyepiece and fields](#telescope-eyepiece-and-fields)):
627
+
628
+ ```ruby
629
+ Skymap::Layers::FieldCircles.new(fields: [finder, setup], labels: true)
630
+ ```
631
+
632
+ The circles are centered on the center of the [view](#view), so the layer needs
633
+ a view, and nothing is drawn without one. Use `center:` to draw them around
634
+ another point, as the equatorial or horizontal coordinates of a point of the
635
+ sky, such as a target when the view is wider than the circles:
636
+
637
+ ```ruby
638
+ Skymap::Layers::FieldCircles.new(fields: [setup], center: target)
639
+ ```
640
+
641
+ A circle has the size of its field on the sphere, then it is projected, so it
642
+ keeps its size and its shape anywhere in the map. Each circle and its label are
643
+ drawn in the `field_circle` color of the [theme](#themes). In a view, a circle
644
+ is drawn whole, even below the horizon, and a circle that does not reach the
645
+ view is left out. In the whole sky, which has no center unless you give a `center:`,
646
+ a circle stops at the horizon.
647
+
648
+ With `labels: true`, the label of each field is written next to its circle.
649
+ A label that would fall outside the view is left out. Give the layer last to
650
+ `Chart#render` to draw the circles over the stars.
651
+
365
652
  #### Cardinal directions
366
653
 
367
654
  `Skymap::Layers::CardinalDirections` labels the cardinal directions (N, E, S,
368
655
  W) in the padding, with a size that follows it: a padding of about 3% of the
369
- size gives readable labels.
656
+ size gives readable labels. It draws nothing when the map has a [view](#view).
657
+
658
+ #### Scale
659
+
660
+ `Skymap::Layers::Scale` draws a scale bar with its length as a label, such as
661
+ `1°`, `30′` or `10″`, in the lower left of a map with a [view](#view). It draws
662
+ nothing on the whole sky, where the scale changes from one place to another:
663
+
664
+ ```ruby
665
+ Skymap::Layers::Scale.new
666
+ ```
667
+
668
+ The bar is the longest of 1, 2, 5, 10, 20 and 30 arc-seconds, arc-minutes or
669
+ degrees that is at most a fifth of the width of the map. It stays horizontal
670
+ and the same in every [orientation](#orientation) of the view. The scale of the
671
+ map changes from the center to the edge, and the bar follows it: its length is
672
+ the angle it says at the place where it is drawn. A straight bar on the map is a
673
+ small circle on the sky, not the shortest path between its ends, so the angle
674
+ between the ends is a little smaller than the label on wide fields. The
675
+ difference is below 0.01% up to a field of view of 90 degrees, which covers
676
+ finder charts, and at most 0.1% at 120 degrees and 0.3% at 180 degrees. Nothing
677
+ is drawn when even 1 arc-second is too long, for a field of view below about
678
+ 0.0014 degrees (5 arc-seconds). The bar and its label use the `scale`
679
+ color of the theme. With a [print size](#print-size), the bar is as long on
680
+ paper as the angle it says.
681
+
682
+ ### Telescope, eyepiece and fields
683
+
684
+ An eyepiece in a telescope shows a small part of the sky, often less than a
685
+ degree wide. These classes describe the equipment, compute that part of the
686
+ sky, and give it to a [view](#view) or to the [field circles](#field-circles).
687
+
688
+ `Skymap::Telescope` has a focal length and an aperture, in millimeters, as
689
+ `focal_length_mm` and `aperture_mm`. `Skymap::Eyepiece` has a focal length in
690
+ millimeters, as `focal_length_mm`, and the apparent field of view that its
691
+ maker gives, as an `Astronoby::Angle`. All the numbers must be above 0, and
692
+ the apparent field of view up to 180 degrees:
693
+
694
+ ```ruby
695
+ telescope = Skymap::Telescope.new(focal_length_mm: 1200, aperture_mm: 200)
696
+ eyepiece = Skymap::Eyepiece.new(
697
+ focal_length_mm: 25,
698
+ apparent_field_of_view: Astronoby::Angle.from_degrees(52)
699
+ )
700
+ ```
701
+
702
+ `Skymap::Setup` pairs a telescope with an eyepiece, and computes what you see
703
+ with them:
704
+
705
+ ```ruby
706
+ setup = Skymap::Setup.new(telescope: telescope, eyepiece: eyepiece)
707
+
708
+ setup.magnification # => 48.0
709
+ setup.true_field # => an Astronoby::Angle of 1.083 degrees
710
+ setup.label # => "25 mm, 48×, 1.1°"
711
+ ```
712
+
713
+ - `magnification` is the focal length of the telescope divided by the one of
714
+ the eyepiece, as a `Float`.
715
+ - `true_field` is the part of the sky you see, as an `Astronoby::Angle`: the
716
+ apparent field of view divided by the magnification, the usual first-order
717
+ formula. With a Barlow lens or a focal reducer, give the effective focal
718
+ length of the telescope. It is what `field_of_view:` of a view takes, to draw
719
+ a map as wide as the eyepiece.
720
+ - `label` is a short text for the circle: the focal length of the eyepiece,
721
+ the magnification and the true field, in degrees (with at most one decimal)
722
+ from 1 degree, in arcminutes (`15′`) below, and in arcseconds (`30″`) below
723
+ one arcminute.
724
+
725
+ `Skymap::Field` is a field that does not come from a setup, such as a finder
726
+ scope, a Telrad ring or the frame of a camera: its `true_field` as an
727
+ `Astronoby::Angle`, and the `label` you choose:
728
+
729
+ ```ruby
730
+ finder = Skymap::Field.new(
731
+ true_field: Astronoby::Angle.from_degrees(5),
732
+ label: "Finder 5°"
733
+ )
734
+ ```
735
+
736
+ `FieldCircles` takes anything with a `true_field` and a `label`, so a list of
737
+ fields and setups draws them together around a target, on a map twice as wide
738
+ as the eyepiece:
739
+
740
+ ```ruby
741
+ view = Skymap::View.new(
742
+ center: target,
743
+ field_of_view: Astronoby::Angle.from_degrees(
744
+ setup.true_field.degrees * 2
745
+ )
746
+ )
747
+
748
+ chart = Skymap::Chart.new(
749
+ observer: observer,
750
+ instant: instant,
751
+ canvas: canvas,
752
+ view: view
753
+ )
754
+ svg = chart.render(
755
+ Skymap::Layers::Stars.new(stars: stars, magnitude_limit: 7),
756
+ Skymap::Layers::FieldCircles.new(fields: [finder, setup], labels: true)
757
+ )
758
+ ```
370
759
 
371
760
  ## Examples
372
761
 
@@ -374,24 +763,77 @@ The [examples](https://github.com/rhannequin/skymap/tree/main/examples)
374
763
  folder has scripts and the maps they draw, for
375
764
  different combinations of layers, themes and observers: the naked-eye sky, a
376
765
  printable map of the constellations, the planets, a night vision map for
377
- outdoors, the southern sky and more. Start from the one closest to what you
378
- want to draw.
766
+ outdoors, the southern sky, the galaxies of Virgo and more. Start from the one
767
+ closest to what you want to draw.
379
768
 
380
769
  ## Data sources
381
770
 
382
771
  ### Stars
383
772
 
384
- `data/stars.csv` is extracted from the [Yale Bright Star Catalogue, 5th
385
- Revised Edition][bsc5] (VizieR V/50): the 9,096 stars of the catalog with
386
- their Harvard Revised number (`hr`), J2000 coordinates (`right_ascension` in
387
- hours, `declination` in degrees) and visual magnitude (`magnitude`). The 14
388
- non-stellar entries kept only for numbering are left out.
773
+ `data/stars.bin` is the star catalog of `Skymap::Catalog::Stars`: 130,556
774
+ stars down to magnitude 9, in a binary file of 2.6 MB that the catalog reads
775
+ by magnitude and by area of the sky, so that a view of a few degrees reads a few
776
+ hundred stars out of the file. Each star has its J2000 coordinates, its visual
777
+ magnitude and its astrometry, blank when unknown: the proper motion in right
778
+ ascension (`proper_motion_ra`, which includes the cosine of the declination) and
779
+ in declination (`proper_motion_dec`) in mas/yr, the `parallax` in mas and the
780
+ `radial_velocity` in km/s. `Skymap::Catalog::Stars` loads them as an
781
+ `Astronoby::AngularVelocity`, an `Astronoby::Angle` and an
782
+ `Astronoby::Velocity` on `Skymap::Star`. They are all optional, so a list of
783
+ your own stars can leave them out: the proper motions are given together, and
784
+ the parallax and the radial velocity are `nil` when unknown.
785
+
786
+ The file merges the stars of two sources:
787
+
788
+ - the 9,096 stars of the [Yale Bright Star Catalogue, 5th Revised Edition][bsc5]
789
+ (VizieR V/50), with their Harvard Revised number (`hr`), extracted in
790
+ `data/stars.csv`. They keep their values: a proper motion for each of them, a
791
+ parallax for 3,100 (387 of them are dynamical parallaxes, not trigonometric
792
+ ones) and a radial velocity for 9,090. The 14 non-stellar entries kept only
793
+ for numbering are left out
794
+ - the 121,460 other stars of [Tycho-2][tycho2] down to magnitude 9, with no
795
+ Harvard Revised number. The visual magnitude is computed from the Tycho
796
+ magnitudes with V = VT - 0.090 (BT - VT), which gives the magnitude of the
797
+ Bright Star Catalogue to 0.001 at the median. The proper motion is the one of
798
+ Tycho-2, and the parallax and the radial velocity are the ones of [Gaia
799
+ DR3][gaia], or the parallax of the new reduction of [Hipparcos][hipparcos]
800
+ when Gaia has none: all but 6 of the stars have a proper motion, 97% a
801
+ parallax and 79% a radial velocity. The position of the few hundred stars for
802
+ which Tycho-2 only gives the position observed around 1991 comes from Gaia or
803
+ Hipparcos, brought to J2000 with their proper motion. A star of Tycho-2 is
804
+ left out when it is already in the Bright Star Catalogue: within 5
805
+ arcseconds, plus 5 years of its proper motion, and 0.5 magnitude
806
+
807
+ The file is generated with `bin/build_stars`, which reads `data/stars.csv` and
808
+ queries the [ESA Gaia archive][esa-archive], where ESA also serves Tycho-2 and
809
+ Hipparcos. `bin/build_catalog` generates `data/stars.csv` from the Bright Star
810
+ Catalogue, which it downloads from CDS. `data/stars.csv` is not part of the
811
+ gem: the gem has `data/stars.bin` only.
812
+
813
+ The data of Gaia, Hipparcos and Tycho come from ESA, which distributes them
814
+ under [CC BY-NC 3.0 IGO][cc-by-nc-igo]: the file is released under the same
815
+ license, with the credits "Credit: ESA, Gaia DPAC" and "Credit: ESA", and ESA
816
+ asks to be contacted before any use that generates a financial gain. The
817
+ license and the terms are in `data/LICENSE-stars.txt`, shipped with the gem. The
818
+ code stays under the MIT license.
389
819
 
390
820
  > Hoffleit, D., Warren Jr., W. H., 1991, _The Bright Star Catalogue, 5th
391
821
  > Revised Ed. (Preliminary Version)_, Astronomical Data Center, NSSDC/ADC.
392
822
 
393
- The file is generated with `bin/build_catalog`, which downloads the catalog
394
- from CDS.
823
+ > Høg, E. et al., 2000, _The Tycho-2 catalogue of the 2.5 million brightest
824
+ > stars_, A&A 355, L27.
825
+
826
+ > van Leeuwen, F., 2007, _Validation of the new Hipparcos reduction_, A&A 474,
827
+ > 653.
828
+
829
+ > Gaia Collaboration, 2016, _The Gaia mission_, A&A 595, A1; Gaia
830
+ > Collaboration, 2023, _Gaia Data Release 3: Summary of the content and survey
831
+ > properties_, A&A 674, A1.
832
+
833
+ This work has made use of data from the European Space Agency (ESA) mission
834
+ Gaia (<https://www.cosmos.esa.int/gaia>), processed by the Gaia Data Processing
835
+ and Analysis Consortium (DPAC,
836
+ <https://www.cosmos.esa.int/web/gaia/dpac/consortium>).
395
837
 
396
838
  This research has made use of the VizieR catalogue access tool, CDS,
397
839
  Strasbourg, France ([DOI: 10.26093/cds/vizier][vizier-doi]). The original
@@ -466,12 +908,90 @@ The file is generated with `bin/build_boundaries`, which downloads the
466
908
  catalog from CDS, keeps each shared border once, adds the points along the
467
909
  borders and precesses them to J2000 with Astronoby.
468
910
 
911
+ ### Deep-sky objects
912
+
913
+ `data/deep_sky_objects.csv` holds 1,741 deep-sky objects, from
914
+ [OpenNGC][openngc] (release v20260501, commit `36cb178`), one row per object:
915
+ the 109 Messier objects, then the 1,496 NGC and 136 IC objects of magnitude 12
916
+ or brighter that are not Messier objects.
917
+
918
+ - `designation`: `M31` for a Messier object, `NGC 7000` or `IC 434` for the
919
+ others, without zero padding.
920
+ - `name`: the first common name of OpenNGC, blank when it has none.
921
+ - `type`: one of the types below.
922
+ - `right_ascension` (hours) and `declination` (degrees): J2000 coordinates.
923
+ - `magnitude`: the visual (V) magnitude, or the blue (B) one when there is no
924
+ V magnitude. Every object has one, and every Messier object has a V magnitude.
925
+ - `magnitude_band`: `V` or `B`, the band of the `magnitude`.
926
+ - `major_axis` and `minor_axis`: the size in arcminutes, blank when unknown.
927
+ - `position_angle`: the angle of the major axis in degrees, from the north
928
+ through the east, blank when unknown. OpenNGC only gives it to galaxies, M24
929
+ and the Pleiades.
930
+
931
+ The OpenNGC types become these ones:
932
+
933
+ | OpenNGC | `type` |
934
+ | -------------------------------------- | ------------------- |
935
+ | `G`, `GPair`, `GTrpl`, `GGroup` | `galaxy` |
936
+ | `OCl` | `open_cluster` |
937
+ | `GCl` | `globular_cluster` |
938
+ | `PN` | `planetary_nebula` |
939
+ | `Neb`, `HII`, `EmN`, `RfN`, `Cl+N` | `nebula` |
940
+ | `SNR` | `supernova_remnant` |
941
+ | `*Ass` | `asterism` |
942
+ | `**` | `double_star` |
943
+ | `Other` | `other` |
944
+
945
+ M42 is the only Messier object of OpenNGC type `Cl+N`, a cluster with
946
+ nebulosity: it is a `nebula`, as its nebulosity is far larger than the
947
+ Trapezium cluster at its center. M24 (a star cloud) is an `asterism`, M73 is
948
+ `other`, and M40 is a `double_star`: OpenNGC lists it with no size. Any other
949
+ OpenNGC type stops the build.
950
+
951
+ `Skymap::Catalog::DeepSkyObjects` loads them as `Skymap::DeepSkyObject`, with
952
+ the sizes as `Astronoby::Angle`. A `minor_axis` is blank for most clusters and
953
+ some nebulae: `Skymap::DeepSkyObject` takes it as a round object, with a
954
+ `minor_axis` equal to its `major_axis`. Both are `nil` when the size is unknown
955
+ (M40, M73).
956
+
957
+ OpenNGC identifies M102 as a duplicate of M101 (its identity is controversial
958
+ and it follows the assumption of NED), so M102 is left out and the file has no
959
+ `M102`. M40 and M45 are not in the NGC and IC catalogs: they come from the
960
+ addendum of OpenNGC, where the Pleiades are listed as Melotte 22.
961
+
962
+ The file leaves out the objects of OpenNGC that are not in the sky or not
963
+ objects (duplicates, non-existent objects, stars, novae and dark nebulae), the
964
+ components of a galaxy listed apart (such as `NGC 520 NED01`), the objects of
965
+ the addendum other than M40 and M45, and every NGC and IC object without a
966
+ magnitude or fainter than magnitude 12. This keeps the objects you can look
967
+ for in an amateur telescope, without the thousands of faint galaxies.
968
+
969
+ The file is generated with `bin/build_deep_sky_objects`, which downloads
970
+ `NGC.csv` and `addendum.csv` from the pinned commit of OpenNGC and keeps these
971
+ objects. Pass the path of a local directory holding the two files to build from
972
+ it. The output must not change when you run it again.
973
+
974
+ OpenNGC is released under the [Creative Commons Attribution-ShareAlike 4.0
975
+ International][cc-by-sa-4] license, and so is `data/deep_sky_objects.csv`: the
976
+ license, with the credits and changes, is in
977
+ `data/LICENSE-deep_sky_objects.txt`, shipped with the gem. If you share this
978
+ file or data adapted from it, you must release your version under the same
979
+ license. The code of Skymap stays under the MIT license.
980
+
981
+ OpenNGC merges the data of the NASA/IPAC Extragalactic Database (NED),
982
+ HyperLEDA, SIMBAD, the HEASARC archive and Harold Corwin's NGC/IC Positions and
983
+ Notes. This research has made use of the NASA/IPAC Extragalactic Database
984
+ (NED), which is operated by the Jet Propulsion Laboratory, California Institute
985
+ of Technology, under contract with the National Aeronautics and Space
986
+ Administration, of the HyperLEDA database, and of the SIMBAD database, operated
987
+ at CDS, Strasbourg, France.
988
+
469
989
  ### Ephemeris of the tests
470
990
 
471
991
  The tests of the Sun, the Moon and the planets use
472
992
  `spec/fixtures/de440s_2025_2030_excerpt.bsp`, an excerpt of DE440s, the
473
- planetary and lunar ephemeris of the [Jet Propulsion Laboratory][jpl-ephemerides],
474
- distributed by [NAIF][naif]. It only keeps the barycenters of the planets, the
993
+ planetary and lunar ephemeris of the
994
+ [Jet Propulsion Laboratory][jpl-ephemerides], distributed by [NAIF][naif]. It only keeps the barycenters of the planets, the
475
995
  Sun, the Moon and the Earth, from 2025 to 2030. As NAIF asks for modified
476
996
  kernels, its comment area names this project as the last to modify it, and
477
997
  points to the original kernel. It is not part of the gem.
@@ -489,14 +1009,19 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
489
1009
  - Tests: `bundle exec rspec`
490
1010
  - Linter ([Standard Ruby] through RuboCop): `bundle exec rubocop`
491
1011
  (`bundle exec rubocop -a` to autocorrect)
1012
+ - Render times of representative charts: `bin/benchmark` (`RUNS=10` to change
1013
+ the number of runs it keeps the best of)
492
1014
  - Everything CI checks: `bin/ci`
493
1015
 
494
1016
  ### Releasing
495
1017
 
496
- 1. Update the version number in `lib/skymap/version.rb` and `CHANGELOG.md`.
497
- 2. Commit, then tag and push: `git tag vX.Y.Z && git push origin vX.Y.Z`.
498
- 3. The [Release workflow](.github/workflows/release.yml) builds and publishes
499
- the gem to RubyGems.org using [trusted publishing].
1018
+ 1. Update the version number in `lib/skymap/version.rb`, date the section of
1019
+ the version in `CHANGELOG.md` and run `bundle install` to update
1020
+ `Gemfile.lock`.
1021
+ 2. Merge it, then tag and push: `git tag vX.Y.Z && git push origin vX.Y.Z`.
1022
+ 3. The [Release workflow](.github/workflows/release.yml) checks that the tag
1023
+ matches the version, then builds and publishes the gem to RubyGems.org using
1024
+ [trusted publishing].
500
1025
 
501
1026
  ## Contributing
502
1027
 
@@ -507,7 +1032,13 @@ the [code of conduct](CODE_OF_CONDUCT.md).
507
1032
 
508
1033
  ## License
509
1034
 
510
- The gem is available as open source under the terms of the [MIT License].
1035
+ The gem is available as open source under the terms of the [MIT License]. The
1036
+ data file `data/deep_sky_objects.csv` is adapted from OpenNGC and released under
1037
+ [CC BY-SA 4.0][cc-by-sa-4] (see [Data sources](#deep-sky-objects-1)). The data
1038
+ file `data/stars.bin` merges data of ESA missions and is released under
1039
+ [CC BY-NC 3.0 IGO][cc-by-nc-igo]: it is free to use with credit, and a use that
1040
+ generates a financial gain needs the authorization of ESA (see [Data
1041
+ sources](#stars-1)).
511
1042
 
512
1043
  ## Code of Conduct
513
1044
 
@@ -521,11 +1052,19 @@ rooms and mailing lists is expected to follow the
521
1052
  [trusted publishing]: https://guides.rubygems.org/trusted-publishing/
522
1053
  [MIT License]: https://opensource.org/licenses/MIT
523
1054
  [bsc5]: https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50
1055
+ [tycho2]: https://cdsarc.cds.unistra.fr/viz-bin/cat/I/259
1056
+ [hipparcos]: https://cdsarc.cds.unistra.fr/viz-bin/cat/I/311
1057
+ [gaia]: https://www.cosmos.esa.int/web/gaia/dr3
1058
+ [esa-archive]: https://gea.esac.esa.int/archive/
524
1059
  [vizier-doi]: https://doi.org/10.26093/cds/vizier
1060
+ [themes-reference]: https://github.com/rhannequin/skymap/blob/main/docs/themes.md
525
1061
  [astronoby-ephem]: https://github.com/rhannequin/astronoby/blob/main/docs/ephem.md
526
1062
  [oae-maps]: https://www.astro4edu.org/
527
1063
  [vi49]: https://cdsarc.cds.unistra.fr/viz-bin/cat/VI/49
528
1064
  [jpl-ephemerides]: https://ssd.jpl.nasa.gov/planets/eph_export.html
529
1065
  [naif]: https://naif.jpl.nasa.gov/naif/rules.html
530
1066
  [cc-by-4]: https://creativecommons.org/licenses/by/4.0/
1067
+ [cc-by-sa-4]: https://creativecommons.org/licenses/by-sa/4.0/
1068
+ [cc-by-nc-igo]: https://creativecommons.org/licenses/by-nc/3.0/igo/
1069
+ [openngc]: https://github.com/mattiaverga/OpenNGC
531
1070
  [iau-csn]: https://www.pas.rochester.edu/~emamajek/WGSN/IAU-CSN.txt