Program Listing for File FkinPurseDashboard.qml

Return to documentation for file (src/qml/components/FkinPurseDashboard.qml)

import QtQuick 2.11
import QtQuick.Layouts 1.11
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.4
import QtCharts 2.2

import fkin.Dds 1.0
import "fkinHelpers.js" as Fkin
import ratatosk 1.0

RowLayout {

  id: root;
  FkinStyle { id: fkinStyle; }
  property vector2d originNED: Qt.vector2d(63.4581027, 10.3683367);
  signal triggerReset;
  signal themeChanged;
  property bool plannerRunning: false;
  property bool ddsInitialized: false;

  property vector3d currentMagDirDepth: Qt.vector3d(0.25, 0, 0);
  property vector2d windMagDir: Qt.vector2d(4, 0.5);
  property vector2d fishCurrent: Qt.vector2d(0.35, 0.35); // also published dds
  property vector2d seaCurrent: Qt.vector2d(currentMagDirDepth.x*Math.cos(currentMagDirDepth.y),
                                            currentMagDirDepth.x*Math.sin(currentMagDirDepth.y));

  property point mouseTip: Qt.point(0,0);
  property bool mouseActive: false;
  property bool manualFishCtrl: manualFish.checked; // Togglable switch

  signal pointHovered(point pnt);

  onPointHovered:{
    mouseTip = pnt;
    mouseActive = true;
    hoverShow.restart();
  }

  Timer {
    id: hoverShow;
    interval: 1500;
    running: false;
    repeat: false;
    onTriggered: {
      root.mouseActive = false;
    }
  }

  property color vesselColor: Material.color(Material.Blue, Material.Shade500);
  property color deployColor: Material.color(Material.Blue, Material.Shade500);
  property color fishColor: Material.color(Material.DeepOrange, Material.Shade500);
  property color windColor: Material.color(Material.Cyan, Material.Shade500);
  property color currentColor: Material.color(Material.Indigo, Material.Shade500);

  FkinDdsTopics { id: topic; }

  // Notifies new solution (resets time since update timer)
  Connections {
    target: ddsVesselTrajectoryBuffer;
    onNewData:  root.triggerReset();
  }

  function init(ddsParticipant){

    // The if buffer size is too small, you will not get all data..
    ddsVesselTrajectoryBuffer.init(
      ddsParticipant, topic.vesselTrajectory, topic.idPursePlanner, 3000, true);
    ddsFishTrajectoryBuffer.init(
      ddsParticipant, topic.fishTrajectory, topic.idPursePlanner, 3000, true);

    ddsVesselPosInfo.init(ddsParticipant, topic.vesselPosInfo);
    vesselNED.init(ddsParticipant, topic.vesselPosInfo, topic.localNedTopic, "vessel");
    ddsVesselBuffer.init(ddsParticipant, topic.localNedTopic, "vessel", 30);

    ddsFishPosInfo.init(ddsParticipant, topic.fishPosInfo);
    fishNED.init(ddsParticipant, topic.fishPosInfo, topic.localNedTopic, "fish");
    fishRelative.init(ddsParticipant, topic.fishRelativePos);
    ddsFishBuffer.init(ddsParticipant, topic.localNedTopic, "fish", 30);

    currentSurface.init(ddsParticipant, topic.currentSurface, Qt.vector2d(0, 0), true);
    currentFish.init(ddsParticipant, topic.currentFish, root.fishCurrent, true);

    fishDepth.init(ddsParticipant, topic.fishDepth, topic.idFish, fishRelative.value.z, true);
    fishVelocityOverGround.init(ddsParticipant, topic.fishVelocityOverGround, Qt.vector2d(0, 0), false);

    // This is TEMOPORARY, TODO: use vessel pos info lat lon instead
    gpsOrigin.init(ddsParticipant, topic.gpsOrigin, root.originNED, true);

    vessel_rot_desired.init(ddsParticipant, topic.vesselRotDesired);

    vessel_deploy_position.init(ddsParticipant, topic.deployPosition);
    fish_collide_position.init(ddsParticipant, topic.collidePosition);
    vessel_deploy_time.init(ddsParticipant, topic.deployTime);

    keepSuggestion.init(ddsParticipant, topic.keepSolution, keeper.keepIt, true);




    }

  Item {
    id: dds;

    DdsKinematics2DBuffer { id: ddsVesselTrajectoryBuffer; }
    DdsKinematics2DBuffer { id: ddsFishTrajectoryBuffer; }

    RatatoskPosInfoSubscriber { id: ddsVesselPosInfo; }
    RatatoskPosInfoSubscriber { id: ddsFishPosInfo; }
    RatatoskDouble3Subscriber { id: fishRelative; }
    RatatoskDoubleValSubscriber { id: vessel_rot_desired; }
    RatatoskDouble2Subscriber { id: vessel_deploy_position; }
    RatatoskDouble2Subscriber { id: fish_collide_position; }
    RatatoskDoubleValSubscriber { id: vessel_deploy_time; }

    RatatoskDouble2Publisher { id: gpsOrigin; }
    RatatoskDouble2Publisher { id: currentSurface; value: root.seaCurrent; }
    RatatoskDouble2Publisher { id: currentFish;    value: root.fishCurrent; }
    DdsIdVec1dPublisher { id: fishDepth;    value: root.manualFishCtrl ? fishDepthPred.value : fishRelative.value.z; }
    RatatoskDouble2Publisher {
      id: fishVelocityOverGround;
      value: root.manualFishCtrl ?
        Qt.vector2d( fishSpeedPred.value*Math.cos(Math.PI*fishCoursePred.value/180),
                     fishSpeedPred.value*Math.sin(Math.PI*fishCoursePred.value/180)) :
        Qt.vector2d( ddsFishPosInfo.sog*Math.cos(Math.PI*ddsFishPosInfo.cog/180),
                     ddsFishPosInfo.sog*Math.sin(Math.PI*ddsFishPosInfo.cog/180));
    }



    // Forcefully send parameters regularly
    Timer {
      interval: 2000;
      running: true;
      repeat: true;
      onTriggered: {
        if(root.ddsInitialized)
        {
          currentSurface.publish();
          currentFish.publish();
          fishDepth.publish();
          fishVelocityOverGround.publish();
          gpsOrigin.publish();
        }
      }
    }
    /*
    Timer {
      interval: 10000;
      running: true;
      repeat: true;
      onTriggered: ddsVesselTrajectoryBuffer.clearBuffers();
    }*/

    TransformToNED {
      id: vesselNED;
      lat: root.originNED.x;
      lon: root.originNED.y;
    }
    DdsIdVec3dBuffer { id: ddsVesselBuffer; }


    TransformToNED {
      id: fishNED;
      lat: root.originNED.x;
      lon: root.originNED.y;
    }
    DdsIdVec3dBuffer { id: ddsFishBuffer; }

  }

  NorthEast {
    id: plot;
    legend.visible: false;
    legend.markerShape: Legend.MarkerShapeCircle;
    legend.alignment: Qt.AlignBottom;
    fovX: Qt.point(-100, 100);
    fovY: Qt.point(-100, 100);
    addMinFOV: false;
    axisX.tickCount: 8;
    axisY.tickCount: 8;
    margins.left: 10;
    margins.right: 10;
    margins.bottom: 10;
    margins.top: 10;

    // Use offseted axes instead based on vessel NED position
    axisX.visible: false;
    axisY.visible: false;

    function setStyle() {
      offY.labelsFont = fkinStyle.plotFont;
      offY.titleFont = fkinStyle.plotFont;
      offX.labelsFont = fkinStyle.plotFont;
      offX.titleFont = fkinStyle.plotFont;
      vesselTrack.setStyle();
      vesselNow.setStyle();
      vesselFuture.setStyle();
      fishTrack.setStyle();
      fishNow.setStyle();
      fishFuture.setStyle();
      plot.themeChanged();
      deployPos.setStyle();
      collidePos.setStyle();
    }


    Component.onCompleted: setStyle();

    ValueAxis {
      id: offX;
      titleText: qsTr("East")+ " [m]";
      labelsFont: plot.style.plotFont;
      tickCount: 8;
      min: plot.axisX.min;
      max: plot.axisX.max;
    }

    ValueAxis {
      id: offY;
      titleText: qsTr("North")+ " [m]";
      labelsFont: plot.style.plotFont;
      tickCount: 8;
      min: plot.axisY.min;
      max: plot.axisY.max;
    }

    LineSeries {
      id: dummy;
      axisXTop: offX;
      axisY: offY;
      visible: false;
    }


    Connections {
      target: root;
      onThemeChanged: plot.setStyle();
    }

    LineSeries {
      id: vesselTrack;
      name: qsTr("Vessel");
      // useOpenGL: true
      // style: Qt.DotLine // no effect with openGL
      axisXTop: plot.axisX;
      axisY: plot.axisY;

      onHovered: if(state){ root.pointHovered(point); }
      Component.onCompleted: setStyle();

      function setStyle(){
        color = root.vesselColor;
        width = 3;
      }

    }

    LineSeries {
      id: vesselFuture;
      name: qsTr("Vessel future");
      axisXTop: plot.axisX;
      axisY: plot.axisY;

      Component.onCompleted: setStyle();

      function setStyle(){
        color = root.vesselColor;
        width = 3;
        style = Qt.DotLine;
      }

      onHovered: if(state){ root.pointHovered(point); }

    }

    ScatterSeries {
      id: vesselNow;
      axisXTop: plot.axisX;
      axisY: plot.axisY;
      color: root.vesselColor;

      Component.onCompleted: setStyle();

      function setStyle(){
        color = root.vesselColor;
        borderWidth = 0;
        markerSize = 12;
        markerShape = ScatterSeries.MarkerShapeCircle;
      }

      onHovered: if(state){ root.pointHovered(point); }

    }

    ScatterSeries {
      id: deployPos;
      axisXTop: plot.axisX;
      axisY: plot.axisY;
      color: root.deployColor;

      Component.onCompleted: setStyle();

      function setStyle(){
        color = root.deployColor;
        borderWidth = 0;
        markerSize = 20;
        markerShape = ScatterSeries.MarkerShapeRectangle;
      }

      onHovered: if(state){ root.pointHovered(point); }

    }

    Connections {
      target: vessel_deploy_position;
      onValueChanged:
      {
        deployPos.remove(0);
        deployPos.append(value.y, value.x);
      }
    }

    ScatterSeries {
      id: collidePos;
      axisXTop: plot.axisX;
      axisY: plot.axisY;
      color: root.deployColor;

      Component.onCompleted: setStyle();

      function setStyle(){
        color = root.fishColor;
        borderWidth = 0;
        markerSize = 20;
        markerShape = ScatterSeries.MarkerShapeRectangle;
      }

      onHovered: if(state){ root.pointHovered(point); }

    }

    Connections {
      target: fish_collide_position;
      onValueChanged:
      {
        collidePos.remove(0);
        collidePos.append(value.y, value.x);
      }
    }

    Connections {
      target: ddsVesselBuffer;
      onNewData:
      {
        ddsVesselBuffer.updateSeries(vesselTrack, FKIN.Y, FKIN.X);
      }
      onRangeChanged:
      {
        // Note that things are opposite (x, y is switched due to North is y, but x)
        if(dim == FKIN.X)        {
          plot.equalizer.registerBox("Vessel",
                                     ddsVesselBuffer.rangeY,
                                     Qt.point(range.x-50, range.y + 50));
        }
        if(dim == FKIN.Y){
          plot.equalizer.registerBox("Vessel",
                                     Qt.point(range.x-50, range.y + 50),
                                     ddsVesselBuffer.rangeX);
        }
      }
    }

    Connections {
      target: vesselNED.ned; //ddsVesselNow;
      onValueChanged:
      {
        vesselNow.remove(0);
        vesselNow.append(value.y, value.x);

        offX.min = plot.axisX.min - value.y;
        offX.max = plot.axisX.max - value.y;
        offY.min = plot.axisY.min - value.x;
        offY.max = plot.axisY.max - value.x;
        plot.equalizer.registerBox("VesselNow",
                                   Qt.point(value.y-20, value.y+20),
                                   Qt.point(value.x-20, value.x+20));
        plot.equalizer.registerBox("VesselNowBig",
                                   Qt.point(value.y-250, value.y+250),
                                   Qt.point(value.x-250, value.x+250));

        /* With this the FOV grows.
        var middleX = 2*(value.y - (plot.axisX.min +  (plot.axisX.max - plot.axisX.min)/2));
        var rngX = Qt.point(plot.axisX.min + (middleX<0)*middleX + 10,
                           plot.axisX.max + (middleX>0)*middleX - 10);
        var middleY = 2*(value.y - (plot.axisX.min +  (plot.axisX.max - plot.axisX.min)/2));
        var rngY = Qt.point(plot.axisY.min + (middleY<0)*middleY + 10,
                            plot.axisY.max + (middleY>0)*middleY - 10);

        plot.equalizer.registerBox("VesselInMiddle", rngX, rngY);
        */
      }
    }


    Connections {
      target: ddsVesselTrajectoryBuffer;
      onNewData:
      {
        // remove old
        ddsVesselTrajectoryBuffer.updateSeries(vesselFuture, FKIN.PosY, FKIN.PosX);
        ddsVesselTrajectoryBuffer.clearBuffers(); // remove after consumption.
      }
      onRangeChanged:
      {
        // Note that things are opposite (x, y is switched due to North is y, but x)
        if(dim == FKIN.PosX){
          plot.equalizer.registerBox("VesselFuture",
                                     ddsVesselTrajectoryBuffer.rangePosY,
                                     Qt.point(range.x-50, range.y+50));
        }

        if(dim == FKIN.PosY){
          plot.equalizer.registerBox("VesselFuture",
                                     Qt.point(range.x-50, range.y+50),
                                     ddsVesselTrajectoryBuffer.rangePosX);
        }
      }
    }


    LineSeries {
      id: fishTrack;
      name: qsTr("Fish");
      // useOpenGL: true
      // style: Qt.DotLine // no effect with openGL
      axisXTop: plot.axisX;
      axisY: plot.axisY;

      onHovered: if(state){ root.pointHovered(point); }
      Component.onCompleted: setStyle();

      function setStyle(){
        color = root.fishColor;
        width = 3;
      }
    }

    LineSeries {
      id: fishFuture;
      name: qsTr("Fish future");
      axisXTop: plot.axisX;
      axisY: plot.axisY;

      onHovered: if(state){ root.pointHovered(point); }
      Component.onCompleted: setStyle();

      function setStyle(){
        color = root.fishColor;
        width = 3;
        style = Qt.DotLine;
      }
    }

    ScatterSeries {
      id: fishNow;
      axisXTop: plot.axisX;
      axisY: plot.axisY;
      color: root.fishColor;

      onHovered: if(state){ root.pointHovered(point); }
      Component.onCompleted: setStyle();

      function setStyle(){
        color = root.fishColor;
        borderWidth = 0;
        markerSize = 12;
        markerShape = ScatterSeries.MarkerShapeCircle;
      }
    }

    Connections {
      target: ddsFishBuffer;
      onNewData:
      {
        ddsFishBuffer.updateSeries(fishTrack, FKIN.Y, FKIN.X);
      }
      onRangeChanged:
      {
        // Note that things are opposite (x, y is switched due to North is y, but x)
        if(dim == FKIN.X)
          plot.equalizer.registerBox("Fish", ddsFishBuffer.rangeY, range);
        if(dim == FKIN.Y)
          plot.equalizer.registerBox("Fish", range, ddsFishBuffer.rangeX);
      }
    }

    Connections {
      target: fishNED.ned;
      onValueChanged:
      {
        fishNow.remove(0);
        fishNow.append(value.y, value.x);

        plot.equalizer.registerBox("FishNow",
                                   Qt.point(value.y-20, value.y+20),
                                   Qt.point(value.x-20, value.x+20));

      }
    }

    Connections {
      target: ddsFishTrajectoryBuffer;
      onNewData:
      {
        ddsFishTrajectoryBuffer.updateSeries(fishFuture, FKIN.PosY, FKIN.PosX);
        ddsFishTrajectoryBuffer.clearBuffers(); // remove after consumption.

      }
      onRangeChanged:
      {
        // Note that things are opposite (x, y is switched due to North is y, but x)
        if(dim == FKIN.PosX){
          plot.equalizer.registerBox("FishFuture",
                                     ddsFishTrajectoryBuffer.rangePosY,
                                     Qt.point(range.x-50, range.y+50));
        }

        if(dim == FKIN.PosY){
          plot.equalizer.registerBox("FishFuture",
                                     Qt.point(range.x-50, range.y+50),
                                     ddsFishTrajectoryBuffer.rangePosX);
        }
      }
    }

  }
  ColumnLayout {
    id: infotain;
    Layout.alignment: Qt.AlignTop;
    Layout.topMargin: 30;
    Layout.rightMargin: 10;

    function toDegrees(rad){
      // returns -180, 180.
      var deg = Math.atan2(Math.sin(rad), Math.cos(rad))*180/Math.PI;
      deg += (deg < 0)*360; // maps negative rot to [180,360]
      return deg;
    }

    function toKnots(mps){
      return mps*3.6/1.852;
    }

    RowLayout {
      Layout.alignment: Qt.AlignCenter;
      Layout.bottomMargin: 20;
      Layout.rightMargin: 20;

      Label {
        text: "\uf022";
        font: fkinStyle.iconFontBig;
        Layout.alignment: Qt.AlignRight | Qt.AlignVCenter;
      }
      Label {
        id: clock;
        font.pointSize: fkinStyle.numberFont.pointSize + 2;
        property date klokka: new Date();
        text: klokka.toLocaleString(Qt.locale(), "hh:mm:ss");
        Layout.alignment: Qt.AlignCenter;
        Timer {
          interval: 1000;
          running: true;
          repeat: true;
          onTriggered: {
            var time = new Date;
            clock.klokka = time;
          }
        }
      }
    }

    InfoDirectionMagnitude {
      id: wind;
      Layout.alignment: Qt.AlignRight;
      Layout.bottomMargin: 15;
      entity: "\uee98"; // wind
      description: qsTr("Wind");
      arrow: "\uea5b"; // down-arrow since wind direction from where it originates
      unit: "kn";
      colorFont: root.windColor;
      magnitude: infotain.toKnots(root.windMagDir.x);
      orientation: infotain.toDegrees(root.windMagDir.y);
    }
    InfoDirectionMagnitude {
      id: current;
      Layout.alignment: Qt.AlignRight;
      Layout.bottomMargin: 15;
      entity: "\uee97"; // wind-waves
      description: qsTr("Current");
      unit: "kn";
      colorFont: root.currentColor;
      magnitude: infotain.toKnots(root.currentMagDirDepth.x);
      orientation: infotain.toDegrees(root.currentMagDirDepth.y);
      extra: Number(root.currentMagDirDepth.z).toLocaleString(Qt.locale(), 'f', 0) + " m";

    }
    InfoDirectionMagnitude {
      id: currentDeep;
      Layout.alignment: Qt.AlignRight;
      Layout.bottomMargin: 15;
      entity: "\uee97"; // wind-waves
      description: qsTr("Current at depth");
      unit: "kn";
      colorFont: root.currentColor;
      magnitude: infotain.toKnots(root.fishCurrent.length());
      orientation: infotain.toDegrees(Math.atan2(root.fishCurrent.y, root.fishCurrent.x));
      extra: isNaN(fishRelative.value.z) ? "-" :
        Number(fishRelative.value.z).toLocaleString(Qt.locale(), 'f', 0) + " m";

    }
    InfoDirectionMagnitude {
      id: vessel;
      Layout.alignment: Qt.AlignRight;
      Layout.bottomMargin: 15;
      entity: "\uee34"; // ship
      description: qsTr("Ship: speed and course over ground");
      unit: "kn";
      colorFont: root.vesselColor;
      magnitude: infotain.toKnots(ddsVesselPosInfo.sog);
      orientation: ddsVesselPosInfo.cog;

    }
    InfoDirectionMagnitude {
      id: fish;
      Layout.alignment: Qt.AlignRight;
      Layout.bottomMargin: 5;
      entity: "\ue850"; // fish-2
      description: qsTr("Fish: speed and course over ground");
      unit: "kn";
      colorFont: root.fishColor;
      magnitude: infotain.toKnots(ddsFishPosInfo.sog);
      orientation: ddsFishPosInfo.cog;
      extra: isNaN(fishRelative.value.z) ? "-" :
        Number(fishRelative.value.z).toLocaleString(Qt.locale(), 'f', 0) + " m";
    }
    ProgressBar {
      // Horizontal line
      implicitWidth: parent.width;
      value: 1.0;
      Layout.alignment: Qt.AlignLeft;
      Layout.bottomMargin: 5;
    }

    RowLayout {
      id: debugItem;
      visible: false;
      implicitWidth: parent.width;
      Label {
        id: icon_;
        text: "\ueec7"; // bug
        font: fkinStyle.iconFontBig;
        Layout.rightMargin: 10;
      }
      Button {
        id: clearGraphs;
        text: qsTr("Clear buffers");
        Layout.alignment: Qt.AlignRight;


        onClicked:
        {
          ddsVesselBuffer.clearBuffers();
          ddsFishBuffer.clearBuffers();
          ddsVesselTrajectoryBuffer.clearBuffers();
          ddsFishTrajectoryBuffer.clearBuffers();
          //fishNow.remove(0);
          //vesselNow.remove(0);

        }
      }
    }

    Label { Layout.fillHeight: true; } // Empty Filler

    // Captain's indicating fish speed and direction

    InfoDirectionMagnitude {
      id: fish_input;
      Layout.alignment: Qt.AlignRight;
      Layout.bottomMargin: 20;
      entity: "\ue850"; // fish-2
      description: qsTr("Captain's prediction of fish speed and course over ground");
      unit: "kn";
      colorFont: root.manaulFishCtrl ? root.fishColor : Material.color(Material.Grey);
      magnitude: infotain.toKnots(fishSpeedPred.value);
      orientation: fishCoursePred.value;
      extra: isNaN(fishDepthPred.value) ? "-" :
        Number(fishDepthPred.value).toLocaleString(Qt.locale(), 'f', 0) + " m";

      //visible: root.manualFishCtrl;
    }

    ColumnLayout {
      Layout.alignment: Qt.AlignCenter;
      Layout.bottomMargin: 10;
      RowLayout {
        ColumnLayout {
          RowLayout {
            Label { text: qsTr("Course"); }
            Slider {
              id: fishCoursePred;
              from: 0;
              value: 0;
              to: 360;
              stepSize: 2;
              snapMode: Slider.SnapOnRelease;
            }
          }
          RowLayout {
            Label { text: qsTr("Speed"); }
            Slider {
              id: fishSpeedPred;
              from: 0.05;
              value: 2;
              to: 4;
              stepSize: 0.05;
              snapMode: Slider.SnapOnRelease;
            }
          }
          RowLayout {
            Label { text: qsTr("Depth"); }
            Slider {
              id: fishDepthPred;
              from: 0;
              value: 50;
              to: 120;
              stepSize: 1;
              snapMode: Slider.SnapOnRelease;
            }
          }
        }
        ColumnLayout {

          Label {
            font: fkinStyle.iconFontHuge;
            text: "\ue850"; // fish
            color: root.manualFishCtrl ? root.fishColor : Material.color(Material.Grey);
            Layout.alignment: Qt.AlignCenter;
            property string toolTipText: qsTr("Manual fish input");
            ToolTip.text: toolTipText;
            ToolTip.visible: toolTipText ? ma_fish.containsMouse : false;
            MouseArea {
              id: ma_fish;
              anchors.fill: parent;
              hoverEnabled: true;
            }
          }
          Switch {
            id: manualFish; rotation: 270;
            Layout.alignment: Qt.AlignCenter;

            onToggled:
            {
              if(checked)
                fish_input.colorFont = root.fishColor;
              else
                fish_input.colorFont = Material.color(Material.Grey);

            }
          }
        }
      }
    }

    InfoPlanning {
      id: plan;
      entity: "\uf020"; // ef8a
      title: qsTr("Path Planner");
      description: qsTr("Duration since new path");
      running: root.plannerRunning;
      down: false;
      expected: 4000; // expected period of optimization

      Connections {
        target: root;
        onTriggerReset: plan.reseted();
      }

    }

    RowLayout {
      FkinDecisionIndicator {
        id: recommendedRotIndicator;

        value: vessel_rot_desired.val;
        running: root.plannerRunning;
        entity: "\ue81b"; // ship-wheel
        description: qsTr("Recommended rate of turn, deg/sec");
        unit: " \u00b0/s";
      }


      FkinDecisionIndicator {
        id: recommendedSettingTime;
        entity: "\uebb1"; // cannon-firing, \ueeca :bullet
        description: qsTr("Countdown to deploy");
        unit: "";
        running: root.plannerRunning;

        Connections {
          target: vessel_deploy_time;
          onValChanged:
          {
            recommendedSettingTime.count = val;
          }
        }
        property double count: 0;
        valueText: !root.plannerRunning ? "00:00" :
          new Date(recommendedSettingTime.count*1000).toLocaleTimeString(
            Qt.locale(), "mm:ss");

        Timer {
          id: settingTimer;
          interval: 500;
          running: root.plannerRunning;
          repeat: true;
          onTriggered:
          {
            recommendedSettingTime.count = recommendedSettingTime.count - settingTimer.interval/1000;
            if(recommendedSettingTime.count < 0){
              recommendedSettingTime.count = 0;
            }
          }
        }
      }
    }

    RowLayout {
      Layout.alignment: Qt.AlignCenter;
      id: keeper;
      property bool keepIt: false;
      DdsBitPublisher {
        id: keepSuggestion;
      }
      Button {
        id: keepTrajectory;
        text: keeper.keepIt ? qsTr("Reject") : qsTr("Keep");
        Layout.fillWidth: false;
        onClicked: {
          keepSuggestion.signal = !keeper.keepIt;
          keeper.keepIt = !keeper.keepIt;
        }
      }
    }

    RowLayout {
      id: mapPosition;
      Label {
        text: "\uef8a";
        font: fkinStyle.iconFontBig;
        Layout.rightMargin: 10;
      }
      Label {
        font: fkinStyle.numberFont;
        text: isNaN(vesselNED.ned.value.x) || !root.mouseActive ? "" :
          Number(root.mouseTip.y - vesselNED.ned.value.x).toLocaleString(
            Qt.locale(),'f', 0) + ", " +
          Number(root.mouseTip.x - vesselNED.ned.value.y).toLocaleString(
            Qt.locale(),'f', 0) + " m";
        Layout.preferredWidth: 200;
      }
      Label {
        font: fkinStyle.numberFont;
        text: isNaN(vesselNED.ned.value.x) || !root.mouseActive ? "" :
          qsTr("Distance: ") + Number(Qt.vector2d(root.mouseTip.y - vesselNED.ned.value.x,
                             root.mouseTip.x - vesselNED.ned.value.y).length())
          .toLocaleString(Qt.locale(),'f', 0) + " m";
      }
    }
  }
}