NATS UK Airspace Data API Documentation¶
Overview¶
NATS (National Air Traffic Services) provides authoritative UK airspace datasets through the European AIS Database (EAD). These datasets contain detailed airspace structure information including controlled airspace zones, flight restriction zones (FRZ), runway protection zones (RPZ), and other airspace classifications essential for drone flight planning.
Data Source: NATS UK - EAD (European AIS Database)
Download URL Pattern: https://nats-uk.ead-it.com/cms-nats/export/sites/default/en/Publications/digital-datasets/EG_UAS_FR_DS_AREA1_FULL_YYYYMMDD_XML.zip
Format: AIXM 5.1 (Aeronautical Information Exchange Model) XML
Update Frequency: Every 28 days (AIRAC cycle)
Coverage: Entire UK territory
File Size: ~5-10 MB (compressed ZIP), ~15-30 MB (extracted XML)
Purpose in Drone Operations Application¶
We use NATS airspace data to: 1. Automatically classify flight locations by airspace class (A-G) based on geographic coordinates 2. Warn users about controlled airspace (Classes A-D) that requires special permissions 3. Pre-populate viability study forms with accurate airspace information 4. Ensure regulatory compliance by identifying FRZ and RPZ zones 5. Replace manual airspace lookup with automated point-in-polygon spatial queries
This builds on the existing Rural/Urban classification by adding airspace regulatory context at the selected location.
1. NATS UK Airspace Datasets¶
Dataset Overview¶
Provider: NATS UK (National Air Traffic Services) Host: European AIS Database (EAD) - nats-uk.ead-it.com Format: AIXM 5.1 XML (Aeronautical Information Exchange Model) Coordinate System: WGS84 (EPSG:4326) Spatial Data: Polygon and Circle geometries defining 3D airspace volumes
What is AIXM?¶
AIXM (Aeronautical Information Exchange Model) is an international standard for aeronautical data exchange developed by the FAA and EUROCONTROL. It's XML-based and defines schemas for representing airspaces, routes, procedures, and navigation aids.
Version 5.1 is the current standard used by NATS UK for UAS (Unmanned Aircraft Systems) datasets.
What is AIRAC?¶
AIRAC (Aeronautical Information Regulation And Control) is an international system for coordinated aeronautical information updates. Changes to airspace structures, procedures, and navigation data are published on a 28-day cycle to ensure global synchronization.
AIRAC Cycle Characteristics:
- Base Date: 2024-01-25 (used for calculation)
- Cycle Length: Exactly 28 days
- Calculation Formula: base_date + (28 × cycle_number)
- Current Cycle: Calculated dynamically based on today's date
- Publication Timing: New datasets published on AIRAC effective dates
Example AIRAC Dates (2025):
2025-01-23 → Cycle 2501
2025-02-20 → Cycle 2502
2025-03-20 → Cycle 2503
2025-04-17 → Cycle 2504
Dataset Download URL Pattern¶
Structure:
https://nats-uk.ead-it.com/cms-nats/export/sites/default/en/Publications/digital-datasets/EG_UAS_FR_DS_AREA1_FULL_{AIRAC_DATE}_XML.zip
Filename Components:
- EG - ICAO country code for United Kingdom
- UAS - Unmanned Aircraft Systems dataset
- FR - Flight Restriction
- DS - Dataset
- AREA1 - Geographic area (full UK coverage)
- FULL - Full dataset (not incremental)
- {AIRAC_DATE} - YYYYMMDD format (e.g., 20250123)
- XML - AIXM XML format
Example URL:
https://nats-uk.ead-it.com/cms-nats/export/sites/default/en/Publications/digital-datasets/EG_UAS_FR_DS_AREA1_FULL_20250123_XML.zip
Dataset File Structure¶
ZIP Archive Contents:
EG_UAS_FR_DS_AREA1_FULL_20250123_XML.zip
└── EG_UAS_FR_DS_AREA1_FULL_20250123.xml (AIXM 5.1 XML file)
Extracted XML Size: 15-30 MB Airspace Volume Count: ~800-900 volumes Typical Volume Types: - FRZ (Flight Restriction Zones): ~100-120 - RPZ (Runway Protection Zones): ~500-550 - CTR (Control Zones): ~50-70 - TMA (Terminal Maneuvering Areas): ~30-50 - Other designated airspaces: ~100-200
2. AIXM 5.1 XML Structure¶
Namespace Declarations¶
AIXM XML uses multiple namespaces for different data types:
<AIXM-Snapshot xmlns="http://www.aixm.aero/schema/5.1/message"
xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:aixm="http://www.aixm.aero/schema/5.1">
Key Namespaces:
- aixm: Core aeronautical data elements
- gml: Geography Markup Language (geometry definitions)
- xlink: XML linking (references between elements)
Airspace Element Structure¶
Basic Hierarchy:
AIXM-Snapshot
└── hasMember
└── Airspace
├── timeSlice
│ └── AirspaceTimeSlice
│ ├── designator (name)
│ ├── type (designation: FRZ, RPZ, CTR, etc.)
│ ├── designatorICAO
│ └── geometryComponent
│ └── AirspaceGeometryComponent
│ ├── operation (BASE, UNION, etc.)
│ ├── theAirspaceVolume
│ │ └── AirspaceVolume
│ │ ├── upperLimit
│ │ ├── upperLimitReference
│ │ ├── lowerLimit
│ │ ├── lowerLimitReference
│ │ └── horizontalProjection
│ │ └── Surface
│ │ └── patches
│ │ └── PolygonPatch / CircleByCenterPoint
Example: FRZ Airspace Volume¶
<aixm:Airspace gml:id="FRZ_LONDON_HEATHROW_RWY_27L">
<gml:identifier codeSpace="urn:uuid:">12345678-abcd-1234-5678-abcdefghijkl</gml:identifier>
<aixm:timeSlice>
<aixm:AirspaceTimeSlice gml:id="FRZ_LONDON_HEATHROW_RWY_27L_TS">
<gml:validTime>
<gml:TimePeriod>
<gml:beginPosition>2025-01-23T00:00:00Z</gml:beginPosition>
<gml:endPosition indeterminatePosition="unknown"/>
</gml:TimePeriod>
</gml:validTime>
<aixm:designator>LONDON HEATHROW RWY 27L</aixm:designator>
<aixm:type>FRZ</aixm:type>
<aixm:designatorICAO>EGLL</aixm:designatorICAO>
<aixm:class>D</aixm:class>
<aixm:geometryComponent>
<aixm:AirspaceGeometryComponent gml:id="GEOM_1">
<aixm:operation>BASE</aixm:operation>
<aixm:theAirspaceVolume>
<aixm:AirspaceVolume gml:id="VOL_1">
<!-- Vertical Limits -->
<aixm:upperLimit uom="FT">2000</aixm:upperLimit>
<aixm:upperLimitReference>SFC</aixm:upperLimitReference>
<aixm:lowerLimit uom="FT">0</aixm:lowerLimit>
<aixm:lowerLimitReference>SFC</aixm:lowerLimitReference>
<!-- Horizontal Geometry -->
<aixm:horizontalProjection>
<aixm:Surface gml:id="SURF_1" srsName="urn:ogc:def:crs:EPSG::4326">
<gml:patches>
<gml:PolygonPatch>
<gml:exterior>
<gml:LinearRing>
<gml:posList>
51.4700 -0.4900
51.4700 -0.4500
51.4500 -0.4500
51.4500 -0.4900
51.4700 -0.4900
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:PolygonPatch>
</gml:patches>
</aixm:Surface>
</aixm:horizontalProjection>
</aixm:AirspaceVolume>
</aixm:theAirspaceVolume>
</aixm:AirspaceGeometryComponent>
</aixm:geometryComponent>
</aixm:AirspaceTimeSlice>
</aixm:timeSlice>
</aixm:Airspace>
Geometry Types¶
1. Polygon Geometry¶
Element: <gml:PolygonPatch>
Coordinates: <gml:posList> - Space-separated lat/lon pairs
Format: lat1 lon1 lat2 lon2 lat3 lon3 ...
Closure: First and last points must be identical
Example:
<gml:PolygonPatch>
<gml:exterior>
<gml:LinearRing>
<gml:posList>
51.4700 -0.4900
51.4700 -0.4500
51.4500 -0.4500
51.4500 -0.4900
51.4700 -0.4900
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:PolygonPatch>
2. Circle Geometry¶
Element: <gml:CircleByCenterPoint>
Centre Point: <gml:pos> - Single lat/lon coordinate
Radius: <gml:radius> with uom attribute (NM or KM)
Conversion: Circle converted to polygon with 64 points for spatial queries
Example:
<gml:CircleByCenterPoint numArc="64">
<gml:pos>51.4700 -0.4543</gml:pos>
<gml:radius uom="NM">2.5</gml:radius>
</gml:CircleByCenterPoint>
Radius Unit Conversion:
- NM, nmi_i, [nmi_i] (Nautical Miles International): 1 NM = 1.852 km
- KM (Kilometers): 1 KM = 1.000 km
Note: Many FRZ zones (e.g., Edinburgh, Heathrow) use CircleByCenterPoint with nmi_i units. The parser converts these circles to 64-point polygons for efficient spatial queries.
Vertical Limits¶
Elements:
- <aixm:upperLimit> - Top altitude of airspace volume
- <aixm:lowerLimit> - Bottom altitude of airspace volume
- <aixm:upperLimitReference> - Reference datum (SFC, MSL, etc.)
- <aixm:lowerLimitReference> - Reference datum
Common Reference Datums:
- SFC - Surface (ground level)
- MSL - Mean Sea Level
- STD - Standard pressure altitude (QNE)
Units of Measurement (uom attribute):
- FT - Feet (most common in UK)
- M - Meters
- FL - Flight Level (100s of feet at standard pressure)
Examples:
<!-- Ground to 2000 feet -->
<aixm:lowerLimit uom="FT">0</aixm:lowerLimit>
<aixm:lowerLimitReference>SFC</aixm:lowerLimitReference>
<aixm:upperLimit uom="FT">2000</aixm:upperLimit>
<aixm:upperLimitReference>SFC</aixm:upperLimitReference>
<!-- 1500 feet MSL to 5000 feet MSL -->
<aixm:lowerLimit uom="FT">1500</aixm:lowerLimit>
<aixm:lowerLimitReference>MSL</aixm:lowerLimitReference>
<aixm:upperLimit uom="FT">5000</aixm:upperLimit>
<aixm:upperLimitReference>MSL</aixm:upperLimitReference>
3. UK Airspace Classification System¶
Airspace Classes A-G¶
| Class | Type | Description | Drone Operations | UK Examples |
|---|---|---|---|---|
| A | Controlled | IFR only, ATC clearance required | Prohibited without special authorisation | Airways above FL245 |
| B | Controlled | IFR and VFR, ATC clearance required | Prohibited without special authorisation | Not commonly used in UK |
| C | Controlled | IFR and VFR, ATC clearance required | Prohibited without special authorisation | Some terminal areas |
| D | Controlled | IFR and VFR, ATC clearance required | Restricted - permission required | Major airports (CTR, TMA) |
| E | Controlled | IFR separation provided, VFR requires radio | Restricted - permission may be required | Airways above controlled airspace |
| F | Uncontrolled | Advisory service only | Permitted with restrictions | Not used in UK |
| G | Uncontrolled | Basic flight information service | Permitted (up to 400ft AGL) | Most UK airspace below controlled zones |
Controlled vs Uncontrolled Airspace¶
Controlled Airspace (Classes A-D): - Requires ATC (Air Traffic Control) clearance or permission - Drone operations generally prohibited or heavily restricted - Includes FRZ, RPZ, CTR, TMA designations - Application Impact: Display warning, recommend seeking permissions
Uncontrolled Airspace (Classes E, G): - No ATC clearance required (though other restrictions may apply) - Class G is standard for drone operations below 400ft AGL - Application Impact: No special warning, standard drone rules apply
Airspace Priority (Most to Least Restrictive)¶
When multiple airspace volumes overlap at a location, the most restrictive class applies:
A > B > C > D > E > G > Unknown
Implementation Logic:
CLASS_PRIORITY = {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'G': 6, 'Unknown': 7}
most_restrictive = min(matches, key=lambda v: CLASS_PRIORITY.get(v.classification, 999))
4. Airspace Designation Types¶
FRZ (Flight Restriction Zone)¶
Purpose: Areas where flight is restricted or prohibited for safety/security Classification: Usually Class D UK Examples: Airport approach/departure paths, runway protection Drone Impact: Prohibited - Requires specific permission
RPZ (Runway Protection Zone)¶
Purpose: Zones protecting runway approaches and takeoff paths Classification: Usually Class D Vertical Limits: Typically surface to 2000 feet UK Examples: Heathrow RWY 27L, Gatwick RWY 26L Drone Impact: Prohibited - No drone operations permitted
CTR (Control Zone)¶
Purpose: Controlled airspace around airports Classification: Usually Class D (sometimes C) Vertical Limits: Surface to specified altitude (often 2000-3000 feet) UK Examples: London Heathrow CTR, Manchester CTR Drone Impact: Restricted - Permission required from ATC
TMA (Terminal Maneuvering Area)¶
Purpose: Controlled airspace for aircraft arriving/departing multiple airports Classification: Usually Class D or E Vertical Limits: Typically 1500 feet to FL100 (10,000 feet) UK Examples: London TMA (covers multiple airports) Drone Impact: Restricted - Permission may be required depending on altitude
CTA (Control Area)¶
Purpose: Controlled airspace above control zones Classification: Usually Class D or E Vertical Limits: Above CTR up to higher flight levels Drone Impact: Usually No Impact (drones operate below these altitudes)
5. Spatial Query Methodology¶
Point-in-Polygon Algorithm¶
We use Shapely (Python geospatial library) for spatial operations:
Process:
1. Parse AIXM XML to extract polygon coordinates
2. Create Shapely Polygon or Point objects
3. Use polygon.contains(point) for point-in-polygon test
4. Return all airspace volumes containing the query point
Coordinate System: WGS84 (EPSG:4326) - Standard latitude/longitude
Example Python Code:
from shapely.geometry import Point, Polygon
# Query location
query_point = Point(-0.4543, 51.4700) # Heathrow (lon, lat)
# Airspace polygon
coords = [
(-0.49, 51.47), (-0.45, 51.47),
(-0.45, 51.45), (-0.49, 51.45),
(-0.49, 51.47)
]
airspace_polygon = Polygon(coords)
# Spatial query
is_inside = airspace_polygon.contains(query_point) # True if inside
Altitude Considerations¶
Query Altitude: 400 feet AGL (Above Ground Level) Rationale: UK drone regulations limit maximum altitude to 400 feet AGL
Vertical Filtering:
- Only consider airspace volumes where lowerLimit <= 400 feet <= upperLimit
- Convert all limits to common unit (feet)
- Handle different reference datums (SFC, MSL)
Simplified Approach:
- For drone operations, assume ground level = sea level (conservative)
- Query at 400 feet above surface
- Exclude airspace volumes with lowerLimit > 400 feet
PreparedGeometry Optimization¶
For performance with large datasets (~800 volumes):
from shapely.prepared import prep
# Prepare geometries once
prepared_geometries = [prep(volume.geometry) for volume in volumes]
# Fast contains() checks
matches = [vol for prep_geom, vol in zip(prepared_geometries, volumes)
if prep_geom.contains(query_point)]
Performance Impact: - Unprepared: ~200-300ms for 800 volumes - Prepared: ~50-100ms for 800 volumes - 3-4x speedup for repeated queries
6. Python Implementation¶
Parsing AIXM XML¶
Libraries:
- lxml - Fast XML parsing with namespace support
- shapely - Spatial geometry operations
- pyproj - Coordinate transformations (if needed)
Example Parser:
from lxml import etree
from shapely.geometry import Polygon, Point
# Define namespaces
NAMESPACES = {
'aixm': 'http://www.aixm.aero/schema/5.1',
'gml': 'http://www.opengis.net/gml/3.2'
}
def parse_aixm_xml(xml_path):
"""Parse AIXM XML and extract airspace volumes"""
tree = etree.parse(xml_path)
root = tree.getroot()
volumes = []
# Find all Airspace elements
for airspace in root.findall('.//aixm:Airspace', NAMESPACES):
timeslice = airspace.find('.//aixm:AirspaceTimeSlice', NAMESPACES)
# Extract attributes
name = timeslice.findtext('.//aixm:designator', namespaces=NAMESPACES)
designation = timeslice.findtext('.//aixm:type', namespaces=NAMESPACES)
classification = timeslice.findtext('.//aixm:class', namespaces=NAMESPACES)
# Extract vertical limits
lower = timeslice.findtext('.//aixm:lowerLimit', namespaces=NAMESPACES)
upper = timeslice.findtext('.//aixm:upperLimit', namespaces=NAMESPACES)
# Extract geometry
poslist = timeslice.find('.//gml:posList', NAMESPACES)
if poslist is not None:
coords_text = poslist.text.strip().split()
coords = [(float(coords_text[i+1]), float(coords_text[i]))
for i in range(0, len(coords_text), 2)]
geometry = Polygon(coords)
volumes.append({
'name': name,
'designation': designation,
'classification': classification,
'lower_limit': lower,
'upper_limit': upper,
'geometry': geometry
})
return volumes
Spatial Query Example¶
def get_classification(lat, lon, altitude_ft=400):
"""
Classify airspace at given coordinates
Args:
lat: Latitude (WGS84)
lon: Longitude (WGS84)
altitude_ft: Query altitude in feet AGL (default 400)
Returns:
dict: Classification result with airspace class, name, controlled status
"""
# Load airspace volumes from cache
volumes = load_cache()
# Create query point
query_point = Point(lon, lat)
# Find matching volumes
matches = []
for volume in volumes:
# Check if point is inside horizontal geometry
if volume.geometry.contains(query_point):
# Check vertical limits (simplified)
lower = parse_altitude(volume.lower_limit)
upper = parse_altitude(volume.upper_limit)
if lower <= altitude_ft <= upper:
matches.append(volume)
# No matches = Class G (uncontrolled)
if not matches:
return {
'success': True,
'airspace_class': 'G',
'airspace_name': 'Uncontrolled Airspace',
'controlled': False
}
# Find most restrictive airspace
most_restrictive = min(matches, key=lambda v: CLASS_PRIORITY[v.classification])
return {
'success': True,
'airspace_class': most_restrictive.classification,
'airspace_name': most_restrictive.name,
'airspace_designation': most_restrictive.designation,
'controlled': most_restrictive.classification in ['A', 'B', 'C', 'D'],
'warning': f'Controlled airspace - {most_restrictive.designation} zone. Permissions required.'
if most_restrictive.classification in ['A', 'B', 'C', 'D'] else None
}
7. Caching Strategy¶
Three-Tier Cache¶
Tier 1: ZIP Archive
- Location: airspace_cache/downloads/
- Filename: EG_UAS_FR_DS_AREA1_FULL_{AIRAC_DATE}_XML.zip
- Purpose: Original download for re-parsing if needed
Tier 2: Extracted XML
- Location: airspace_cache/xml/
- Filename: EG_UAS_FR_DS_AREA1_FULL_{AIRAC_DATE}.xml
- Purpose: Ready for parsing without re-extraction
Tier 3: Serialized JSON
- Location: airspace_cache/airspace_cache.json
- Purpose: Fast loading (~1-2 seconds vs ~10-15 seconds for XML parsing)
- Structure:
{
"airac_date": "20250123",
"generated_at": "2025-01-23T10:00:00Z",
"volume_count": 827,
"volumes": [
{
"name": "LONDON HEATHROW RWY 27L",
"designation": "RPZ",
"classification": "D",
"lower_limit": "0 FT SFC",
"upper_limit": "2000 FT SFC",
"geometry": {"type": "Polygon", "coordinates": [[...]]}
}
]
}
Cache Validation¶
Checks Performed:
1. File Existence: Does airspace_cache.json exist?
2. AIRAC Currency: Is cached AIRAC date within 28 days?
3. Data Integrity: Can JSON be parsed? Are geometries valid?
Stale Cache Handling: - If cache is >28 days old but new data unavailable: Use stale cache (better than no data) - Log warning about stale data - Display cache age in admin interface
8. Example Test Locations¶
1. London Heathrow Airport¶
Coordinates: 51.4700, -0.4543 Expected Classification: Class D (or Class A CTR) Airspace Name: LONDON HEATHROW CTR or HEATHROW FRZ/RPZ Designation: CTR, FRZ, or RPZ Controlled: Yes Warning: Controlled airspace - Permissions required
2. Central London¶
Coordinates: 51.5074, -0.1278 Expected Classification: Class D Airspace Name: LONDON TMA (Terminal Maneuvering Area) Designation: TMA Controlled: Yes Warning: Controlled airspace - Permission may be required
3. Scottish Highlands (Remote)¶
Coordinates: 57.5000, -5.0000 Expected Classification: Class G Airspace Name: Uncontrolled Airspace Designation: N/A Controlled: No Warning: None
4. Edinburgh Airport / FRZ¶
Coordinates: 55.9500, -3.3725 Expected Classification: Class D Airspace Name: EDINBURGH Designation: FRZ (Flight Restriction Zone) Geometry: CircleByCenterPoint with 2.5 NM radius Controlled: Yes Warning: Controlled airspace - Permissions required
Note: Edinburgh FRZ is a circular zone covering the general airport area. Additionally, there are two RPZ (Runway Protection Zones) for RWY 06 and RWY 24.
Related Test Location - Cammo Nature Reserve: - Coordinates: 55.9574, -3.3232 - Expected: Class D FRZ (within Edinburgh FRZ circle) - Use: Validates CircleByCenterPoint geometry handling
5. Manchester Airport¶
Coordinates: 53.3537, -2.2750 Expected Classification: Class D Airspace Name: MANCHESTER CTR Designation: CTR Controlled: Yes Warning: Controlled airspace - Permissions required
9. Error Handling¶
Scenarios and Responses¶
| Scenario | Response | User Impact |
|---|---|---|
| No cache exists (first run) | Attempt download; fallback to "Unknown" if fails | Classification shows "Unknown" |
| NATS server unreachable | Use stale cache (even if >28 days old) | May have outdated airspace data |
| XML parsing errors | Skip problematic volumes, log warnings | Some airspace zones may be missing |
| Invalid geometries | Skip malformed polygons, continue parsing | Some zones missing from queries |
| Query outside UK | Return Class G (uncontrolled) | Assume uncontrolled for non-UK |
| Cache corruption | Delete cache, attempt re-download | Temporary "Unknown" classification |
Logging Levels¶
INFO: Normal operations
INFO: Airspace cache loaded successfully (827 volumes)
INFO: AIRAC cycle 2501 (2025-01-23) loaded
WARNING: Degraded functionality
WARNING: Using stale cache (34 days old)
WARNING: Skipped 3 volumes with invalid geometries
ERROR: Significant failures
ERROR: Failed to download NATS dataset: HTTP 404
ERROR: XML parsing failed: Invalid AIXM structure
CRITICAL: Data integrity issues
CRITICAL: Cache file corrupted - unable to parse JSON
10. References¶
Official Documentation¶
- NATS UK Digital Datasets: nats-uk.ead-it.com
- AIXM Specification: aixm.aero
- AIRAC Cycle Information: EUROCONTROL AIS
- UK Airspace Regulations: CAA CAP 722 (UAS Operations)
Technical Standards¶
- EPSG:4326: WGS84 Geographic Coordinate System
- GML 3.2: Geography Markup Language
- OGC Standards: Open Geospatial Consortium
Python Libraries¶
- lxml: lxml.de - XML processing with namespaces
- Shapely: shapely.readthedocs.io - Spatial geometry operations
- pyproj: pyproj4.github.io - Coordinate transformations
Version History¶
Version 1.1 - 2025-12-29
- Added CircleByCenterPoint geometry support for FRZ zones
- Fixed bug where general airport FRZ zones were not detected (only RPZ zones)
- Added support for nmi_i and [nmi_i] nautical mile units
- Updated Edinburgh example to reflect FRZ (not CTR) with circular geometry
- Added Cammo Nature Reserve test location example
Version 1.0 - 2025-01-15 Initial documentation for NATS UK airspace classification integration