Flight Animation

This example uses OpenLayers v4.0.1. The latest is v.

#### Flight Animation

Demonstrates how to animate flights with ´postcompose´.

This example shows how to use postcompose and vectorContext to animate flights. A great circle arc between two airports is calculated using arc.js and then the flight paths are animated with postcompose. The flight data is provided by OpenFlights (a simplified data set from the Mapbox.js documentation is used).

Related API documentation:

- ol.Feature
,
- ol.Map
,
- ol.View
,
- ol.geom.LineString
,
- ol.layer.Tile
,
- ol.layer.Vector
,
- ol.proj
,
- ol.source.Stamen
,
- ol.source.Vector
,
- ol.style.Stroke
,
- ol.style.Style

Copy Edit
Flight Animation Demonstrates how to animate flights with ´postcompose´. var map = new ol.Map({ layers: [ new ol.layer.Tile({ source: new ol.source.Stamen({ layer: 'toner' }) }) ], target: 'map', view: new ol.View({ center: [0, 0], zoom: 2 }) }); var style = new ol.style.Style({ stroke: new ol.style.Stroke({ color: '#EAE911', width: 2 }) }); var flightsSource; var addLater = function(feature, timeout) { window.setTimeout(function() { feature.set('start', new Date().getTime()); flightsSource.addFeature(feature); }, timeout); }; var pointsPerMs = 0.1; var animateFlights = function(event) { var vectorContext = event.vectorContext; var frameState = event.frameState; vectorContext.setStyle(style); var features = flightsSource.getFeatures(); for (var i = 0; i < features.length; i++) { var feature = features[i]; if (!feature.get('finished')) { // only draw the lines for which the animation has not finished yet var coords = feature.getGeometry().getCoordinates(); var elapsedTime = frameState.time - feature.get('start'); var elapsedPoints = elapsedTime * pointsPerMs; if (elapsedPoints >= coords.length) { feature.set('finished', true); } var maxIndex = Math.min(elapsedPoints, coords.length); var currentLine = new ol.geom.LineString(coords.slice(0, maxIndex)); // directly draw the line with the vector context vectorContext.drawGeometry(currentLine); } } // tell OpenLayers to continue the animation map.render(); }; flightsSource = new ol.source.Vector({ wrapX: false, attributions: 'Flight data by ' + '<a href="http://openflights.org/data.html">OpenFlights</a>,', loader: function() { var url = 'https://openlayers.org/en/v4.0.1/examples/data/openflights/flights.json'; fetch(url).then(function(response) { return response.json(); }).then(function(json) { var flightsData = json.flights; for (var i = 0; i < flightsData.length; i++) { var flight = flightsData[i]; var from = flight[0]; var to = flight[1]; // create an arc circle between the two locations var arcGenerator = new arc.GreatCircle( {x: from[1], y: from[0]}, {x: to[1], y: to[0]}); var arcLine = arcGenerator.Arc(100, {offset: 10}); if (arcLine.geometries.length === 1) { var line = new ol.geom.LineString(arcLine.geometries[0].coords); line.transform(ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857')); var feature = new ol.Feature({ geometry: line, finished: false }); // add the feature with a delay so that the animation // for all features does not start at the same time addLater(feature, i * 50); } } map.on('postcompose', animateFlights); }); } }); var flightsLayer = new ol.layer.Vector({ source: flightsSource, style: function(feature) { // if the animation is still active for a feature, do not // render the feature with the layer style if (feature.get('finished')) { return style; } else { return null; } } }); map.addLayer(flightsLayer); <div id="map" class="map"></div>

    <!DOCTYPE html>
    <html>
      <head>
        <title>Flight Animation</title>
        <link rel="stylesheet" href="https://openlayers.org/en/v4.0.1/css/ol.css" type="text/css">
        <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
        <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
        <script src="https://openlayers.org/en/v4.0.1/build/ol.js"></script>
        <script src="https://api.mapbox.com/mapbox.js/plugins/arc.js/v0.1.0/arc.js"></script>
      </head>
      <body>
        <div id="map" class="map"></div>
        <script>
          var map = new ol.Map({
            layers: [
              new ol.layer.Tile({
                source: new ol.source.Stamen({
                  layer: 'toner'
                })
              })
            ],
            target: 'map',
            view: new ol.View({
              center: [0, 0],
              zoom: 2
            })
          });

          var style = new ol.style.Style({
            stroke: new ol.style.Stroke({
              color: '#EAE911',
              width: 2
            })
          });

          var flightsSource;
          var addLater = function(feature, timeout) {
            window.setTimeout(function() {
              feature.set('start', new Date().getTime());
              flightsSource.addFeature(feature);
            }, timeout);
          };

          var pointsPerMs = 0.1;
          var animateFlights = function(event) {
            var vectorContext = event.vectorContext;
            var frameState = event.frameState;
            vectorContext.setStyle(style);

            var features = flightsSource.getFeatures();
            for (var i = 0; i < features.length; i++) {
              var feature = features[i];
              if (!feature.get('finished')) {
                // only draw the lines for which the animation has not finished yet
                var coords = feature.getGeometry().getCoordinates();
                var elapsedTime = frameState.time - feature.get('start');
                var elapsedPoints = elapsedTime * pointsPerMs;

                if (elapsedPoints >= coords.length) {
                  feature.set('finished', true);
                }

                var maxIndex = Math.min(elapsedPoints, coords.length);
                var currentLine = new ol.geom.LineString(coords.slice(0, maxIndex));

                // directly draw the line with the vector context
                vectorContext.drawGeometry(currentLine);
              }
            }
            // tell OpenLayers to continue the animation
            map.render();
          };

          flightsSource = new ol.source.Vector({
            wrapX: false,
            attributions: 'Flight data by ' +
                  '<a href="http://openflights.org/data.html">OpenFlights</a>,',
            loader: function() {
              var url = 'https://openlayers.org/en/v4.0.1/examples/data/openflights/flights.json';
              fetch(url).then(function(response) {
                return response.json();
              }).then(function(json) {
                var flightsData = json.flights;
                for (var i = 0; i < flightsData.length; i++) {
                  var flight = flightsData[i];
                  var from = flight[0];
                  var to = flight[1];

                  // create an arc circle between the two locations
                  var arcGenerator = new arc.GreatCircle(
                      {x: from[1], y: from[0]},
                      {x: to[1], y: to[0]});

                  var arcLine = arcGenerator.Arc(100, {offset: 10});
                  if (arcLine.geometries.length === 1) {
                    var line = new ol.geom.LineString(arcLine.geometries[0].coords);
                    line.transform(ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857'));

                    var feature = new ol.Feature({
                      geometry: line,
                      finished: false
                    });
                    // add the feature with a delay so that the animation
                    // for all features does not start at the same time
                    addLater(feature, i * 50);
                  }
                }
                map.on('postcompose', animateFlights);
              });
            }
          });

          var flightsLayer = new ol.layer.Vector({
            source: flightsSource,
            style: function(feature) {
              // if the animation is still active for a feature, do not
              // render the feature with the layer style
              if (feature.get('finished')) {
                return style;
              } else {
                return null;
              }
            }
          });
          map.addLayer(flightsLayer);
        </script>
      </body>
    </html>