Data
Offline First
The SDK is offline first. All tracked turbulence data is stored offline until successfully sent to the server and the fetched data from the server is stored on the disk and accessible offline according to the description below. All configurations are stored on the disk and stay across app launches until explicit change.
Data Query
At first request, the whole data according to SkyPath.dataQuery
is fetched and then only the new data that appeared (delta) is fetched to save network traffic. When cached data expires or a delta can't be received, the whole fresh data will be received again.
There are thousands of turbulence reports around the globe. To reduce network traffic usage and keep only data that is currently needed the data fetch is separated into the different types controlled by the SkyPath.dataQuery
object that is set initially to default values and can be updated at any time. All of the below are optional to set but recommended due to your specific flow.
After updating SkyPath.dataQuery
in any way, the check, if need to fetch new data, will be made. And if the change requires a new server fetch, the server request will be made immediately. No need to call SkyPath.fetchData(refresh:)
.
You can update data queries at any time. When updating just one field, use one call
SkyPath.shared.dataQuery.types = [.turbulence, .turbulencePolygons]
When updating multiple fields prefer to copy a current object, modify, and set to update dataQuery
in SDK in one call only. On any dataQuery
object update SDK will check if need to update the data, for example because the polygon has been updated. Updating dataQuery
multiple times immediately could result in a situation when you will not get fresh data immediately but in the next fetch cycle in a minute or so.
var dataQuery = SkyPath.shared.dataQuery
dataQuery.types = [.turbulence, .turbulencePolygons]
dataQuery.polygon = some_polygon
SkyPath.shared.dataQuery = dataQuery
Updating SkyPath.dataQuery
will trigger a fetch from the server if needed automatically. Receiving server response will take some time so SkyPathDelegate
callbacks will be called in a delay, not immediately after SkyPath.dataQuery
update. Delegate callbacks will not be called if no new server fetch is needed or no new data will be received.
Update the visible data by querying SDK after updating the SkyPath.dataQuery
not waiting for the SkyPathDelegate
. SDK caches data locally, so querying after the DataQuery update will return cached data if any. Then, update data again in the SkyPathDelegate
callback.
Turbulence Polygons
A planet-wide aggregated turbulence area polygons as a GeoJSON string. Used to show turbulence areas worldwide without fetching too much data. DataQuery.types
should have .turbulencePolygons
to fetch it. Stored on disk, accessible offline.
Turbulence polygons are generated per time history separately and by default, only selected time history is fetched for polygons. To enable fetching polygons for all time histories up to selected set a corresponding flag. For example, if set to fetch 4h, then to have polygons available offline for 0.5h, 1h, 2h, and 4h you need to set this flag to true
.
SkyPath.shared.dataQuery.globalTurbulencePolygonsUpToEnabled = false
globalTurbulencePolygonsUpToEnabled = true
consumes more network traffic
SDK periodically fetches global turbulence polygons and calls the delegate method when receiving new data.
// SkyPathDelegate
func didReceiveNewTurbulencePolygons() { }
Polygons data is generated per each history time separately, so if SkyPath.dataHistory
is set to .twoHours
for example, polygons for .fourHours
will not be available to query, because it will not be fetched from the server. It is possible also to fetch polygons for times up to selected by setting DataQuery. globalTurbulencePolygonsUpToEnabled = true
. It means that if set to .fourHours
polygons will be fetched also for .halfHour
, .hour
, .twoHours
, and .fourHours
. It will allow you to change time and see polygons data when offline.
If DataQuery.globalEnabled
== true
turbulence polygons will be fetched worldwide, otherwise, if polygon
is set - only inside the polygon. The default is true
.
It is recommended to keep turbulence polygons and low-level hexagons data on separate layers. Show and hide the corresponding layer when zooming in/out of the map. Here is an approximate zoom level when switching from hexagons to global polygons:
3320 meters/point (1660 meters/pixel) or 10892 feet/point (5446 feet/pixel)
Here is an example screenshot of the iPad Pro 9.7 inch screen with a visible area when to switch.
Route Corridor (Polygon)
Route corridor polygon is a geo-fence area to fetch data inside only. It should be a valid GeoJSON Polygon RFC 7946. Use your route line coordinates to create a polygon with some width distance (50-150 NM is good).
SDK will fetch all data in the corridor at first and then only the delta since the previous fetch in this corridor. When changing the corridor to a different then all data will be fetched in the new corridor (not delta) and then will use delta again. So the route corridor should change only when the route line changes. Changing corridors frequently will result in big network traffic use. Also, choose the corridor width according to your needs because a bigger width and bigger corridor will consume more network traffic.
Frequent changes and a bigger width of the corridor consume more network traffic
It is fetched separately from other data types and as fast as possible, and also stored offline. Stored on disk, accessible offline.
let polygon: [CLLocationCoordinate2D] = []
SkyPath.shared.dataQuery.polygon = polygon
According to GeoJSON Polygon RFC 7946 it should be a closed ring with up to 250 coordinates, otherwise SkyPath.didFailToFetchNewData
will be called with an error. It is recommended to have fewer coordinates and simplify the polygon with enough tolerance.
To make a route corridor and simplification of the polygon you can use the provided [CLLocationCoordinate2D].buffer(widthNM:simplify:tolerance:)
from the SDK.
Usage example (a background thread is recommended to not block the main thread):
// where `polygon` is [CLLocationCoordinate2D]
dataQuery.polygon = polygon.buffer(widthNM: 100, simplify: true)
For performance and network optimization, we recommend limiting the polygon to an area no larger in size than the route corridor in 250 NM width.
Viewport
A viewport is a polygon of a visible map area in the app to fetch the right data when it's needed. Please keep in mind, that the SDK will try to fetch the data for the viewport as soon as possible after updating SkyPath.shared.dataQuery.viewport
. So to save network traffic consider updating viewport
when it's needed.
All data in the viewport will be fetched at first and then a delta will be fetched only. But when changing a viewport all data will be fetched again.
Frequent changes and a bigger viewport consumes more network traffic
A good place could be when the pilot moved the map manually, released the finger and the map stopped moving after animation, or when the focused map area is moved by code far from the previously focused area. Stored in memory, accessible offline until app relaunch. The previous viewport data is replaced with new viewport data.
let polygon: [CLLocationCoordinate2D] = []
SkyPath.shared.dataQuery.viewport = polygon
The simplest viewport polygon would be a currently visible rectangle on the map like [NorthWest, NorthEast, SouthEast, SouthWest, NorthWest]
. For a more complex polygon, the following rules are applied.
Should be a closed ring and have max 250 coordinates, otherwise SkyPath.didFailToFetchNewData
will be called with an error. Simplify the polygon with enough tolerance.
To make a viewport and simplification of the polygon you can use the provided [CLLocationCoordinate2D].buffer(widthNM:simplify:tolerance:)
from the SDK.
Usage example (a background thread is recommended to not block the main thread):
// where `polygon` is [CLLocationCoordinate2D]
dataQuery.viewport = polygon.buffer(widthNM: 100, simplify: true)
// or
dataQuery.viewport = polygon.simplify()
For performance and network optimization, we recommend limiting the viewport to an area no larger in size than the Continental US for example.
History Time
Set DataHistoryTime
to fetch data for. It's an enum with cases: halfHour
, hour
, twoHours
, fourHours
, and sixHours
. The default is twoHours
. The server does some data precalculations so only the specified time frames are supported.
SkyPath.shared.dataHistoryTime = .twoHours
When started, SDK fetches initial data from time back in time and then receives only updates since the last update.
It determines the data history to be fetched from the server. So if it is set to .twoHours
for example (the default one), there will be no data locally available for more than 2 hours ago. If need 4 hours of history, set SkyPath.dataHistoryTime
to .fourHours
.
Changing from lower time to higher will require an API request to fetch data, when changing from higher to lower time, data could be available immediately as it was already included in the higher history time fetch. Alternatively, time can be set to .sixTime
once to fetch 6 hours of history data always and then just query with a different time history to show on the map. Please note, that it will increase network traffic but will allow having more data available immediately and offline during changing time history.
Update
Set DataUpdateFrequency
, it's an enum with cases: none
, minimal
, medium
, fast
. The default is fast
(every 1 min). It controls time intervals to fetch data for global turbulence polygons, route polygons, viewport, and other data. When the data query route polygon or the viewport is changed, they are fetched as fast as possible not waiting for the next time interval.
SkyPath.shared.dataUpdateFrequency = .fast
To save network traffic, you can set it to none
to disable periodic new data fetch from the server at all or set it to minimal
to update with bigger time intervals.
Can be updated at any time in the project. Value is stored across app launches until changed.
The default is fast
.
When you change some query parameters SDK will start fetching fresh data immediately if needed. No need to force fetch manually.
At some point, if need to fetch fresh data immediately (not after a data query update) call the following:
SkyPath.shared.fetchData(refresh: true)
Frequent fetchData(refresh: true)
consumes more network traffic
It could be helpful to know if SkyPath data was updated a long time ago (when offline for example), so check when the last time data was successfully received from the server.
SkyPath.shared.dataUpdatedAt
Implement SkyPathDelegate.didFailToFetchNewData(with:)
to monitor if there is any error and handle it accordingly.
Stop Fetching
The preferred way to stop data fetching is to change the update frequency SkyPath.shared.dataUpdateFrequency = .none
.
Based on your implementation you can use other options:
-
Set
DataQuery.types
to an empty setSkyPath.shared.dataQuery.types = []
. -
Remove the
polygon
, andviewport
in theSkyPath.dataQuery
and setglobalEnabled
tofalse
. It can stop fetching because no area is set to fetch data in.
SDK will continue recording and reporting data.
Removing a flight by SkyPath.setFlight(nil)
does not stop fetching data because SDK allows fetching data without it, for example for a map visible viewport or global polygons.
Data Types
SkyPath provides different data types, see DataTypeOptions
. The default is turbulence
only. Set it if you need more than just turbulence data.
SkyPath.shared.dataQuery.types = [.turbulence, .turbulencePolygons]
Turbulence Severity
Color Legend
Smooth #FFFFFF (alpha 0.6) Border #898989 (alpha 0.8) ⬡
Light #FFF04C (alpha 0.8) ⬣
Light-Moderate #FFBC09 (alpha 0.8) ⬣
Moderate #FF7100 (alpha 0.8) ⬣
Moderate-Occasionally Severe #FF0000 (alpha 0.8) ⬣
By default, all severities of turbulence will be fetched, but you can provide a list of severities to fetch.
SkyPath.shared.dataQuery.sevs = [.moderate, .moderateSevere]
.none
or smooth severity means that the pilot who crossed this hexagon didn't experience any turbulence, so his report is none. Each hexagon can have multiple reports left for it because multiple flights crossed this hexagon. By default, TurbulenceQuery
has aggregate = true
which means you'll get one TurbulenceItem
per hexagon (tile) with the most severe report for this hexagon.