Home
Categories
Dictionary
Glossary
Download
Project Details
Changes Log
What Links Here
FAQ
License

Synthetic map UA application



This article presents the UA application in the synthetic map tutorial.

Overview

The UA application is responsible to:
  • Show the elements of the tactical environment (FEBA, flightplans, waypoints, Zones, and aircraft
  • Handle the Zoom In / Zoom Out on the Map, and the change of the map center
The associated Definition file is: sitac.xml.

Initialize the data structures


We need to initialize the buffers for the following arinc_661_glossary#mapitemlists:
  • FlightPlans
  • Waypoints
  • Zones
  • Feba
  • Aircrafts
This must be called in the init method[1]
See Developing a UA for the reason why is should be in this method
.

For example for the Waypoints:
      int APPLID_ID = 1; // the application ID of the Definition file
      int LAYER_ID = 1; // the layer ID of the SITAC Layer
      int WAYPOINTS_ID = 14; // the ID of the Waypoints MapItemList widget
      info = new BufferOfItemsInfo("Waypoints", APPLID_ID , LAYER_ID, WAYPOINTS_ID, wptBuffer);
      wptinfos.put(info.getName(), info);
      wptItems = mapItemsConstructor.getMapItemList(api, APPLID_ID, LAYER_ID, WAYPOINTS_ID);
      wptBuffer.setClearFlag(true);      

Initialize the event handlers


We need to add the code the handle the widget events:
  • Handle the click on the Zoom In and Zoom Out events
  • Handle the change of the map center
This must also be called in the init method[1]
See Developing a UA for the reason why is should be in this method
.

Handle the click on the Zoom buttons

For the Zoom In button: The way the Zoom In work is that we will divide the range by 2 for each click (we won't allow to zoom under the 10 NM range):
      int LAYER_SITAC_ID = 1; // the layer ID of the SITAC Layer
      int ZOOMIN_ID = 6; // the widget ID of the Zoom In button      
      api.addWidgetEventListener2(LAYER_SITAC_ID, ZOOMIN_ID, new ARINCEventListener() {
         public void eventReceived(ARINCEvent evt) {
           try {
             if (range > 10) {
               range = range / 2; // we won't allow to zoom under the 10 NM range
             }
             updateRange();
           } catch (ARINCRuntimeException ex) {
             logger.error(module, ex.getMessage());
           }
         }
      });      
The update range code is the following (we will update the range label and the MapHorz range):
      private void updateRange() {
         int LAYER_SITAC_ID = 1; // the layer ID of the SITAC Layer
         int RANGE_ID = 7; // the widget ID of the range label
         int MAPHORZ_ID = 1; // the widget ID of the MapHorz         
         String value = Integer.toString((int) range);
         api.setWidgetParameter(LAYER_SITAC_ID, RANGE_ID, ARINC661.A661_STRING, value);
         api.setWidgetParameter(LAYER_SITAC_ID, MAPHORZ_ID, ARINC661.A661_RANGE, range);
         api.sendAll();
      }      
For the Zoom Out button: the code is similar, except that be multiply by 2 rather than divide the range.

Handle the change of the map center

The way we will handle the change of the map center is:
  • We listen to a click on the MapHorzSource, which will return the latitude and longitude of the point where the user has clicked
  • We show a MapHorzPanel with a Center and Cancel button
  • We listen to the click on the center button to change the reference of the map

Handle the click on the MapHorzSource widget

For the click on the MapHorzSource widget:
      api.addWidgetEventListener2(LAYER_SITAC_ID, MAPHORZ_SOURCE_ID, new ARINCEventListener() {
         public void eventReceived(ARINCEvent evt) {
           try {
             WidgetEvent widgetEvt = (WidgetEvent) evt;
             showMapPanel(widgetEvt);
           } catch (ARINCRuntimeException ex) {
             logger.error(module, ex.getMessage());
           }
         }
      });      
and:
      private void showMapPanel(WidgetEvent widgetEvt) {
         centerMapLatitudeTemp = (Float) widgetEvt.getValues().get(0);
         centerMapLongitudeTemp = (Float) widgetEvt.getValues().get(1);
         int LAYER_SITAC_ID = 1; // the layer ID of the SITAC Layer
         int ACTION_PANEL_ID = 5; // the layer ID of the panel which shows the CENTER button 
         api.setWidgetParameter(LAYER_SITAC_ID, ACTION_PANEL_ID, ARINC661.A661_VISIBLE, true);
         api.setWidgetParameter(LAYER_SITAC_ID, ACTION_PANEL_ID, ARINC661.A661_REF_POS_X, centerMapLatitudeTemp);
         api.setWidgetParameter(LAYER_SITAC_ID, ACTION_PANEL_ID, ARINC661.A661_REF_POS_Y, centerMapLongitudeTemp);
         api.sendAll();
      }        
After clicking on the MapHorzSource widget, the server sends the latitude and longitude of the point under the mouse. We then show the Panel containing the CENTER and CANCEL button at the position of the click.

Handle the click on the CENTER button

For the click on the CENTER button:
      api.addWidgetEventListener2(LAYER_SITAC_ID, CENTER_ID, new ARINCEventListener() {
         public void eventReceived(ARINCEvent evt) {
           try {
             centerMap();
           } catch (ARINCRuntimeException ex) {
             logger.error(module, ex.getMessage());
           }
         }
      });     
and:
      private void centerMap() {
         api.setWidgetParameter(LAYER_SITAC_ID, ACTION_PANEL_ID, ARINC661.A661_VISIBLE, false); // to make the panel invisible
         centerMapLatitude = centerMapLatitudeTemp;
         centerMapLongitude = centerMapLongitudeTemp;
         this.setMapCenter(centerMapLatitude, centerMapLongitude);
         api.sendAll();
      }   
      
      private void setMapCenter(float latitude, float longitude) {
         int LAYER_SITAC_ID = 1; // the layer ID of the SITAC Layer
         int MAPHORZ_ID = 1; // the widget ID of the MapHorz widget
         api.setWidgetParameter(LAYER_SITAC_ID, MAPHORZ_ID, ARINC661.A661_PRP_LAT, latitude);
         api.setWidgetParameter(LAYER_SITAC_ID, MAPHORZ_ID, ARINC661.A661_PRP_LONG, longitude);
         api.setWidgetParameter(LAYER_SITAC_ID, MAPHORZ_ID, ARINC661.A661_AC_LAT, latitude);
         api.setWidgetParameter(LAYER_SITAC_ID, MAPHORZ_ID, ARINC661.A661_AC_LONG, longitude);
      }          
The code when the user clicks on CANCEL is similar, but then we just make the panel invisible.

Listen to the services notifications

We have the following declarations for the services:
      private static final NamespaceKey WORLD = NamespaceKey.createKey("http://dassault-aviation.com/tacticalenv", "worldModel");
      private static final NamespaceKey FLIGHTPLANS = NamespaceKey.createKey("http://dassault-aviation.com/tacticalenv", "flightplansModel");
      private static final NamespaceKey WAYPOINTS = NamespaceKey.createKey("http://dassault-aviation.com/tacticalenv", "waypointsModel");
      private static final NamespaceKey AIRCRAFTS = NamespaceKey.createKey("http://dassault-aviation.com/tacticalenv", "aircraftsModel");
      private static final NamespaceKey CONFIG = NamespaceKey.createKey("http://dassault-aviation.com/tacticalenv", "worldConfig");      
And the subscribe method which is the method to use in the User Applications to be notified from services:
      public void subscribe(ServiceInstance service) {
         NamespaceKey key = service.getKey();
         if (key.equals(WORLD)) {
           // world service (zones and FEBA)
           receiveEnvWorld(service);
         } else if (key.equals(CONFIG)) {
           // worldConfig service (latitude and longitude of the tactical environment)
           receiveConfig(service);
         } else if (key.equals(FLIGHTPLANS)) {
           // flightplans service (flightplans)
           receiveFlightPlans(service);
         } else if (key.equals(WAYPOINTS)) {
           // waypoints service (waypoints)
           receiveWaypoints(service);
         } else if (key.equals(AIRCRAFTS)) {
           // aircrafts service (aircrafts)
           receiveAircrafts(service);
         }
      }      

Listen to the aircrafts notifications

Main Article: aircraftsModel

In our case, we will only have one aircraft, we will only use the first element of the Aircrafts. Note that the "http://dassault-aviation.com/tacticalenv":aircraftsModel service sends an Aircrafts object.
      private void receiveAircrafts(ServiceInstance service) {
         aircrafts = (Aircrafts) service.getValue("aircrafts");
         aircraftsBuffer.reset();
         int LAYER_SITAC_ID = 1; // the layer ID of the SITAC Layer
         int AIRCRAFTS_ID = 50; // the widget ID of the MapHorzItemList widget   
         int DEFAULT_STYLE = 0; // the default itemStyle           
         if (aircrafts.countAircrafts() != 0) {
           // the first MapItem is the ItemStyle
           ItemMap item = mapItemsConstructor.addMapItem(aircraftsBuffer, 0, XMLMapItem.TYPE_HORIZ, "A661_ITEM_STYLE");
           item.addProperty("ItemStyleSet", DEFAULT_STYLE);
           // we only take into account the first aircraft
           Iterator<Aircraft> it = aircrafts.getAircrafts().values().iterator();
           Aircraft aircraft = it.next();
        
           // the second MapItem is the Aircraft
           item = mapItemsConstructor.addMapItem(aircraftsBuffer, 1, XMLMapItem.TYPE_HORIZ, "A661_SYMBOL_ROTATED");
           item.addProperty("X", aircraft.getLatitude());
           item.addProperty("Y", aircraft.getLongitude());
           item.addProperty("SymbolType", 0);
           item.addProperty("Orientation", aircraft.getOrientation() + 180);
           api.setWidgetParameter(LAYER_SITAC_ID, AIRCRAFTS_ID, ARINC661.A661_BUFFER_OF_MAPITEM, aircraftsBuffer);
           api.sendAll();
         }
      }      

Listen to the flightplans notifications

Main Article: flightplansModel

      private void receiveFlightPlans(ServiceInstance service) {
         int LAYER_SITAC_ID = 1; // the layer ID of the SITAC Layer
         int FLIGHTPLANS_ID = 10; // the widget ID of the flightplans MapHorzItemList widget   
         int WAYPOINTS_ID = 14; // the widget ID of the waypoints MapHorzItemList widget         
         int DEFAULT_STYLE = 0; // the default itemStyle       
         Flightplans flightplans = (Flightplans) service.getValue("flightplans");      
         fplBuffer.reset();
         wptBuffer.reset();
         // we will also show the waypoints even if they are not in the flightplan
         showWaypoints();
         // now we show the flightplan
         Iterator<Flightplan> it = flightplans.getFlightplans().values().iterator();
         while (it.hasNext()) {
           Flightplan fp = it.next();
           this.setOneFlightplan(fp, waypoints);
         }
         // now send the buffers for the waypoints and the flightplan to the server
         api.setWidgetParameter(LAYER_SITAC_ID, FLIGHTPLANS_ID, ARINC661.A661_BUFFER_OF_MAPITEM, fplBuffer);
         api.setWidgetParameter(LAYER_SITAC_ID, WAYPOINTS_ID, ARINC661.A661_BUFFER_OF_MAPITEM, wptBuffer);
         api.sendAll();
      }  
For the waypoints:
      private void showWaypoints() {
         int DEFAULT_STYLE = 0; // the default itemStyle          
         int currentWPT = wptBuffer.getNumberOfItems();
         int j = currentWPT > 0 ? currentWPT + 1 : 0;
         Iterator<Waypoint> it = waypoints.getWaypoints().values().iterator();
         while (it.hasNext()) {
           Waypoint waypoint = it.next();
           // we first show the ItemStyle of the waypoint. In our case, the default style is a symbol shown in green
           ItemMap item = mapItemsConstructor.addMapItem(wptBuffer, j, XMLMapItem.TYPE_HORIZ, "A661_ITEM_STYLE");
           item.addProperty("ItemStyleSet", DEFAULT_STYLE);
           j++;

           // then we show the symbol, which can be a NAV symbol (a small circle), or an ATT symbol for a landing waypoint
           item = mapItemsConstructor.addMapItem(wptBuffer, j, XMLMapItem.TYPE_HORIZ, "A661_SYMBOL_GENERIC");
           item.addProperty("X", waypoint.getLatitude());
           item.addProperty("Y", waypoint.getLongitude());
           if (waypoint.getType().equals("ATT")) {
             item.addProperty("SymbolType", SYMBOL_ATT_WPT);
           } else {
             item.addProperty("SymbolType", SYMBOL_NAV_WPT);
           }
           j++;
           // an then the name of the waypoint
           item = mapItemsConstructor.addMapItem(wptBuffer, j, XMLMapItem.TYPE_HORIZ, "A661_LEGEND");
           item.addProperty("LegendString", waypoint.getName());
           item.addProperty("EndFlag", true);
           j++;
         }
      }      
For each flightplan, we must be aware that each flightplan only refer to the waypoints names, so we must get the waypoint position associated wtih each waypoint name:
         private void setOneFlightplan(Flightplan fp, Waypoints waypoints) {
         int currentFP = fplBuffer.getNumberOfItems();
         int currentWPT = wptBuffer.getNumberOfItems();
         int i = currentFP > 0 ? currentFP + 1 : 0;
         int j = currentWPT > 0 ? currentWPT + 1 : 0;
         boolean isStart = true;
         Iterator<String> it2 = fp.getWaypoints().iterator();
         while (it2.hasNext()) {
           String id = it2.next();
           Waypoint waypoint = waypoints.getWaypoint(id);
           // we first show the ItemStyle of the flighplan. In our case, the default style is line segments shown in green
           ItemMap item = mapItemsConstructor.addMapItem(fplBuffer, i, XMLMapItem.TYPE_HORIZ, "A661_ITEM_STYLE");
           item.addProperty("ItemStyleSet", DEFAULT_STYLE);
           i++;
           // as you see, we get the waypoint position for each point to show the segments
           // we show a lineStart to signal the beginning of the flighplan 
           if (isStart) {
             item = mapItemsConstructor.addMapItem(fplBuffer, i, XMLMapItem.TYPE_HORIZ, "A661_LINE_START");
             item.addProperty("X", waypoint.getLatitude());
             item.addProperty("Y", waypoint.getLongitude());
             isStart = false;
             i++;
           }
           // we show a segment for each new point in the flightplan
           item = mapItemsConstructor.addMapItem(fplBuffer, i, XMLMapItem.TYPE_HORIZ, "A661_LINE_SEGMENT");
           item.addProperty("X", waypoint.getLatitude());
           item.addProperty("Y", waypoint.getLongitude());

           if (!it2.hasNext()) {
             // this is necessary to specify that the list of segments is finished
             item.addProperty("EndFlag", true);
           }
           i++;
         }
      }      

Listen to the worldModel notifications

The http://dassault-aviation.com/tacticalenv:worldModel service contains the zones and the FEBA. Note that we keep a WorldModel instance in our class. The following code ensures that the content of our model will be updated to the correct content when the service is received:
      private void receiveEnvWorld(ServiceInstance service) {
         // ensure that the content of our WorldModel instance is syncrhonized with the service
         wmodel.updateModel(service);

         // send the FEBA to the ARINC 661 server
         FEBA feba = wmodel.getFEBA();
         this.sendFEBA(feba);
         // send the zones to the ARINC 661 server
         Zones zones = wmodel.getZones();
         sendZones(zones);
      }      

Show the FEBA

The FEBA is just a list of points:
      private void sendFEBA(FEBA feba) {
         int LAYER_SITAC_ID = 1; // the layer ID of the SITAC Layer      
         int FEBA_ID = 40; // the widget ID of the FEBA MapHorzItemList widget
         int FEBA_STYLE = 1; // the style for the FEBA (FEBA is red)                      
         febaBuffer.reset();
         int i = 0;
         boolean isStart = true;
         ItemMap item;
         item = mapItemsConstructor.addMapItem(febaBuffer, i, XMLMapItem.TYPE_HORIZ, "A661_ITEM_STYLE");
         item.addProperty("ItemStyleSet", FEBA_STYLE); // FEBA is red
         i++;
         Iterator<Point> it = feba.getPoints().iterator();
         while (it.hasNext()) {
           Point point = it.next();
           if (isStart) {
             // add once the first point of the list of segments
             item = mapItemsConstructor.addMapItem(febaBuffer, i, XMLMapItem.TYPE_HORIZ, "A661_LINE_START");
             item.addProperty("X", point.getLatitude());
             item.addProperty("Y", point.getLongitude());
             i++;
             isStart = false;
           } else {
             // add each point in the list of points to draw each egment
             item = mapItemsConstructor.addMapItem(febaBuffer, i, XMLMapItem.TYPE_HORIZ, "A661_LINE_SEGMENT");
             item.addProperty("X", point.getLatitude());
             item.addProperty("Y", point.getLongitude());
             i++;
             if (!it.hasNext()) {
                // this is necessary to specify that the list of segments is finished
                item.addProperty("EndFlag", true);
             }
           }
         }
         api.setWidgetParameter(LAYER_SITAC_ID, FEBA_ID, ARINC661.A661_BUFFER_OF_MAPITEM, febaBuffer);
      }     

Notes

  1. ^ [1] [2] See Developing a UA for the reason why is should be in this method

See also


Categories: tutorials

Copyright 2017-2020 Dassault Aviation. All Rights Reserved. Documentation and source under the LGPL v3 licence