Skip to main content

Best Practices

SafeSky UAV API - Integration Guide

This guide provides architectural patterns and best practices for integrating the SafeSky UAV API into mission control software, moving-map applications, and operational dashboards.

Overview

The SafeSky API enables developers to build production-grade air traffic awareness systems that display real-time positions of UAVs, helicopters, recreational aviation, general aviation aircraft, and commercial traffic.

This guide addresses the technical challenges of building responsive, scalable traffic layers for moving map applications: dynamic mapping interfaces where traffic must be rendered, updated, and managed in real-time as users interact with the map (panning, zooming, following UAV positions).

Key integration challenges solved:

  • Efficient viewport-based traffic queries that adapt to map interactions
  • Smooth traffic rendering with position extrapolation between updates
  • Layer management and z-index handling for mixed traffic types
  • Performance optimisation for high-traffic-density areas
  • Coordinate system handling and map projection compatibility

Reference Implementation: https://live.safesky.app

Key Design Goals
  • Real-time traffic awareness with minimal latency
  • Smooth, professional rendering on dynamic maps
  • Efficient resource utilization for fleet operations
  • Compliance with aviation best practices

1. Integration Architecture

Dual-Thread Pattern

Implement traffic retrieval and telemetry publishing as independent, asynchronous processes. This separation is critical for operational reliability.

┌─────────────────────┐      ┌─────────────────────┐
│  Traffic Retrieval  │      │   UAV Telemetry     │
│      Thread         │      │  Publisher Thread   │
├─────────────────────┤      ├─────────────────────┤
│ GET /v1/uav         │      │ POST /v1/uav        │
│ Viewport queries    │      │ Position updates    │
│ 3-second polling    │      │ 1Hz transmission    │
└─────────────────────┘      └─────────────────────┘
         ↓                            ↓
    ┌────────────────────────────────────┐
    │      Application State/Map         │
    └────────────────────────────────────┘

Architectural Benefits:

  • Fault isolation: Network issues in one thread don't cascade to the other
  • Independent scaling: Adjust polling and publishing rates separately
  • Simplified error handling: Each thread manages its own retry logic
  • Resource optimisation: Non-blocking I/O prevents thread starvation

Implementation Note: In single-threaded environments (e.g., JavaScript), use separate async tasks or workers to achieve the same isolation.

JavaScript Example:

// Independent traffic retrieval (3-second polling)
async function pollTraffic() {
  setInterval(async () => {
    try {
      const viewport = getMapViewportWithOverscan();
      const traffic = await fetchTraffic(viewport);
      updateTrafficLayer(traffic);
    } catch (error) {
      console.error('Traffic retrieval error:', error);
    }
  }, 3000);
}

// Independent telemetry publishing (1Hz)
async function publishUAVTelemetry(uavList) {
  setInterval(async () => {
    try {
      await postTelemetry(uavList);
    } catch (error) {
      console.error('Publish error:', error);
    }
  }, 1000);
}

2. Traffic Retrieval Strategy

Efficient traffic retrieval is essential for a responsive UI and predictable backend load. The SafeSky UAV API supports two complementary ways to retrieve surrounding traffic using the same endpoint (GET /v1/uav):

  • Viewport Query (bounding box): best for map rendering (what’s visible on screen).
  • Radius Query (circle around a point): best for proximity logic (alerts, local awareness around a UAV/operator).

Choose the strategy based on how your application defines the “area of interest”.

2.1 Viewport-Based Queries

For map-based applications, use bounding box queries that align with the visible viewport:

GET /v1/uav?viewport=lat_min,lng_min,lat_max,lng_max

Example:

GET /v1/uav?viewport=48.8566,2.3522,48.8766,2.3922

Viewport queries are recommended for moving maps because they:

  • Match how mapping SDKs work (screen = rectangle)
  • Avoid fetching large off-screen traffic that won’t be rendered
  • Scale naturally with zoom level (smaller bounds when zoomed in, larger when zoomed out)
2.2 Radius Queries

Use radius queries when your logic is centred around a specific point (e.g., UAV position, operator location, mission centroid) and you want all traffic within a fixed distance, regardless of the current map viewport.

GET /v1/uav?lat=<latitude>&lng=<longitude>&rad=<meters>

Example:

GET /v1/uav?lat=48.8566&lng=2.3522&rad=10000

