Top

Airport Map

In this guide you will learn how to create an iteractive airport map, showing airplanes parked and airplanes taking off.

By referring to this guide, you will be able to:

  • Use a third part library with aria templates.
  • Use filters to simulate a server to fecth data from an external xml file.
  • Use the submitJsonRequest to request data.
  • Use the aria templates asynch paradigm.
  • Use $viewReady statement.
  • Use the onModuleEvent method.
  • Add and use listeners.

Tutorial

Step 1

Create a file using your favourite editor and call it index.html. This will be the bootstrap for your app.

Save it inside the root of your project (e.g. /guides/airportmap/).

Step 2

Inside index.html you will have to load the aria templates framework, define the container div and load the template that you will create.

To load the framework:

<script type="text/javascript" src="/aria/ariatemplates-1.2.0.js"></script>
<script type="text/javascript" src="/aria/css/atskin-1.2.0.js"></script>

To define the div container:

<div id="root"></div>
To load the template:
<script type="text/javascript">
Aria.loadTemplate({
  classpath:'ariadoc.guides.airportmap.view.Main',
  div:"root",
  moduleCtrl: {
    classpath : "ariadoc.guides.airportmap.Controller"
  }
});
</script>
To use the external library you have to import it inside the index.html after the framework scripts:
<script type="text/javascript" src="./lib/raphael-min.js"></script>

Step 3

Create a file and call it Main.tpl. This is the template that will be used to display the airport map.

Save it inside the view folder inside your project (/guides/airportmap/view/).

Step 4

Define the classpath of your template and declare that it has a script and a CSS template:
{Template {
    $classpath:'ariadoc.guides.airportmap.view.Main',
    $hasScript: true,
    $css: ['ariadoc.guides.airportmap.view.MainStyle']
}}
Define the macro main() to display the links to interact with the airport map and to define the map canvas:
{macro main()}
 <div class="toplinks">
   <a {on click {fn:"showMap"}/}>Show Map</a>&nbsp;|&nbsp;
   <a {on click {fn:"zoom",args:0.5}/}>Zoom+</a>&nbsp;|&nbsp;
   <a {on click {fn:"zoom",args:-0.5}/}>Zoom-</a>&nbsp;|&nbsp;
   <a {on click {fn:"zoom",args:0}/}>Zoom 1</a>
 </div>
 <div style="position:relative">
   <div id="mapcanvas" style="position:absolute;top:0px;left:0px">
   </div>
   <div id="hourcanvas"  style="position:absolute;top:5px;left:995px">
   </div>
 </div>
{/macro}

Step 5

Create a file and call it MainScript.js.

Save it inside the view folder inside your project (/guides/airportmap/view/).

Step 6

