Skip to content

OpenNGC Sexagesimal Coordinates → Decimal Degrees

View on GitHub

What

Convert the celestial coordinates in the OpenNGC deep-sky catalogue from sexagesimal form — right ascension as HH:MM:SS.s (hours) and declination as ±DD:MM:SS.s — into decimal degrees, the form plotting libraries, GIS tools and cross-match services expect.

Why interesting

Essentially every astronomical catalogue stores coordinates sexagesimally, and it trips up newcomers twice: right ascension is in hours, not degrees (so it needs a ×15 factor, since 24h = 360°), and the declination's sign sits on the degrees field but must apply to the arc-minutes and arc-seconds too. Getting either wrong silently places objects in the wrong part of the sky. The standard fix is an astropy/SkyCoord call or a hand-written parser; bxp does it declaratively in two expressions.

Edge cases sourced from.

  • The HH:MM:SS / ±DD:MM:SS convention and the hours→degrees ×15 factor are standard equatorial coordinate notation used by every catalogue.
  • Non-existent catalogue entries (Type = NonEx) carry blank RA/Dec, so the conversion must guard empty cells.

Data source. OpenNGC — the open, maintained catalogue of NGC/IC objects (database_files/NGC.csv, semicolon-delimited). Licensed CC-BY-SA-4.0. (This slice: famous Messier objects — M31, M42, M51, M13, M57, … — plus a few IC objects and one NonEx blank-coord entry.)

The trick

(see sample.json):

  • RA → degrees: (SPLIT_PART([RA],':',1) + SPLIT_PART([RA],':',2)/60 + SPLIT_PART([RA],':',3)/3600) * 15 — split on :, sum to hours, ×15. Click it to run against sample.csv; show all turns the whole catalogue into degrees.
  • Dec → degrees: capture the sign once with STARTS_WITH('-'), then apply it to the magnitude ABS(deg) + min/60 + sec/3600 so the sign covers the whole value, not just the degrees field.
  • Both guarded with ISEMPTY(...) so blank-coord NonEx rows stay empty instead of computing to 0 (which would be a real point on the sky).

At full scale

bash fetch-full.sh          # downloads all ~13,970 NGC/IC objects into ./full/
bxp-cli --config full.json  # converts every object's RA/Dec → decimal degrees

Final result

The Andromeda Galaxy (M31 = NGC 224) and the Orion Nebula (M42 = NGC 1976, southern) convert exactly:

NGC0224  00:42:44.35  +41:16:08.6  →  10.68479166666   41.269055555556
NGC1976  05:35:16.48  -05:23:22.8  →  83.818666666665  -5.389666666666

Those decimal degrees drop straight into a scatter plot, a astropy SkyCoord, or a spatial join — no per-coordinate parsing in the consumer.

Sample data

Run it with bxp-cli --config ./sample.json --template ngc_to_decimal_degrees:

{
  // OpenNGC — the open catalogue of NGC/IC deep-sky objects. Like nearly all
  // astronomical catalogues, it stores celestial coordinates in SEXAGESIMAL
  // form: right ascension as HH:MM:SS.s (hours, not degrees) and declination as
  // ±DD:MM:SS.s. To plot, cross-match, or feed them to a GIS/most APIs you need
  // DECIMAL DEGREES. This template does the standard conversion in two
  // expressions — no astropy.
  conversion_templates: {
    ngc_to_decimal_degrees: {
      data_dir:          ".",
      file_pattern_in:   ".csv",
      file_pattern_out:  ".csvx",
      // TRICK 0 — OpenNGC is semicolon-delimited.
      csv_delimiter_in:  ";",
      csv_delimiter_out: ",",

      input_schema: {
        $name: "[Name]",

        // TRICK 1 — RA is in HOURS:MIN:SEC. Convert to hours, then ×15 because
        // 24h spans 360° (1h = 15°). SPLIT_PART on ':' pulls each part.
        //   00:42:44.35 → (0 + 42/60 + 44.35/3600) × 15 = 10.6848°
        // (Non-existent catalogue entries have blank coords → guard with LEN.)
        $ra_deg: "IF(ISEMPTY([RA]), '', (SPLIT_PART([RA], ':', 1) + SPLIT_PART([RA], ':', 2) / 60 + SPLIT_PART([RA], ':', 3) / 3600) * 15)",

        // TRICK 2 — Dec is ±DEG:ARCMIN:ARCSEC. The sign on the degrees part must
        // apply to the whole value, so capture it once (STARTS_WITH '-') and
        // multiply the magnitude (ABS of the degrees field) by it.
        //   +41:16:08.6 → +(41 + 16/60 + 8.6/3600) = 41.2691°
        //   -05:23:22.8 → -(5  + 23/60 + 22.8/3600) = -5.3897°
        $dec_deg: "IF(ISEMPTY([Dec]), '', IF(STARTS_WITH(TRIM([Dec]), '-'), -1, 1) * (ABS(SPLIT_PART([Dec], ':', 1)) + SPLIT_PART([Dec], ':', 2) / 60 + SPLIT_PART([Dec], ':', 3) / 3600))"
      },

      row_rules: [ { when: "1 = 1", rows: [ {} ] } ],

      output_schema: {
        name:    "$name",
        ra_deg:  "$ra_deg",
        dec_deg: "$dec_deg"
      }
    }
  }
}
Name;Type;RA;Dec;Const;MajAx;MinAx;PosAng;B-Mag;V-Mag;J-Mag;H-Mag;K-Mag;SurfBr;Hubble;Pax;Pm-RA;Pm-Dec;RadVel;Redshift;Cstar U-Mag;Cstar B-Mag;Cstar V-Mag;M;NGC;IC;Cstar Names;Identifiers;Common names;NED notes;OpenNGC notes;Sources
NGC0224;G;00:42:44.35;+41:16:08.6;And;177.83;69.66;35;4.29;3.44;2.09;1.28;0.98;23.63;Sb;6.0000;;;-300;-0.001000;;;;031;;;;2MASX J00424433+4116074,IRAS 00400+4059,MCG +07-02-016,PGC 002557,UGC 00454;Andromeda Galaxy;;;Type:1|RA:1|Dec:1|Const:99|MajAx:3|MinAx:3|PosAng:3|B-Mag:3|V-Mag:2|J-Mag:2|H-Mag:2|K-Mag:2|SurfBr:3|Hubble:3|Pax:2|RadVel:2|Redshift:2
NGC1952;SNR;05:34:31.97;+22:00:52.1;Tau;8.00;4.00;;;8.40;;;;;;;;;;;;;;001;;;;IRAS 05315+2158,LBN 833;Crab Nebula;Position is for the Crab pulsar.;;Type:1|RA:1|Dec:1|Const:99|MajAx:9|MinAx:9|V-Mag:3
NGC1976;Cl+N;05:35:16.48;-05:23:22.8;Ori;90.00;60.00;;4.00;4.00;;;;;;;1.670;-0.300;28;0.000093;;;;042;;;;LBN 974,MWSC 0582;Great Orion Nebula,Orion Nebula;;;Type:1|RA:1|Dec:1|Const:99|MajAx:9|MinAx:9|B-Mag:3|V-Mag:10|Pm-RA:2|Pm-Dec:2|RadVel:2|Redshift:2
NGC5194;G;13:29:52.71;+47:11:42.6;CVn;13.71;11.67;163;8.61;8.36;6.40;5.65;5.50;22.90;SABb;7.8000;;;465;0.001550;;;;051;;;;2MASX J13295269+4711429,IRAS 13277+4727,MCG +08-25-012,PGC 047404,UGC 08493;Whirlpool Galaxy;;;Type:1|RA:1|Dec:1|Const:99|MajAx:3|MinAx:3|PosAng:3|B-Mag:3|V-Mag:2|J-Mag:2|H-Mag:2|K-Mag:2|SurfBr:3|Hubble:3|Pax:2|RadVel:2|Redshift:2
NGC6205;GCl;16:41:41.63;+36:27:40.7;Her;16.50;;;;5.80;4.45;3.94;3.85;;;0.0813;-3.180;-2.560;-244;-0.000815;;;;013;;;;2MASX J16414163+3627407,MWSC 2445;Hercules Globular Cluster;;;Type:1|RA:1|Dec:1|Const:99|MajAx:5|V-Mag:2|J-Mag:2|H-Mag:2|K-Mag:2|Pax:2|Pm-RA:2|Pm-Dec:2|RadVel:2|Redshift:2
NGC6720;PN;18:53:35.01;+33:01:42.9;Lyr;1.27;;;9.70;8.80;16.40;;;;;1.2707;1.494;2.486;-19;-0.000064;;15.03;15.29;057;;;HD 175353;IRAS 18517+3257,PN G063.1+13.9;Ring Nebula;;;Type:1|RA:1|Dec:1|Const:99|MajAx:8|B-Mag:3|V-Mag:3|J-Mag:2|Pax:2|Pm-RA:2|Pm-Dec:2|RadVel:2|Redshift:2|Cstar B-Mag:8|Cstar V-Mag:8
NGC0598;G;01:33:50.89;+30:39:36.8;Tri;62.09;36.73;23;6.35;5.79;5.04;4.35;4.10;23.61;Sc;;;;-179;-0.000598;;;;033;;;;2MASX J01335090+3039357,IRAS 01310+3024,MCG +05-04-069,PGC 005818,UGC 01117;Triangulum Galaxy,Triangulum Pinwheel;;;Type:1|RA:1|Dec:1|Const:99|MajAx:3|MinAx:3|PosAng:3|B-Mag:3|V-Mag:3|J-Mag:2|H-Mag:2|K-Mag:2|SurfBr:3|Hubble:3|RadVel:2|Redshift:2
IC0001;**;00:08:27.05;+27:43:03.6;Peg;;;;;;;;;;;;;;;;;;;;;;;;;;;Type:1|RA:1|Dec:1|Const:99
IC0002;G;00:11:00.88;-12:49:22.3;Cet;0.98;0.32;142;15.46;;12.26;11.48;11.17;23.45;Sb;;;;6775;0.022860;;;;;;;;2MASX J00110081-1249206,IRAS 00084-1306,MCG -02-01-031,PGC 000778;;;;Type:1|RA:1|Dec:1|Const:99|MajAx:3|MinAx:3|PosAng:3|B-Mag:3|J-Mag:2|H-Mag:2|K-Mag:2|SurfBr:3|Hubble:3|RadVel:2|Redshift:2
IC0010;G;00:20:17.34;+59:18:13.6;Cas;6.76;6.03;129;11.78;10.35;7.23;6.34;6.01;24.53;IB;;;;-348;-0.001160;;;;;;;;2MASX J00201733+5918136,IRAS 00175+5902,IRAS 00177+5900,MCG +10-01-001,PGC 001305,UGC 00192;;The 2MASX position refers to the center of the IR isophotes.;;Type:1|RA:1|Dec:1|Const:99|MajAx:3|MinAx:3|PosAng:3|B-Mag:3|V-Mag:3|J-Mag:2|H-Mag:2|K-Mag:2|SurfBr:3|Hubble:3|RadVel:2|Redshift:2
IC1064;NonEx;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Type:1
name,ra_deg,dec_deg
NGC0224,10.68479166666,41.269055555556
NGC1952,83.633208333345,22.014472222222
NGC1976,83.818666666665,-5.389666666666
NGC5194,202.469625,47.195166666666
NGC6205,250.42345833333,36.461305555556
NGC6720,283.395874999995,33.028583333334
NGC0598,23.462041666665,30.660222222222
IC0001,2.11270833333,27.717666666667
IC0002,2.753666666655,-12.822861111111
IC0010,5.07225,59.303777777778
IC1064,,

Full-scale & binary files (run it on the complete dataset): fetch-full.sh · full.json.