Radius queries are recommended when you need:

  • Proximity / safety alerting around a UAV (or operator) position
  • Backend checks independent of UI viewport (e.g., monitoring a mission corridor)
  • Applications without a map, or where map rendering is secondary

Trade-offs:

  • A radius query may return aircraft outside the currently visible map area (if you also render a map)
  • If used for rendering, you may want client-side filtering to prevent clutter outside the viewport

Technical Advantages of Viewport Query vs Radius Query:

Aspect Viewport Query Radius Query
Zoom adaptability Natural scaling with map bounds Fixed circular area
Data efficiency Fetches only visible region Often includes off-screen data
Rendering alignment 1:1 with visible area Requires post-filtering
Edge cases Handles rectangular screens correctly Circular overlap issues
2.3 Viewport Padding (Overscan)

In moving-map applications, the visible viewport changes continuously as the user pans, zooms, or as the own-ship position moves across the map. If traffic is queried only for the exact visible bounds, aircraft can appear abruptly at the screen edges or disappear momentarily during fast interactions.

To avoid this, extend the queried viewport 5–15% beyond the visible map area. This technique, commonly referred to as overscan, allows the client to pre-fetch traffic that is about to enter the screen.

This approach is widely used in aviation navigation displays and professional GIS systems to ensure smooth visual continuity.

Implementation Example:

const OVERSCAN_FACTOR = 0.10; // 10% padding

function getQueryViewport(mapBounds) {
  const latPadding = (mapBounds.north - mapBounds.south) * OVERSCAN_FACTOR;
  const lngPadding = (mapBounds.east - mapBounds.west) * OVERSCAN_FACTOR;
  
  return {
    lat_min: mapBounds.south - latPadding,
    lng_min: mapBounds.west - lngPadding,
    lat_max: mapBounds.north + latPadding,
    lng_max: mapBounds.east + lngPadding
  };
}

Why this matters for moving maps

  • Smooth panning: Aircraft already exist just outside the visible area, so panning the map does not trigger sudden icon appearances.
  • Predictable motion: Fast-moving aircraft (e.g. 150–250+ knots) remain visible and continuous between polling cycles.
  • Reduced visual jitter: Small map movements do not require immediate re-queries, improving UI stability.
  • Professional look & feel: Matches the behaviour users expect from aviation-grade navigation displays.

2.4 Polling Frequency

This frequency balances real-time awareness with bandwidth efficiency.

Network Optimisation:

  • Consider compression (gzip) for responses
  • Debounce rapid viewport changes (wait 300-500ms after user stops moving map)
  • Only query when viewport changes significantly (>20% of current bounds)

3. Understanding UAV Traffic Types

The SafeSky UAV API can return two different kinds of UAV-related traffic, and it is important to treat them differently in both your UI and your safety logic:

  • Live Traffic: real-time objects with a precise position (a tracked point).
  • Advisory Traffic: declared or expected UAV operation areas (an active zone with declared maximum altitude, not a track).

These two traffic types serve different operational purposes. Live traffic answers the question “Where is it right now?”, while advisory traffic answers “UAV activity is taking place in this area.”


Live Traffic vs. Advisory Traffic
Characteristic Live Traffic Advisory Traffic
What it represents Actively tracked aircraft or UAV Declared UAV operation area
Geometry Point (latitude / longitude) GeoJSON Polygon
Precision High (GPS / transponder based) Medium to low (area of activity)
Update frequency Typically every 1–5 seconds Typically ~1 minute
User interpretation “This object is here and moving” “UAV activity is occuring here”
Rendering model Aircraft/UAV icon with heading Area overlay (no heading)
Primary purpose Tactical awareness & deconfliction Strategic awareness & caution

3.1 Live Traffic

Live traffic represents aircraft and UAVs that are actively broadcasting telemetry, such as ADS-B, FLARM, ADS-L, SafeSky, or UAV telemetry. These objects should be treated as moving tracked targets.

Typical characteristics:

  • Precise latitude and longitude
  • Motion data such as ground_speed, course (and sometimes vertical_rate)
  • A recent last_update timestamp
  • A status such as AIRBORNE, GROUNDED, or INACTIVE
  • Optional accuracy information

Live traffic response model (example):

{
  "id": "UAV123",
  "latitude": 48.86584,
  "longitude": 2.63723,
  "beacon_type": "UAV",
  "call_sign": "FlyingFrog",
  "transponder_type": "ADS-B",
  "last_update": 1733412793,
  "altitude": 180,
  "course": 250,
  "ground_speed": 24,
  "status": "AIRBORNE",
  "accuracy": 10
}