Define the classpath, the dependencies and the constructor:
Aria.tplScriptDefinition({
  $classpath : 'ariadoc.guides.airportmap.view.MainScript',
  $dependencies: [
     'ariadoc.guides.airportmap.view.svg.MapCanvas',
     'ariadoc.guides.airportmap.view.svg.HourCanvas'
  ],
  $constructor : function () {
    this.paper=null;
    this.uiData={
      W:1000,
      H:750
    };
  }
Call, inside the $viewReady, the module controller method to load the initial data and the module controller zoom method inside the zoom function:
$viewReady:function() {
   // get the initial data
   this.moduleCtrl.loadData();
}

zoom:function(evt,factor) {
   this.moduleCtrl.zoom(factor);
}
Call, inside the onModuleEvent, the map refresh to react to the event raised by the module controller:
onModuleEvent:function(evt) {
   if ((evt.name=="methodCallback" || evt.name=="methodCallEnd") && evt.method=="updateData") {
     if (this.mapCanvas) {
       this.mapCanvas.refreshDisplay();
     }
   }
}

Note: To get more info about the onModuleEvent take a look at this page.

Display the map:
showMap:function(evt) {
  if (this.initialized) return; // otherwise we will create 2 papers (!)
  this.initialized=true;
  var pkg=ariadoc.guides.airportmap.view;
  this.mapCanvas = new pkg.svg.MapCanvas(this.data,{containerId:"mapcanvas"});
  this.hourCanvas = new pkg.svg.HourCanvas(this.data,{containerId:"hourcanvas"});
  this.paper=this.mapCanvas.createMap();
  this.hourCanvas.display();
}
Inside the svg folder (/guides/airportmap/view/svg) you can find the classes that create an abstract layer to wrap the third part library code to be used with aria templates to display the airport map.
Inside this folder you will find:
  • the FlightsLayer.js that it is used to draw the aircrafts;
  • the HourCanvas.js that it is used to display the timer;
  • the MapCanvas.js that it is used to display the airport;
  • the LayerMgr.js that it is used to manage the different layers;
Those classes will not be described inside this guide, but feel free to read the code.

Step 7

Create a file and call it ControllerInterface.js.

Save it inside the root folder inside your project (/guides/airportmap/).

Step 8

Define the classpath, which class the controller interface extends, that is always aria.templates.IModuleCtrl and write the signature of all the methods of your controller:
Aria.interfaceDefinition({
  $classpath : 'ariadoc.guides.airportmap.ControllerInterface',
  $extends : 'aria.templates.IModuleCtrl',
  $interface : {
     loadData : {$type : "Function",$callbackParam: 1},
     updateData : {$type : "Function",$callbackParam: 0},
     zoom: {$type : "Function"}
  }
});

Step 9

Create a file and call it Controller.js.

Save it inside the root folder inside your project (/guides/airportmap/).

Step 10

Define the classpath, which class the controller extends, that is always aria.templates.ModuleCtrl, which controller interface implements, in our case ariadoc.guides.airportmap.ControllerInterface (that's the controller interface defined in the previous step), define the constructor and the destructor. Remember to add the filter to fetch the data from the xml file in order to simulate the server requests:
Aria.classDefinition({
   $classpath : 'ariadoc.guides.airportmap.Controller',
   $extends : 'aria.templates.ModuleCtrl',
   $implements : ['ariadoc.guides.airportmap.ControllerInterface'],
   $dependencies: ['aria.core.Timer'],
   $constructor : function () {
        this.$ModuleCtrl.constructor.call(this);

    // add test filter for response mocks
    aria.core.IOFiltersMgr.addFilter('ariadoc.guides.airportmap.mocks.TestMsgHandler');
   
    this._data = {
       pollTimeout:500,// by default new data will be requested every 500ms
       viewPort:{
          hDim:1129,    // horizontal dimension
          vDim:726,     // vertical dimension
          zoom:1,       // zoom level - cannot be 1
          vPosition:0   // vertical position of the viewport when zoom>1
       },
       layers:{
       }
    };
},
   $destructor : function () {
       this.$ModuleCtrl.$destructor.call(this);
   }
Define the method to make a Json request to fetch the data:
/**
 * Retrieve the data to be displayed on the map
 * @param {Callback} cb
 */

 loadData: function(cb) {
   var requestData = {};
   this.submitJsonRequest("getData", requestData, {
     fn : this._loadData,
     scope : this,
     args : cb
   });
}
When the response is received load the mechanism to poll the data automatically:
/**
 * loadData internal callback - called when the server response is received
 * @param {} res the submitJsonRequest return value
 * @param {Callback} args
 */

 _loadData:function (res, cb) {
   if (res.error) {
     this.$callback(cb);
     return;
   }
   var ds = res.response;
  // add dataset to the list and select it
  this.json.setValue(this._data,"layers", ds.layers);
  this._queueDataUpdate();
  this.$callback(cb);
}
Queue update requests:
/**
 * queue a request to updateData
 */

 _queueDataUpdate:function() {
   // trigger polling
   var interfaceWrapper=this.$interface(this.$publicInterfaceName)
   aria.core.Timer.addCallback({
     fn:interfaceWrapper.updateData,
     scope:interfaceWrapper,
     delay:this._data.pollTimeout
   });
}
and generate request to get updated data:
/**
 * Sends a request to get updated data
 * This request is automatically polled once loadData has been called
 */

 updateData:function(cb) {
  // send the request
  var timeStamp=123456789; // TODO use timestamp received with last data update
  var requestArgs = {since:timeStamp};
  this.submitJsonRequest("getDataUpdates", requestArgs, {
    fn : "_updateDataResponse",
    scope : this,
    args : cb
 });
}
and manage the response updating the data for each layer:
_updateDataResponse:function(res,args) {
  var cb=args;
  if (res.error) {
    this.$callback(cb);
    return;
  }
  var ds = res.response;
  // go through each layer and update the data model with the response data
  if (ds.layers && ds.layers.flights) {
    if (!this._data.layers) this.json.setValue(this._data,"layers",{flights:{}});
    else if (!this._data.layers.flights) this.json.setValue(this._data.layers,"flights",{});
    var flights=this._data.layers.flights;
    var newItems=ds.layers.flights.items, newItem, item;
    for (var key in newItems) {
       newItem=newItems[key];
       item=flights.items[key];
       if (!item) {
         // new flight
         this.json.setValue(flights.items,key,newItem);
       } else {
         // new postions
         if (newItem.positions) {
           // append new positions to the position list
           for (var p=0, max=newItem.positions.length;max>p;p++) {
             this.json.add(item.positions,newItem.positions[p])          ;
           }
           // splice if more than 20 positions
           var maxsz=20;
           if (item.positions.length>maxsz) {
             this.json.splice(item.positions,0,item.positions.length-maxsz);
           }
         }
       }
    }
 }
 // TODO don't forget mechanism to flush un-used data - e.g. when a plane has taken-off
 this._queueDataUpdate();
}
Define the zoom function:
 /**
  * Update the zoom factor
  * Note: the call will be ignored if the increment results in a zoom which is less than 1
  * @param {int} increment the update factor - can be positive or negative, if 0 the zoom will be reset
  */

  zoom: function (increment) {
   var zoom2=this._data.viewPort.zoom+increment;
   if (increment==0) zoom2=1
   if (zoom2>=1) {
     this.json.setValue(this._data.viewPort,"zoom",zoom2);
   }
}

Step 11

Create a file and call it MainStyle.tpl.css.

Save it inside the view folder inside your project (/guides/airportmap/view/).

Step 12

Give some style to your todo app.

Step 13

That's it! Open the index.html with your favourite browser (e.g. Chrome) and enjoy.