OpenNGC Sexagesimal Coordinates → Decimal Degrees¶
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 againstsample.csv; show all turns the whole catalogue into degrees. - Dec → degrees: capture the sign once with
STARTS_WITH('-'), then apply it to the magnitudeABS(deg) + min/60 + sec/3600so the sign covers the whole value, not just the degrees field. - Both guarded with
ISEMPTY(...)so blank-coordNonExrows stay empty instead of computing to0(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.