Rendering guidance for live traffic:

  • Render as a point symbol (UAV icon).
  • Rotate the icon according to course.
  • Use smooth motion (extrapolation / dead reckoning) between polling cycles.
  • Apply data ageing rules (fade → uncertainty → removal) based on last_update.

3.2 Advisory Traffic (Operational Areas)

Advisory traffic represents declared UAV operations rather than tracked objects. It indicates areas where UAV activity is planned or ongoing, even when no live telemetry is available.

Advisory traffic is returned as a GeoJSON FeatureCollection, where each feature describes an operational area:

  • Polygon geometry: explicit area boundaries.
  • Point geometry + max_distance: centre point with an operational radius.

Advisory traffic response model (example):

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "id": "Advisory123",
        "call_sign": "UAV_Alpha",
        "last_update": 1738142598,
        "max_altitude": 150,
        "remarks": "Surveying area"
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [4.39201, 50.69378],
            [4.39300, 50.69400],
            [4.39400, 50.69500],
            [4.39201, 50.69378]
          ]
        ]
      }
    }
  ]
}

Rendering guidance for advisory traffic:

  • Render as an area overlay (polygon or circle).
  • Use semi-transparent fills to preserve map readability.
  • Display a label with call_sign (or ID) and max_altitude.
  • Do not render heading indicators, motion vectors, or animations.
  • Update less frequently than live traffic (advisories change slowly).

Key rule: advisory traffic is a risk / awareness zone, not a precise position.


3.3 Common Pitfalls
  • Do not extrapolate advisory areas. They are static operational zones.
  • Do not use identical colours or styles for live and advisory traffic.
  • Do not apply the same expiry timing to live and advisory data.

  • Display live traffic above advisory areas (higher z-index).
  • Use clearly distinct colours and shapes for each traffic type.
  • On user interaction:
    • Live traffic: show call sign, altitude, speed, last update, and source.
    • Advisory traffic: show operation name, max altitude, remarks, and last update.
GeoJSON Rendering Implementation

The following example demonstrates how to properly render advisory traffic from GeoJSON responses:

Implementation Example:

function renderPolygonAdvisory(advisoryGeoJSON) {
  const features = advisoryGeoJSON.features.map(feature => {
    const props = feature.properties;
    
    if (feature.geometry.type === 'Polygon') {
      // Convert GeoJSON coordinates to map projection format
      const coordinates = feature.geometry.coordinates[0].map(coord => 
        [coord[0], coord[1]] // [lng, lat]
      );
      
      // Render polygon with exact boundaries
      const polygon = new Polygon({
        coordinates: [coordinates],
        fill: 'rgba(255, 165, 0, 0.25)', // Semi-transparent orange
        stroke: 'rgb(255, 140, 0)',
        strokeWidth: 2
      });
      
      // Calculate polygon centroid for label placement
      const centroid = calculatePolygonCentroid(coordinates);
      
      const label = new Label({
        position: centroid,
        text: `${props.call_sign || props.id}\nMax Alt: ${props.max_altitude}m`,
        style: {
          backgroundColor: 'rgba(255, 255, 255, 0.8)',
          padding: '4px',
          borderRadius: '4px'
        }
      });
      
      return [polygon, label];
    } else if (feature.geometry.type === 'Point') {
      // Handle point-based advisory within GeoJSON
      const coords = feature.geometry.coordinates;
      const radius = props.max_distance || 500;
      
      const circle = new Circle({
        center: [coords[0], coords[1]],
        radius: radius,
        fill: 'rgba(255, 165, 0, 0.25)',
        stroke: 'rgb(255, 140, 0)',
        strokeWidth: 2
      });
      
      return circle;
    }
  }).flat();
  
  return features;
}

// Helper function for centroid calculation
function calculatePolygonCentroid(coordinates) {
  let x = 0, y = 0;
  coordinates.forEach(coord => {
    x += coord[0];
    y += coord[1];
  });
  return [x / coordinates.length, y / coordinates.length];
}

Rendering Best Practices:

  1. Coordinate Order: GeoJSON uses [longitude, latitude] order. Ensure your mapping library expects this format
  2. Closed Polygons: The first and last coordinates are identical in GeoJSON; some libraries auto-close, others don't
  3. Multi-Polygon Support: If advisories can have holes or multiple parts, handle MultiPolygon geometry type
  4. Simplification: For complex polygons with many vertices, consider simplification at high zoom levels for performance
  5. Label Placement: Position labels at the polygon centroid or largest interior point to avoid edge placement
  6. Point Geometry: When rendering Point-based advisories, use the max_distance property as the circle radius

