DotNet.Document.Systems and regions - Elite-Dangerous-Almanac/Almanac-Core GitHub Wiki
.NET / Systems and regions
Elite Dangerous uses the word "region" for four different things, and uses two
three-number coordinate shapes that are easy to confuse. This guide separates them, and
covers the id64 round trip that most tools need first.
ProceduralSystem is the handle that ties a
procedural name to its id64 and back.
using EliteDangerousAlmanac.Astronomy;
ProceduralSystem system = ProceduralSystem.FromName("Synuefe EN-H d11-96")!;
ulong address = system.SystemAddress; // 3309179996515
string region = system.NamingRegionName; // "Synuefe"
char code = system.MassCode; // 'd'
string name = ProceduralSystem.FromSystemAddress(address).Name; // "Synuefe EN-H d11-96"FromName answers null for a name that is not procedural. Sol, Shinrarta Dezhra
and every other hand-named system fall outside this type by design, so null means "this
is hand-named", not "something went wrong". The two factories differ deliberately: a name
that does not parse is an ordinary answer, while an address whose grid slot has no
procedural name raises ArgumentOutOfRangeException.
An address is a ulong, which is the range the game's own id64 occupies, so no address
needs a wider type or a conversion. A decimal string — a query parameter, a configuration
file, a column read as text — becomes one through
SystemAddress.
using EliteDangerousAlmanac.Astronomy;
ulong address = SystemAddress.Parse("3309179996515");
SystemAddress.TryParse("not an address", out ulong _); // false, for input you do not controlTo take an address apart without building a whole system:
using EliteDangerousAlmanac.Astronomy;
DecodedSystemAddress decoded = SystemAddress.Decode(3309179996515);
int sizeClass = decoded.SizeClass; // 3
long sequence = decoded.Sequence; // 96
SectorGridPosition grid = decoded.SectorGridPosition; // (39, 31, 18)Both are three numbers. The type names are what stop you mixing them up.
-
GalacticPosition is
X,Y,Zin light-years, Sol at the origin. This is what the journal'sStarPos, EDSM and Spansh give you. -
SectorGridPosition is
SectorX,SectorY,SectorZ— integer indices on the naming grid, whose cell edge isGalaxyGrid.SectorEdgeLy.
using EliteDangerousAlmanac.Astronomy;
GalacticPosition position = new(-81.625, -151.3125, -376.0625); // light-years
SectorGridPosition grid = GalaxyGrid.ToSectorGridPosition(position); // (38, 31, 18)
string sector = SectorName.FromGridPosition(grid); // "Synuefai"If you are starting from a real position and only want the name, go straight there with
GalaxyGrid.ToSectorName rather than converting by hand.
Answered here for one position — the Pleiades. They disagree, which is the point: each names a different thing.
using EliteDangerousAlmanac.Astronomy;
GalacticPosition position = new(-81.625, -151.3125, -376.0625);
string sector = GalaxyGrid.ToSectorName(position); // "Synuefai" procedural sector
string? region = HandAuthoredRegions.FindAt(position)?.Name; // "Pleiades Sector" hand-authored
string? codex = CodexRegionMap.FindAt(position)?.Name; // "Inner Orion Spur" codex region
double? corner = NamingRegionOrigins.Resolve("Synuefai")?.X; // 1556480 naming-region origin| Concept | What it is | Entry point |
|---|---|---|
| Procedural sector | The boxel-grid name a system inherits |
GalaxyGrid.ToSectorName, SectorName.FromGridPosition
|
| Naming-region origin | A sector's corner, used to encode id64
|
NamingRegionOrigins.Resolve |
| Hand-authored region | Pleiades, Coalsack, … — named by Frontier | HandAuthoredRegions.FindAt |
| Codex region | One of the galactic codex zones, numbered 1 to 42 |
CodexRegionMap.FindAt, CodexRegionMap.FindForBoxel
|
CodexRegionMap.FindAt reads only the X and Z axes, because the codex map is an X/Z
projection. Its overload taking a GalacticPosition accepts one as it comes and ignores
the Y; the overload taking a
GalacticPlanePosition says the same
thing in the type.
CodexRegion.Name is the canonical English name. To show the region in the player's
language, pass the identifier to
DisplayText.CodexRegionName,
which reads a separate catalogue so a region lookup never loads six languages:
using EliteDangerousAlmanac.Astronomy;
using EliteDangerousAlmanac.Localization;
GalacticPosition position = new(-81.625, -151.3125, -376.0625);
CodexRegion? region = CodexRegionMap.FindAt(position);
string? english = region?.Name; // "Inner Orion Spur"
string? french = DisplayText.CodexRegionName(region?.Id ?? 0, "fr"); // "Boucle d'Orion interne"The catalogue is keyed by the identifier and not by the name, so a corrected English name
cannot orphan its translations. It is complete in all six languages, and it stores the
game's own value where the game leaves a region untranslated:
DisplayText.CodexRegionName(18, "ru") is "Inner Orion Spur" because that is what the
game shows, not because the lookup fell back to English.
A system inside a hand-authored region takes that region's name instead of the procedural
sector's — ProceduralSystem.UsesHandAuthoredRegion tells you which happened. That is
also why FromSystemAddress takes an optional position: an address states the boxel and
not the place, so without a position such a system quietly carries its procedural name.
The nebula catalogue answers "what is near here", not "what is this called".
using EliteDangerousAlmanac.Astronomy;
GalacticPosition position = new(-81.625, -151.3125, -376.0625);
foreach (NebulaNearby near in NebulaCatalogue.Nearest(position, NebulaCatalogue.Real, 2))
{
// near.Name is "Pleiades", then "Taurus Dark Region";
// near.DistanceLy is how far it is from the position.
}
int within100Ly = NebulaCatalogue.Within(position, NebulaCatalogue.Real, 100).Count;The catalogue argument is required, not defaulted. Nearly all of
NebulaCatalogue.All is planetary nebulae that most callers never touch, so there is no
one catalogue worth reading without being asked to. NebulaCatalogue.Real is the small,
human-recognisable slice, and it is the one most callers want.
Two kinds, one lookup, from a name alone.
using EliteDangerousAlmanac.Astronomy;
PermitLockKind? sol = PermitLocks.ForSystemName("Sol")?.Kind; // System, individually locked
PermitLockKind? col =
PermitLocks.ForSystemName("Col 70 Sector AB-C d1-23")?.Kind; // Region, the region is locked
PermitLock? open = PermitLocks.ForSystemName("Synuefe EN-H d11-96"); // null, not lockedThere are narrower lookups beneath it — by address, by region name, and the boolean forms
— but start here: it is the only one that answers for both kinds without you knowing in
advance which applies. A system that carries a lock of its own and also sits inside a
locked region answers with its own. ProceduralSystem.RequiresRegionPermit is a
region-level flag only; individually locked systems are hand-named and so never reach a
ProceduralSystem at all.