Visual Distinction is Critical:

  • Users must immediately recognize advisory areas as zones of potential activity, not exact positions
  • Never render advisory traffic with precise aircraft icons that imply real-time tracking
  • Always show the operational radius/area visually
  • Use different colours/styles to distinguish from live traffic

User Experience Guidelines:

  • Hover/Click Info: Display call_sign, max_altitude, remarks (if available), and last_update timestamp
  • Opacity: Use semi-transparent fills (20-30% opacity) to avoid obscuring map features
  • Z-Index: Render advisory areas below live traffic to maintain priority
  • Update Frequency: Advisory areas change infrequently; avoid aggressive re-rendering
Example: Mixed Traffic Response

When querying traffic with GET /v1/uav, you receive an array of live traffic objects:

[
  {
    "latitude": 48.86584,
    "longitude": 2.63723,
    "beacon_type": "JET",
    "call_sign": "IBE06CK",
    "transponder_type": "ADS-B",
    "last_update": 1733412793,
    "altitude": 10554,
    "course": 205,
    "ground_speed": 237,
    "status": "AIRBORNE",
    "id": "3423C3"
  },
  {
    "id": "hx-137105",
    "latitude": 63.41303,
    "longitude": 10.11205,
    "beacon_type": "UAV",
    "accuracy": 1852,
    "call_sign": "HWX137105",
    "transponder_type": "ADVISORY",
    "last_update": 1766068287,
    "altitude": 231,
    "course": 0,
    "ground_speed": 0,
    "remarks": "Axel B\n97497815\nAviant",
    "operation_area": "{\"type\":\"Polygon\",\"coordinates\":[[[10.11204838,63.42966685],[10.10479224,63.42934699],[10.09781542,63.42839974],[10.09138643,63.42686155],[10.08575261,63.42479163],[10.08113055,63.42226962],[10.07769777,63.41939255],[10.07558594,63.41627108],[10.07487581,63.41302522],[10.07559419,63.40977973],[10.07771302,63.4066593],[10.08115047,63.4037838],[10.08577418,63.40126364],[10.09140636,63.39919557],[10.09783067,63.39765894],[10.10480049,63.39671274],[10.11204838,63.39639325],[10.11929626,63.39671274],[10.12626608,63.39765894],[10.1326904,63.39919557],[10.13832258,63.40126364],[10.14294628,63.4037838],[10.14638373,63.4066593],[10.14850256,63.40977973],[10.14922095,63.41302522],[10.14851081,63.41627108],[10.14639898,63.41939255],[10.14296621,63.42226962],[10.13834414,63.42479163],[10.13271032,63.42686155],[10.12628133,63.42839974],[10.11930452,63.42934699],[10.11204838,63.42966685]]]}",
    "status": "AIRBORNE"
  },
  {
    "id": "096E2863",
    "latitude": 59.17081,
    "longitude": 9.5268,
    "beacon_type": "HELICOPTER",
    "vertical_rate": 45,
    "accuracy": 0,
    "call_sign": "ROTORWING 30",
    "transponder_type": "ADS-BI",
    "last_update": 1766068285,
    "altitude": 450,
    "altitude_accuracy": 10,
    "course": 315,
    "ground_speed": 54,
    "status": "AIRBORNE"
  }
]

Note: Advisory traffic is retrieved separately as GeoJSON via the same endpoint when advisory areas are active in the queried region.


4. Publishing UAV Information

The SafeSky API provides two distinct publishing mechanisms depending on whether you have live telemetry or only planned operation areas.

4.1 Live UAV Telemetry Publishing

Endpoint: POST /v1/uav

Use this when you have real-time position data from active UAVs.

POST /v1/uav
Content-Type: application/json

Publishing Frequency: 1 second (1Hz)

4.2 Batch Publishing for Fleet Operations

When managing multiple UAVs, always use batch publishing by sending an array of telemetry objects in a single request.

Why Batch Publishing Matters:

For fleet operations, sending individual HTTP requests for each UAV creates unnecessary overhead. Consider a fleet of 10 UAVs publishing at 1Hz:

Approach UAVs Requests/sec HTTP Overhead Network Latency
Individual 10 10 ~4KB 10× RTT
Batch 10 1 ~400B 1× RTT

Benefits: Single HTTP handshake, one round-trip instead of N, essential for swarm operations (20+ UAVs), and reduced risk of throttling

Implementation Strategy:

  1. Centralized Collection: Aggregate all UAV telemetry in a central data structure before publishing
  2. Atomic Updates: Publish the entire fleet state in one transaction
  3. Consistent Timestamps: Ensure all telemetry in a batch shares the same or very close last_update timestamp
  4. Error Handling: If the batch fails, retry the entire batch rather than individual UAVs

When to Use Batch Publishing:

  • Managing 2+ UAVs simultaneously
  • Swarm or coordinated multi-UAV operations
  • Fleet management dashboards
  • Cloud-based mission control systems

When Individual Publishing is Acceptable:

  • Single UAV operations only
  • Independent UAV systems with no central coordination

Best Practice: Even for 2 UAVs, use batch publishing. It simplifies your architecture and scales effortlessly as your fleet grows.


4.2.1 Example: Batch UAV Publish Payload
[
  {
    "id": "UAV1",
    "latitude": 48.86584,
    "longitude": 2.63723,
    "altitude": 120,
    "course": 205,
    "ground_speed": 12,
    "status": "AIRBORNE",
    "last_update": 1733412793
  },
  {
    "id": "UAV2",
    "latitude": 48.87012,
    "longitude": 2.64188,
    "altitude": 115,
    "course": 210,
    "ground_speed": 10,
    "status": "AIRBORNE",
    "last_update": 1733412793
  }
]

4.3 Pre-Flight Visibility
4.3.1 GROUNDED Status Broadcasting

One important aspect of UAV safety is pre-flight visibility: announcing your UAV's presence before it takes off.

Why Pre-Flight Broadcasting Matters:

In aviation, situational awareness begins before takeoff. Manned aircraft pilots (especially helicopter pilots operating at low altitudes) need advance notice of UAV operations in their vicinity. Broadcasting your UAV's position while still on the ground:

  • Allows nearby pilots to adjust their flight paths proactively
  • Prevents last-minute evasive maneuvers during critical flight phases (takeoff/landing)
  • Establishes your operational intent in shared airspace
  • Provides time for radio communication if needed
  • Reduces collision risk during the vulnerable takeoff phase

GROUNDED Status Payload Example:

{
  "id": "UAV1",
  "latitude": 48.86584,
  "longitude": 2.63723,
  "altitude": 0,
  "status": "GROUNDED",
  "last_update": 1734537600
}

Key Payload Requirements:

  • status: Must be "GROUNDED" (not "INACTIVE")
  • altitude: Set to 0 or ground elevation
  • latitude/longitude: Exact takeoff location
  • last_update: Current timestamp
  • Continue publishing at 1Hz until takeoff

Timing Guidelines:

State Transition Workflow:

1. Power on UAV
   ↓
2. Start publishing: status = "GROUNDED"
   (3-5 minutes before takeoff)
   ↓
3. Conduct pre-flight checks
   (Continue publishing GROUNDED status)
   ↓
4. UAV lifts off
   ↓
5. Immediately switch: status = "AIRBORNE"
   (Within 1 second of takeoff)

Implementation Best Practices:

  1. Automate Based on Power-On: Start broadcasting GROUNDED status as soon as the UAV is powered and GPS lock is acquired
  2. Pre-Filed Flight Plans: If using mission planning software, automatically trigger GROUNDED broadcasting when flight plan is activated
  3. Visual Confirmation: In pilot interfaces, show a clear indicator that pre-flight broadcasting is active
  4. Delayed Takeoff Handling: If takeoff is delayed, continue broadcasting GROUNDED status. Do not stop or restart
  5. Multi-UAV Coordination: For fleet operations, stagger GROUNDED broadcasts if multiple UAVs are taking off from the same location

Critical Locations Requiring Pre-Flight Visibility:

  • Within 5 km of airports or helipads
  • Emergency service flight paths (air ambulance routes)
  • Near hospitals (frequent helicopter traffic)
  • Coastal areas (search and rescue operations)
  • Mountainous terrain (rescue helicopter activity)
  • Urban areas with general aviation traffic

Common Mistakes to Avoid:

  • Publishing only after takeoff (too late for nearby traffic)
  • Using status: "INACTIVE" instead of "GROUNDED"
  • Setting altitude to actual flight altitude while still on ground
  • Stopping broadcasts during pre-flight checks
  • Not updating position if UAV is moved before takeoff

5. Smooth Motion Rendering

In moving map applications, the quality of traffic animation directly impacts user confidence and situational awareness. Jerky or teleporting aircraft icons create confusion and undermine trust in the system's accuracy. Professional aviation displays maintain smooth, predictable motion even with infrequent updates.

This section addresses the challenge of rendering fluid 25fps animation when traffic data arrives only every 3 seconds. The solution is client-side position extrapolation (dead reckoning), a technique widely used in aviation navigation systems and air traffic control displays.

5.1 Client-Side Position Extrapolation

Traffic updates arrive at discrete 3-second intervals. To achieve smooth animation, implement dead reckoning between updates.

Algorithm:

function extrapolatePosition(aircraft, currentTime) {
  const elapsedSeconds = (currentTime - aircraft.last_update) / 1000;
  const distanceMeters = aircraft.ground_speed * elapsedSeconds;
  
  const deltaLat = distanceMeters * Math.cos(aircraft.course * Math.PI / 180) / 111320;
  const deltaLng = distanceMeters * Math.sin(aircraft.course * Math.PI / 180) / 
                   (111320 * Math.cos(aircraft.latitude * Math.PI / 180));
  
  return {
    latitude: aircraft.latitude + deltaLat,
    longitude: aircraft.longitude + deltaLng
  };
}

Animation Approach: Use requestAnimationFrame to call extrapolatePosition for each traffic marker at 25fps, creating smooth motion between 3-second API updates.

Performance Optimisation for Zoom Levels: At higher zoom levels (zoomed out, showing large geographical areas), animation frame rate can be progressively reduced:

  • Close zoom (city-level view): 25fps for smooth, professional motion
  • Medium zoom (regional view): 10-15fps provides adequate fluidity
  • Far zoom (country/continent view): 1fps (matching API update rate) is sufficient

At distant zoom levels, aircraft appear as small icons with minimal apparent motion, making high frame rates unnecessary. This optimisation significantly reduces CPU usage when displaying large numbers of aircraft.

Rendering Impact:

  • Transforms discrete position updates into fluid animation
  • Eliminates visual "jumping" or "teleporting" artifacts
  • Creates professional, aviation-grade map presentation
  • Improves user perception of traffic flow patterns
  • Scales efficiently across different zoom levels

Limitation: Extrapolation assumes constant velocity and heading. Manoeuvring aircraft may show brief position discrepancies until the next update.


6. Map Layer Management

In complex moving map applications displaying multiple traffic types, proper layer organisation is critical for both visual clarity and user interaction. Poor layer management leads to advisory overlays blocking live traffic clicks, labels obscuring icons, or uncertainty indicators rendering above precise position markers.

This section provides a proven layer architecture that ensures:

  • Correct visual hierarchy (most important information on top)
  • Proper interaction handling (clicks reach the intended targets)
  • Clean separation between different traffic types
  • Consistent behaviour across different mapping libraries

The layer stack design follows aviation display standards where precision information (live traffic) always takes visual precedence over advisory information (operational areas).

6.1 Layer Z-Index Organisation

Proper layer ordering ensures visual clarity in moving map applications:

┌─────────────────────────────────────┐
│  Traffic Labels & Tooltips   (top)  │  
├─────────────────────────────────────┤
│  Live Traffic Icons                 │  
├─────────────────────────────────────┤
│  Uncertainty Circles                │  
├─────────────────────────────────────┤
│  Advisory Overlays                  │  
├─────────────────────────────────────┤
│  Base Map                  (bottom) │  
└─────────────────────────────────────┘

Key Principles:

  • Live traffic icons render above advisory overlays
  • Advisory areas use semi-transparent fills (20-30% opacity)
  • Separate live traffic markers from advisory polygons in different layer groups
  • GeoJSON coordinates are [longitude, latitude]—convert to your map library's format
  • Use click handlers on live traffic; avoid on advisory overlays to prevent blocking map interaction

Implementation varies by mapping SDK:

  • Leaflet: L.layerGroup() with custom panes and z-index
  • Mapbox GL JS: Layer IDs with beforeId parameter
  • Google Maps: setZIndex() on overlay objects
  • OpenLayers: Z-index in layer style definitions

7. Traffic Aging and Expiry

Displaying stale traffic data as if it were current is one of the most dangerous mistakes in aviation situational awareness systems. An aircraft shown at a 2-minute-old position could be miles away from that location, leading to false assumptions about separation and safety.

This section establishes data freshness policies that balance practical update rates with safety requirements. The approach mirrors aviation standards: as data ages, visual confidence indicators degrade progressively until the data is removed entirely.

Key principle: Never show old data as if it were current. Users must always know when position information is becoming uncertain.

7.1 Data Freshness Management

Data freshness is fundamental to aviation safety. Displaying outdated position information as if it were current creates false situational awareness and can lead to dangerous assumptions about separation, traffic patterns, and collision avoidance.

Different traffic sources require different expiry policies based on their operational characteristics:

  • Speed differential: Fast jets (400+ knots) cover 200+ metres per second; slower UAVs cover 10-20 metres per second
  • Update frequency: Live transponders update every 1-3 seconds; advisory areas update every 30-60 seconds
  • Precision expectations: Point positions require strict freshness; area declarations remain valid longer
  • Consequence of staleness: Manned aviation position errors create immediate collision risk; advisory area drift is lower risk

The policies below establish when data transitions from current → stale → expired, with visual feedback at each stage.


7.1.1 Advisory UAV Traffic (Operational Areas)

Advisory traffic represents declared operational areas rather than live tracked positions. These are typically polygons or circles indicating where UAV activity is planned or ongoing, often filed in advance through flight planning systems.

Characteristics:

  • Slower aircraft (UAVs typically <100 knots)
  • Area-based (not point-based) awareness
  • Operational intent rather than tactical position
  • Lower positional precision requirements

Freshness Policy:

Age State Action
0-90s Current Display at full opacity with normal styling
90s-120s Aging Reduce opacity to 70%; add subtle visual indicator
>120s Expired Remove from display

Rationale: Advisory areas change slowly as they represent planned operations. A 2-minute validity window accommodates typical update intervals whilst preventing long-stale operational areas from cluttering the display.

Implementation Note: If an advisory area has an explicit end time in its metadata, respect that time regardless of last update age.


7.1.2 Live Traffic (ADS-B, FLARM, ADS-L, SafeSky)

Live traffic from transponders and telemetry systems demands strict freshness policies due to high speeds and rapid position changes. This category includes:

  • ADS-B: Automatic Dependent Surveillance-Broadcast (commercial aviation, general aviation)
  • FLARM: Collision avoidance system (gliders, light aircraft)
  • ADS-L: Low-power ADS-B variant
  • SafeSky: Smartphone-based telemetry from manned pilots

Speed Context:

  • Commercial jets: 400-550 knots (200-280 metres/second)
  • General aviation: 80-200 knots (40-100 metres/second)
  • Helicopters: 80-150 knots (40-75 metres/second)
  • UAVs with live telemetry: 10-80 knots (5-40 metres/second)

At these speeds, even 30 seconds of staleness can represent 1-8 kilometres of position error for fast aircraft.

Freshness Policy:

Age State Visual Feedback User Interpretation
0-30s Current Full opacity, normal icon, solid colour "This position is reliable for tactical decisions"
30-45s Stale 70% opacity + expanding uncertainty circle "Position is approximate; exercise caution"
>45s Expired Remove from display entirely N/A—do not show

Critical Safety Rule:

Never display traffic data older than 45 seconds. Showing a "frozen" aircraft icon at a stale position creates false confidence in separation that may no longer exist. An aircraft displayed 1 minute ago could be 10+ kilometres from that position.

Rationale for 45-Second Cutoff:

  • Transponder update rates: 1-3 seconds typical
  • Network/processing delays: 2-5 seconds typical
  • Grace period for temporary signal loss: 30-40 seconds
  • Safety margin: After 45s total, position uncertainty exceeds acceptable limits

Implementation Guidance:

const MAX_AGE_SECONDS = 45;
const STALE_THRESHOLD_SECONDS = 30;

function shouldDisplayTraffic(aircraft, currentTime) {
  const ageSeconds = (currentTime - aircraft.last_update) / 1000;
  return ageSeconds <= MAX_AGE_SECONDS;
}

function getTrafficOpacity(aircraft, currentTime) {
  const ageSeconds = (currentTime - aircraft.last_update) / 1000;
  if (ageSeconds < STALE_THRESHOLD_SECONDS) return 1.0;
  if (ageSeconds < MAX_AGE_SECONDS) return 0.7;
  return 0; // Should not be displayed
}

7.1.3 Uncertainty Visualisation

Implementation Approach:

function getUncertaintyRadius(lastUpdate, currentTime, groundSpeed) {
  const ageSeconds = (currentTime - lastUpdate) / 1000;
  if (ageSeconds < 30) return 0;
  
  // Assume maximum manoeuvring potential
  const maxDistanceMeters = groundSpeed * 0.514444 * ageSeconds;
  return maxDistanceMeters; // Display as circle radius
}

Purpose: Prevents false precision in position estimates. Users see explicit uncertainty rather than misleading "current" positions.

Visual Design: Use semi-transparent expanding circles that grow linearly with time since last update.


8. Visual Design Guidelines

Visual design in aviation safety systems is not merely aesthetic. It directly impacts a pilot's ability to quickly identify threats, assess separation, and make time-critical decisions. Icons must be instantly recognisable at a glance, even in high-workload scenarios or poor lighting conditions.

This section provides design standards based on:

  • International aviation iconography conventions
  • Human factors research in cockpit displays
  • Accessibility requirements for diverse user populations
  • Real-world feedback from pilots and UAV operators

The goal is to create a visual language that is intuitive, unambiguous, and consistent with expectations from professional aviation systems.

8.1 Traffic Icon States

Implement progressive visual feedback to communicate data confidence:

Current (0–30s) Aging (30–45s) Uncertain Expired (>45s)
Solid icon
Full colour
70% opacity
Subtle pulse
+ uncertainty circle Remove from display
8.2 Traffic Categorization

Use distinct iconography for different aircraft types:

  • UAV/Drone: Quadcopter or fixed-wing silhouette
  • Helicopter: Rotor-based icon with rotation indicator
  • General Aviation: Small aircraft silhouette
  • Commercial: Larger aircraft profile
  • Unknown: Generic aircraft symbol

Accessibility Note: Ensure icons are distinguishable by shape, not just colour, for colour-blind users.


9. Icon Assets

SafeSky provides optimised icon sets for rapid integration.

Download: aircraft_icons.zip

Beacon Type to Icon Mapping:

The table below defines the standard mapping between beacon types returned by the SafeSky API and the corresponding icon assets to be used for visualisation. Each beacon type is associated with a default static SVG icon.

For certain aircraft categories where motion is an important visual cue (notably helicopters and gyrocopters), an optional animated icon set is available. These animation frames can be cycled to represent rotor movement and improve situational awareness, but their use is optional and depends on client-side rendering capabilities and performance constraints.

Beacon type Icon (static) Optional animation
UNKNOWN dot.svg
STATIC_OBJECT dot.svg
GLIDER glider.svg
PARA_GLIDER para_glider.svg
HAND_GLIDER hand_glider.svg
PARA_MOTOR para_motor.svg
PARACHUTE parachute.svg
FLEX_WING_TRIKES flex_wing_trikes.svg
THREE_AXES_LIGHT_PLANE light_aircraft.svg
MOTORPLANE aircraft.svg
JET heavy_aircraft.svg
HELICOPTER helicopter.svg helicopter-anim_0.svghelicopter-anim_3.svg
GYROCOPTER gyrocopter.svg gyrocopter-anim_0.svggyrocopter-anim_3.svg
AIRSHIP airship.svg
BALLOON ballon.svg
UAV uav.svg
PAV pav.svg
MILITARY military.svg

10. Configuration Reference

10.1 Timing Parameters
Parameter Value Rationale
GET traffic (viewport) 3 seconds Optimal balance of real-time awareness and bandwidth efficiency
POST UAV telemetry 1 second Maintains continuity for receiving applications
Pre-flight GROUNDED publish 3–5 min before takeoff Provides advanced notice to nearby traffic
Uncertainty visualisation 30 seconds after update Reflects realistic position drift
Traffic expiry (non-UAV) 45 seconds after update Prevents stale data display
UAV expiry 2 minutes after update Accommodates lower-frequency updates
Viewport overscan 10-15% Eliminates edge transition artifacts

11. Reference Implementation

Explore a production implementation of these patterns:

Live Demo: https://live.safesky.app

The reference implementation demonstrates:

  • Dual-thread architecture with independent polling and publishing
  • Viewport-based queries with dynamic overscan
  • Client-side position extrapolation for smooth rendering
  • Progressive data aging with uncertainty visualisation
  • Batch telemetry publishing for fleet operations

Use the browser developer tools to observe network traffic patterns, polling behaviour, and rendering optimisations in a production environment.