Implemented map history save and display on a map
authorzev <zev@radensolutions.com>
Fri, 15 Aug 2014 15:40:07 +0000 (18:40 +0300)
committerzev <zev@radensolutions.com>
Fri, 15 Aug 2014 15:40:07 +0000 (18:40 +0300)
29 files changed:
ChangeLog
include/geolocation.h
include/netxmsdb.h
include/nms_cscp.h
include/nxclapi.h
sql/metadata.in
src/java/netxms-base/src/main/java/org/netxms/base/GeoLocation.java
src/java/netxms-base/src/main/java/org/netxms/base/NXCPCodes.java
src/java/netxms-client/src/main/java/org/netxms/client/NXCSession.java
src/java/netxms-eclipse/Dashboard/src/org/netxms/ui/eclipse/dashboard/widgets/GeoMapElement.java
src/java/netxms-eclipse/OSM/plugin.xml
src/java/netxms-eclipse/OSM/src/org/netxms/ui/eclipse/osm/actions/OpenHistoryMap.java [new file with mode: 0644]
src/java/netxms-eclipse/OSM/src/org/netxms/ui/eclipse/osm/views/AbstractGeolocationView.java
src/java/netxms-eclipse/OSM/src/org/netxms/ui/eclipse/osm/views/HistoryView.java [copied from webui/webapp/OSM/src/org/netxms/ui/eclipse/osm/views/AbstractGeolocationView.java with 73% similarity]
src/java/netxms-eclipse/OSM/src/org/netxms/ui/eclipse/osm/widgets/GeoMapViewer.java
src/libnetxms/geolocation.cpp
src/libnetxms/nxcp.cpp
src/server/core/mobile.cpp
src/server/core/netobj.cpp
src/server/core/session.cpp
src/server/include/nms_core.h
src/server/include/nms_objects.h
src/server/tools/nxdbmgr/upgrade.cpp
webui/webapp/Dashboard/src/org/netxms/ui/eclipse/dashboard/widgets/GeoMapElement.java
webui/webapp/OSM/plugin.xml
webui/webapp/OSM/src/org/netxms/ui/eclipse/osm/actions/OpenHistoryMap.java [new file with mode: 0644]
webui/webapp/OSM/src/org/netxms/ui/eclipse/osm/views/AbstractGeolocationView.java
webui/webapp/OSM/src/org/netxms/ui/eclipse/osm/views/HistoryView.java [copied from webui/webapp/OSM/src/org/netxms/ui/eclipse/osm/views/AbstractGeolocationView.java with 73% similarity]
webui/webapp/OSM/src/org/netxms/ui/eclipse/osm/widgets/GeoMapViewer.java

index e2e8300..ac3da3f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,8 @@
 - Management console:
        - Can show alarms for multiple selected objects
        - Fixed non-working ordering in event list in alarm details view
+    - Fixed bug with LDAP user system rights
+    - Added device geolocation tracking and display on map 
 - Fixed issues: #593, #613, #624
 
 
index 5dabf70..4c55ea6 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Copyright (C) 2003-2013 Victor Kirhenshtein
 **
@@ -77,6 +77,7 @@ public:
        bool isValid() { return m_isValid; }
        int getAccuracy() { return m_accuracy; }
        time_t getTimestamp() { return m_timestamp; }
+   bool sameLocation(const TCHAR* loc, const TCHAR* lat, int oldAccurasy);
 
        void fillMessage(CSCPMessage &msg);
 };
index 95fdb43..7e7891e 100644 (file)
@@ -23,6 +23,6 @@
 #ifndef _netxmsdb_h
 #define _netxmsdb_h
 
-#define DB_FORMAT_VERSION   332
+#define DB_FORMAT_VERSION   333
 
 #endif
index 7c6c9f6..7d6066d 100644 (file)
@@ -494,6 +494,7 @@ typedef struct
 #define CMD_FILEMGR_UPLOAD             0x012A
 #define CMD_GET_SWITCH_FDB             0x012B
 #define CMD_COMMAND_OUTPUT             0x012C
+#define CMD_GET_LOC_HISTORY            0x012D
 
 #define CMD_RS_LIST_REPORTS            0x1100
 #define CMD_RS_GET_REPORT              0x1101
@@ -1164,6 +1165,8 @@ typedef struct
 
 #define VID_FILE_LIST_BASE          ((UINT32)0x10000000)
 
+#define VID_LOC_LIST_BASE           ((UINT32)0x10000000)
+
 //
 // Inline functions
 //
index 901680b..13d4ddc 100644 (file)
@@ -537,6 +537,7 @@ enum
 #define RCC_NO_LDAP_CONNECTION       ((UINT32)112)
 #define RCC_NO_ROUTING_TABLE         ((UINT32)113)
 #define RCC_NO_FDB                   ((UINT32)114)
+#define RCC_NO_LOC_HISTORY           ((UINT32)115)
 
 /**
  * Mask bits for NXCModifyEventTemplate()
index 2da248b..0cd7745 100644 (file)
@@ -42,3 +42,6 @@ INSERT INTO metadata (var_name,var_value)
        VALUES ('TDataIndexCreationCommand_1','CREATE INDEX idx_tdata_rec_%d_instance ON tdata_records_%d(instance)');
 INSERT INTO metadata (var_name,var_value)
        VALUES ('TDataIndexCreationCommand_2','CREATE INDEX idx_tdata_rec_%d_id ON tdata_records_%d(record_id)');
+
+INSERT INTO metadata (var_name,var_value)
+       VALUES ('LocationHistory','CREATE TABLE gps_history_%d (latitude varchar(20), longitude varchar(20), accuracy integer not null, start_timestamp integer not null, end_timestamp integer not null, PRIMARY KEY(start_timestamp))');
index 73ea89e..8b7dc9a 100644 (file)
@@ -23,13 +23,11 @@ import java.util.Date;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.netxms.base.NXCPCodes;
-import org.netxms.base.NXCPMessage;
-
 /**
  * Geolocation encoding
  */
-public class GeoLocation
+@SuppressWarnings("rawtypes")
+public class GeoLocation implements Comparable
 {
        private static final double ROUND_OFF = 0.00000001;
 
@@ -43,6 +41,7 @@ public class GeoLocation
        private double longitude;
        private int accuracy;   // Location accuracy in meters
        private Date timestamp;
+       private Date endTimestamp;
        
        /**
         * Create geolocation object from NXCP message
@@ -58,6 +57,20 @@ public class GeoLocation
        }
        
        /**
+    * Create geolocation object from NXCP message
+    * @param msg NXCP message
+    */
+   public GeoLocation(long base, final NXCPMessage msg)
+   {
+      type = 0;
+      latitude = msg.getVariableAsReal(base);
+      longitude = msg.getVariableAsReal(base+1);
+      accuracy = msg.getVariableAsInteger(base+2);
+      timestamp = msg.getVariableAsDate(base+3);
+      endTimestamp = msg.getVariableAsDate(base+4);
+   }
+       
+       /**
         * Create geolocation object of type UNSET or GPS
         */
        public GeoLocation(boolean isGPS)
@@ -342,4 +355,35 @@ public class GeoLocation
                double _lon = parse(lon, false);
                return new GeoLocation(_lat, _lon);
        }
+
+   @Override
+   public int compareTo(Object arg0)
+   {
+      GeoLocation loc = (GeoLocation)arg0;
+      return timestamp.compareTo(loc.getTimestamp());
+   }
+
+   /**
+    * @return the end_timestamp
+    */
+   public Date getEndTimestamp()
+   {
+      return endTimestamp;
+   }
+
+   /**
+    * @param end_timestamp the end_timestamp to set
+    */
+   public void setEndTimestamp(Date endTimestamp)
+   {
+      this.endTimestamp = endTimestamp;
+   }
+   
+   public boolean equals(Object obj)
+   {
+      if(! (obj instanceof GeoLocation))
+         return false;
+      GeoLocation loc = (GeoLocation)obj;      
+      return loc.getTimestamp().equals(getTimestamp());      
+   }
 }
index 6f5add4..5dfde64 100644 (file)
@@ -319,6 +319,8 @@ public class NXCPCodes
    public static final int CMD_FILEMGR_MOVE_FILE = 0x0129;
    public static final int CMD_FILEMGR_UPLOAD = 0x012A;
    public static final int CMD_GET_SWITCH_FDB = 0x012B;
+   public static final int CMD_COMMAND_OUTPUT = 0x012C;
+   public static final int CMD_GET_LOC_HISTORY = 0x012D;
        
        // CMD_RS_ - Reporting Server related codes
        public static final int CMD_RS_LIST_REPORTS = 0x1100;
@@ -901,4 +903,5 @@ public class NXCPCodes
        public static final long VID_RULE_LIST_BASE = 0x10000000L;
        public static final long VID_EXTENSION_LIST_BASE = 0x10000000L;
        public static final long VID_DCI_ID_LIST_BASE = 0x10000000L;
+   public static final long VID_LOC_LIST_BASE = 0x10000000L;
 }
index 921f364..16371ea 100644 (file)
@@ -7727,4 +7727,28 @@ public class NXCSession implements Session, ScriptLibraryManager, UserManager, S
       sendMessage(msg);
    }
    
+   
+   public List<GeoLocation> getLocationHistory(long objId, Date from, Date to) throws NXCException, IOException
+   {
+      final NXCPMessage msg = newMessage(NXCPCodes.CMD_GET_LOC_HISTORY);
+
+      int timeFrom = (from != null) ? (int) (from.getTime() / 1000) : 0;
+      int timeTo = (to != null) ? (int) (to.getTime() / 1000) : 0;
+      
+      msg.setVariableInt32(NXCPCodes.VID_OBJECT_ID, (int)objId);
+      msg.setVariableInt32(NXCPCodes.VID_TIME_FROM, (int)timeFrom);
+      msg.setVariableInt32(NXCPCodes.VID_TIME_TO, (int)timeTo);
+      sendMessage(msg);
+      
+      NXCPMessage response = waitForRCC(msg.getMessageId());
+      int size = response.getVariableAsInteger(NXCPCodes.VID_NUM_RECORDS);
+      List <GeoLocation> elements = new ArrayList<GeoLocation>();
+      long i, base;
+      for(i = 0, base = NXCPCodes.VID_LOC_LIST_BASE; i < size; i++, base += 10)
+      {
+         elements.add(new GeoLocation(base, response));
+      }
+      Collections.sort(elements);
+      return elements;
+   }
 }
index 0cc04d5..eac9441 100644 (file)
@@ -57,7 +57,7 @@ public class GeoMapElement extends ElementWidget
                layout.marginWidth = 0;
                setLayout(layout);
                
-               mapWidget = new GeoMapViewer(this, SWT.NONE);
+               mapWidget = new GeoMapViewer(this, SWT.NONE, false, null);
                mapWidget.setViewPart(viewPart);
                mapWidget.setTitle(config.getTitle());
                mapWidget.showMap(config.getLatitude(), config.getLongitude(), config.getZoom());
index 80d76a7..201f627 100644 (file)
             name="%view.name.WorldMap"
             restorable="true">
       </view>
+      <view
+            allowMultiple="true"
+            class="org.netxms.ui.eclipse.osm.views.HistoryView"
+            icon="icons/map.png"
+            id="org.netxms.ui.eclipse.osm.views.HistoryView"
+            name="Geolocation History">
+      </view>
    </extension>
 
    <extension
                label="%action.label.Geolocation"
                menubarPath="additions">
          </action>
+         <action
+               class="org.netxms.ui.eclipse.osm.actions.OpenHistoryMap"
+               enablesFor="1"
+               icon="icons/map.png"
+               id="org.netxms.ui.eclipse.osm.actions.OpenHistoryMap#MobileDevice"
+               label="Geolocation History"
+               menubarPath="additions">
+         </action>
       </objectContribution>
    </extension>
    <extension
diff --git a/src/java/netxms-eclipse/OSM/src/org/netxms/ui/eclipse/osm/actions/OpenHistoryMap.java b/src/java/netxms-eclipse/OSM/src/org/netxms/ui/eclipse/osm/actions/OpenHistoryMap.java
new file mode 100644 (file)
index 0000000..ba8a5eb
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2003-2011 Victor Kirhenshtein
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.ui.eclipse.osm.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.netxms.base.GeoLocation;
+import org.netxms.client.objects.AbstractObject;
+import org.netxms.ui.eclipse.osm.views.HistoryView;
+import org.netxms.ui.eclipse.tools.MessageDialogHelper;
+
+/**
+ * Object action: open geolocation view
+ */
+public class OpenHistoryMap implements IObjectActionDelegate
+{
+       private IWorkbenchWindow window;
+       private AbstractObject object;
+       
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction, org.eclipse.ui.IWorkbenchPart)
+        */
+       @Override
+       public void setActivePart(IAction action, IWorkbenchPart targetPart)
+       {
+               window = targetPart.getSite().getWorkbenchWindow();
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
+        */
+       @Override
+       public void run(IAction action)
+       {
+               if(window != null)
+               {       
+                  //Open dialog
+                       try 
+                       {
+                               window.getActivePage().showView(HistoryView.ID, Long.toString(object.getObjectId()), IWorkbenchPage.VIEW_ACTIVATE);
+                       } 
+                       catch (PartInitException e) 
+                       {
+                               MessageDialogHelper.openError(window.getShell(), "Error while opening historical map", "Error while opening historical map: " + e.getMessage());
+                       }
+               }
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection)
+        */
+       @Override
+       public void selectionChanged(IAction action, ISelection selection)
+       {
+               Object obj;
+               if ((selection instanceof IStructuredSelection) &&
+                        ((obj = ((IStructuredSelection)selection).getFirstElement()) instanceof AbstractObject))
+               {
+                       object = (AbstractObject)obj;
+               }
+               else
+               {
+                       object = null;
+               }
+               action.setEnabled((object != null) && (object.getGeolocation().getType() != GeoLocation.UNSET));
+       }
+}
index 8779972..d9c6451 100644 (file)
@@ -93,7 +93,7 @@ public abstract class AbstractGeolocationView extends ViewPart
        public void createPartControl(Composite parent)
        {
                // Map control
-               map = new GeoMapViewer(parent, SWT.BORDER);
+               map = new GeoMapViewer(parent, SWT.BORDER, false, null);
                map.setViewPart(this);
                
                createActions();
@@ -24,6 +24,7 @@ import org.eclipse.jface.action.IMenuListener;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Menu;
@@ -33,6 +34,7 @@ import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.part.ViewPart;
 import org.netxms.base.GeoLocation;
 import org.netxms.client.NXCSession;
+import org.netxms.client.objects.AbstractObject;
 import org.netxms.ui.eclipse.console.resources.SharedIcons;
 import org.netxms.ui.eclipse.osm.Messages;
 import org.netxms.ui.eclipse.osm.tools.MapAccessor;
@@ -43,9 +45,15 @@ import org.netxms.ui.eclipse.shared.ConsoleSharedData;
 /**
  * Base class for all geographical views
  */
-public abstract class AbstractGeolocationView extends ViewPart
+public class HistoryView extends ViewPart
 {
+   public static final String ID = "org.netxms.ui.eclipse.osm.views.HistoryView"; //$NON-NLS-1$
        public static final String JOB_FAMILY = "MapViewJob"; //$NON-NLS-1$
+       private static final int[] presetRanges = { 10, 30, 60, 120, 240, 720, 1440, 2880, 7200, 10080, 44640, 525600 };
+   private static final String[] presetNames = 
+      { "10 minutes", "30 minutes", "1 hour", "2 hours", "4 hours", "12 hours", "Today",
+        "Last 2 days", "Last 5 days", "This week", "This month","This Year" };
+
        
        protected GeoMapViewer map;
        
@@ -53,20 +61,28 @@ public abstract class AbstractGeolocationView extends ViewPart
        private int zoomLevel = 15;
        private Action actionZoomIn;
        private Action actionZoomOut;
+   private Action[] presetActions;
+       private AbstractObject object;
        
        /**
         * Get initial center point for displayed map
         * 
         * @return
         */
-       protected abstract GeoLocation getInitialCenterPoint();
+       protected GeoLocation getInitialCenterPoint() 
+       {
+          return object.getGeolocation();
+       }
        
        /**
         * Get initial zoom level
         * 
         * @return
         */
-       protected abstract int getInitialZoomLevel();
+       protected int getInitialZoomLevel()
+       {
+      return 15;
+       }
 
        /* (non-Javadoc)
         * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
@@ -84,6 +100,19 @@ public abstract class AbstractGeolocationView extends ViewPart
                catch(Exception e)
                {
                }
+               
+               try
+      {
+         long id = Long.parseLong(site.getSecondaryId());
+         object = ((NXCSession)ConsoleSharedData.getSession()).findObjectById(id);
+         setPartName(Messages.get().LocationMap_PartNamePrefix + object.getObjectName());
+      }
+      catch(Exception e)
+      {
+         throw new PartInitException(Messages.get().LocationMap_InitError1, e);
+      }
+      if (object == null)
+         throw new PartInitException(Messages.get().LocationMap_InitError2);
        }
        
        /* (non-Javadoc)
@@ -93,7 +122,7 @@ public abstract class AbstractGeolocationView extends ViewPart
        public void createPartControl(Composite parent)
        {
                // Map control
-               map = new GeoMapViewer(parent, SWT.BORDER);
+               map = new GeoMapViewer(parent, SWT.BORDER, true, object);
                map.setViewPart(this);
                
                createActions();
@@ -110,7 +139,7 @@ public abstract class AbstractGeolocationView extends ViewPart
                        @Override
                        public void onZoom(int zoomLevel)
                        {
-                               AbstractGeolocationView.this.zoomLevel = zoomLevel;
+                               HistoryView.this.zoomLevel = zoomLevel;
                                mapAccessor.setZoom(zoomLevel);
                                actionZoomIn.setEnabled(zoomLevel < 18);
                                actionZoomOut.setEnabled(zoomLevel > 0);
@@ -147,6 +176,19 @@ public abstract class AbstractGeolocationView extends ViewPart
                        }
                };
                actionZoomOut.setImageDescriptor(SharedIcons.ZOOM_OUT);
+               
+               presetActions = new Action[presetRanges.length];
+      for(int i = 0; i < presetRanges.length; i++)
+      {
+         final Integer presetIndex = i;
+         presetActions[i] = new Action(presetNames[i]) {
+            @Override
+            public void run()
+            {
+               map.changeTimePeriod(presetRanges[presetIndex]);
+            }
+         };
+      }
        }
 
        /**
@@ -166,6 +208,12 @@ public abstract class AbstractGeolocationView extends ViewPart
         */
        protected void fillLocalPullDown(IMenuManager manager)
        {
+          MenuManager presets = new MenuManager("&Presets");
+      for(int i = 0; i < presetActions.length; i++)
+         presets.add(presetActions[i]);
+      
+      manager.add(presets);
+      manager.add(new Separator());       
                manager.add(actionZoomIn);
                manager.add(actionZoomOut);
        }
@@ -209,6 +257,12 @@ public abstract class AbstractGeolocationView extends ViewPart
         */
        protected void fillContextMenu(final IMenuManager manager)
        {
+          MenuManager presets = new MenuManager("&Presets");
+      for(int i = 0; i < presetActions.length; i++)
+         presets.add(presetActions[i]);
+      
+      manager.add(presets);
+      manager.add(new Separator());
                manager.add(actionZoomIn);
                manager.add(actionZoomOut);
        }
index fee4f3f..dddd35a 100644 (file)
@@ -19,6 +19,7 @@
 package org.netxms.ui.eclipse.osm.widgets;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -30,6 +31,7 @@ import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.MouseListener;
 import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackAdapter;
 import org.eclipse.swt.events.MouseWheelListener;
 import org.eclipse.swt.events.PaintEvent;
 import org.eclipse.swt.events.PaintListener;
@@ -44,9 +46,11 @@ import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ToolTip;
 import org.eclipse.ui.IViewPart;
 import org.eclipse.ui.model.WorkbenchLabelProvider;
 import org.netxms.base.GeoLocation;
+import org.netxms.client.NXCSession;
 import org.netxms.client.objects.AbstractObject;
 import org.netxms.ui.eclipse.console.resources.SharedColors;
 import org.netxms.ui.eclipse.console.resources.SharedIcons;
@@ -59,15 +63,22 @@ import org.netxms.ui.eclipse.osm.Messages;
 import org.netxms.ui.eclipse.osm.tools.Area;
 import org.netxms.ui.eclipse.osm.tools.MapAccessor;
 import org.netxms.ui.eclipse.osm.tools.MapLoader;
+import org.netxms.ui.eclipse.osm.tools.QuadTree;
 import org.netxms.ui.eclipse.osm.tools.Tile;
 import org.netxms.ui.eclipse.osm.tools.TileSet;
 import org.netxms.ui.eclipse.osm.widgets.helpers.GeoMapListener;
+import org.netxms.ui.eclipse.shared.ConsoleSharedData;
 
 /**
  * This widget shows map retrieved via OpenStreetMap Static Map API
  */
 public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCacheListener, MouseWheelListener, MouseListener, MouseMoveListener
 {
+   private static final int START = 1;
+   private static final int END = 2;
+   private static final String pointInformation[] = {"Start","End"};
+   
+   
        private static final Color MAP_BACKGROUND = new Color(Display.getCurrent(), 255, 255, 255);
        private static final Color INFO_BLOCK_BACKGROUND = new Color(Display.getCurrent(), 150, 240, 88);
        private static final Color INFO_BLOCK_BORDER = new Color(Display.getCurrent(), 0, 0, 0);
@@ -98,20 +109,35 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
        private Point currentPoint;
        private Point dragStartPoint = null;
        private Point selectionStartPoint = null;
-       private Point selectionEndPoint = null; 
+       private Point selectionEndPoint = null;
        private Set<GeoMapListener> mapListeners = new HashSet<GeoMapListener>(0);
        private String title = null;
        private int offsetX;
        private int offsetY;
        private TileSet currentTileSet = null;
-
+       //Historical variables
+       private boolean historycalData;
+   private List<GeoLocation> history = new ArrayList<GeoLocation>();
+   private QuadTree<GeoLocation> locationTree = new QuadTree<GeoLocation>();
+   private AbstractObject historyObject = null;
+   private Date till = new Date();
+   private int timeFrame = 60*60*1000;
+   private boolean strictTime = false;
+   private int highlightobjectID = -1;
+   private ToolTip toolTip;
+       
        /**
         * @param parent
         * @param style
         */
-       public GeoMapViewer(Composite parent, int style)
+       public GeoMapViewer(Composite parent, int style, final boolean historycalData, AbstractObject historyObject)
        {
                super(parent, style | SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
+               this.historycalData = historycalData;
+               if(historycalData)
+               {
+                  this.historyObject = historyObject;             
+               }
 
                labelProvider = WorkbenchLabelProvider.getDecoratingWorkbenchLabelProvider();
                mapLoader = new MapLoader(getDisplay());
@@ -163,6 +189,72 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                addMouseWheelListener(this);
 
                GeoLocationCache.getInstance().addListener(this);
+               addMouseTrackListener(new MouseTrackAdapter() {
+         @Override
+         public void mouseHover(MouseEvent e)
+         {
+            highlightobjectID = -1;
+            toolTip.setVisible(false);
+            if (!historycalData) //$NON-NLS-1$
+               return;           
+            
+            Point p = new Point(e.x, e.y);
+            p.x -= 5;
+            p.y -= 5;
+            GeoLocation loc1 = getLocationAtPoint(p);
+            p.x += 10;
+            p.y += 10;
+            GeoLocation loc2 = getLocationAtPoint(p);
+            Area area = new Area(loc1.getLatitude(), loc1.getLongitude(), loc2.getLatitude(), loc2.getLongitude());
+            List<GeoLocation> suitablePoints = locationTree.query(area);
+            if(suitablePoints.size() == 0)
+               return;
+            
+            int i = 0;
+            if(suitablePoints.size() > 1)
+            {
+               double minDistance = 100;
+               for(int j = 0; j < suitablePoints.size(); j++)
+               {
+                  double newDistance =  Math.pow( Math.pow(suitablePoints.get(j).getLatitude() - loc1.getLatitude(), 2) + Math.pow(suitablePoints.get(j).getLongitude() - loc1.getLongitude(), 2), 0.5);  
+                  if(minDistance > newDistance)
+                  {
+                     minDistance = newDistance;
+                     i = j;
+                  }
+               }
+            }
+            
+            highlightobjectID = history.indexOf(suitablePoints.get(i)); 
+            redraw();
+         }
+
+         /* (non-Javadoc)
+          * @see org.eclipse.swt.events.MouseTrackAdapter#mouseExit(org.eclipse.swt.events.MouseEvent)
+          */
+         @Override
+         public void mouseExit(MouseEvent e)
+         {
+            highlightobjectID = -1;
+            toolTip.setVisible(false);
+            redraw();
+         }
+      });
+               
+               addMouseMoveListener(new MouseMoveListener() {
+         @Override
+         public void mouseMove(MouseEvent e)
+         {
+            if (highlightobjectID != -1)
+            {
+               highlightobjectID = -1;
+               toolTip.setVisible(false);
+               redraw();
+            }
+         }
+      });
+               
+      toolTip = new ToolTip(getShell(), SWT.BALLOON);  
        }
        
        /**
@@ -267,9 +359,16 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                                                }
                                                
                                                Point mapSize = new Point(currentImage.getImageData().width, currentImage.getImageData().height);
-                                               coverage = GeoLocationCache.calculateCoverage(mapSize, accessor.getCenterPoint(), GeoLocationCache.CENTER, accessor.getZoom());
-                                               objects = GeoLocationCache.getInstance().getObjectsInArea(coverage);
-                                               GeoMapViewer.this.redraw();
+                  coverage = GeoLocationCache.calculateCoverage(mapSize, accessor.getCenterPoint(), GeoLocationCache.CENTER, accessor.getZoom());
+                                               if(!historycalData)
+                                               {
+                                               objects = GeoLocationCache.getInstance().getObjectsInArea(coverage);
+                                               GeoMapViewer.this.redraw();
+                                               }
+                                               else
+                                               {
+                                                  GeoMapViewer.this.updateHistory();
+                                               }
                                        }
                                });
                        }
@@ -390,13 +489,61 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                        gc.setTextAntialias(SWT.ON);
 
                        final Point centerXY = GeoLocationCache.coordinateToDisplay(accessor.getCenterPoint(), accessor.getZoom());
-                       for(AbstractObject object : objects)
-                       {
-                               final Point virtualXY = GeoLocationCache.coordinateToDisplay(object.getGeolocation(), accessor.getZoom());
-                               final int dx = virtualXY.x - centerXY.x;
-                               final int dy = virtualXY.y - centerXY.y;
-                               drawObject(gc, imgW / 2 + dx, imgH / 2 + dy, object);
-                       }
+         if(!historycalData)
+         {
+                       for(AbstractObject object : objects)
+                       {
+                               final Point virtualXY = GeoLocationCache.coordinateToDisplay(object.getGeolocation(), accessor.getZoom());
+                               final int dx = virtualXY.x - centerXY.x;
+                               final int dy = virtualXY.y - centerXY.y;
+                               drawObject(gc, imgW / 2 + dx, imgH / 2 + dy, object);
+                       }
+         }
+         else
+         {  
+            int nextX = 0;
+            int nextY = 0;
+            for(int i = 0; i < history.size(); i++)
+            {
+               final Point virtualXY = GeoLocationCache.coordinateToDisplay(history.get(i), accessor.getZoom());
+               final int dx = virtualXY.x - centerXY.x;
+               final int dy = virtualXY.y - centerXY.y;
+               
+               if (i != history.size() - 1)
+               { 
+                  final Point virtualXY2 = GeoLocationCache.coordinateToDisplay(history.get(i+1), accessor.getZoom());
+                  nextX = imgW / 2 + (virtualXY2.x - centerXY.x);
+                  nextY = imgH / 2 + (virtualXY2.y - centerXY.y);
+               }
+               
+               int color = SWT.COLOR_RED;
+               if(i ==  highlightobjectID)
+               {
+                  color = SWT.COLOR_GREEN;
+                  toolTip.setText("Start time: " + history.get(i).getTimestamp() + "\nEnd Time: " + history.get(i).getEndTimestamp() + "\nLocation: " + history.get(i));
+                  toolTip.setVisible(true);
+               }
+                  
+               if(i==0)
+               {
+                  if(i == history.size() - 1)
+                  {
+                     nextX = imgW / 2 + dx;
+                     nextY = imgH / 2 + dy;
+                  }
+                  drawObject(gc, imgW / 2 + dx, imgH / 2 + dy, GeoMapViewer.START, nextX, nextY, color);                  
+                  continue;
+               } 
+               
+               if (i == history.size() - 1)
+               {    
+                  drawObject(gc, imgW / 2 + dx, imgH / 2 + dy, GeoMapViewer.END, nextX, nextY, color);
+                  continue;
+               }
+               
+               drawObject(gc, imgW / 2 + dx, imgH / 2 + dy, 0, nextX, nextY, color);
+            }
+         }
        
                        final GeoLocation gl = new GeoLocation(accessor.getLatitude(), accessor.getLongitude());
                        final String text = gl.toString();
@@ -404,7 +551,7 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
        
                        Rectangle rect = getClientArea();
                        rect.x = 10;
-                       // rect.x = rect.width - textSize.x - 20;
+                       rect.x = rect.width - textSize.x - 20;
                        rect.y += 10;
                        rect.width = textSize.x + 10;
                        rect.height = textSize.y + 8;
@@ -460,7 +607,7 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
         * @param y
         * @param object
         */
-       private void drawObject(GC gc, int x, int y, AbstractObject object)
+       private void drawObject(GC gc, int x, int y, AbstractObject object) 
        {
                final String text = object.getObjectName();
                final Point textSize = gc.textExtent(text);
@@ -504,6 +651,65 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                gc.drawText(text, rect.x + LABEL_X_MARGIN + image.getImageData().width + LABEL_SPACING, rect.y + LABEL_Y_MARGIN);
        }
 
+         /**
+    * Draw object on map
+    * 
+    * @param gc
+    * @param x
+    * @param y
+    * @param object
+    */
+   private void drawObject(GC gc, int x, int y, int flag, int prevX, int prevY, int color) 
+   {    
+      if(flag == GeoMapViewer.START || flag == GeoMapViewer.END)
+      {
+         if(flag == GeoMapViewer.START)
+         {
+            gc.drawLine(x, y, prevX, prevY);
+         }
+         gc.setBackground(Display.getCurrent().getSystemColor(color)); 
+         gc.fillOval(x - 5, y -5, 10, 10);
+         
+         final String text = pointInformation[flag -1];
+         final Point textSize = gc.textExtent(text);
+         
+         Rectangle rect = new Rectangle(x - LABEL_ARROW_OFFSET, y - LABEL_ARROW_HEIGHT - textSize.y, textSize.x
+               + LABEL_X_MARGIN * 2 + LABEL_SPACING, textSize.y + LABEL_Y_MARGIN * 2);
+         
+         gc.setBackground(LABEL_BACKGROUND);
+
+         gc.setForeground(BORDER_COLOR);
+         gc.setLineWidth(4);
+         gc.fillRoundRectangle(rect.x, rect.y, rect.width, rect.height, 8, 8);
+         gc.drawRoundRectangle(rect.x, rect.y, rect.width, rect.height, 8, 8);
+         
+         gc.setLineWidth(2);
+         gc.fillRoundRectangle(rect.x, rect.y, rect.width, rect.height, 8, 8);
+         gc.drawRoundRectangle(rect.x, rect.y, rect.width, rect.height, 8, 8);
+         final int[] arrow = new int[] { rect.x + LABEL_ARROW_OFFSET - 4, rect.y + rect.height, x, y, rect.x + LABEL_ARROW_OFFSET + 4,
+               rect.y + rect.height };
+
+         gc.setLineWidth(4);
+         gc.setForeground(BORDER_COLOR);
+         gc.drawPolyline(arrow);
+
+         gc.fillPolygon(arrow);
+         gc.setForeground(LABEL_BACKGROUND);
+         gc.setLineWidth(2);
+         gc.drawLine(arrow[0], arrow[1], arrow[4], arrow[5]);
+         gc.drawPolyline(arrow);
+
+         gc.setForeground(LABEL_TEXT);
+         gc.drawText(text, rect.x + LABEL_X_MARGIN + LABEL_SPACING, rect.y + LABEL_Y_MARGIN);
+      }
+      else 
+      {
+         gc.drawLine(x, y, prevX, prevY);
+         gc.setBackground(Display.getCurrent().getSystemColor(color)); 
+         gc.fillOval(x - 5, y -5, 10, 10);
+      }      
+   }   
+       
        /*
         * (non-Javadoc)
         * 
@@ -538,8 +744,15 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                                || ((prevLocation != null) && (prevLocation.getType() != GeoLocation.UNSET) && coverage.contains(
                                                prevLocation.getLatitude(), prevLocation.getLongitude())))
                {
-                       objects = GeoLocationCache.getInstance().getObjectsInArea(coverage);
-                       redraw();
+                  if(!historycalData)
+                  {
+                       objects = GeoLocationCache.getInstance().getObjectsInArea(coverage);
+                       redraw();
+                  }
+                  else
+                  {
+                     updateHistory();
+                  }
                }
        }
        
@@ -746,4 +959,51 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
        {
                this.title = title;
        }
+       
+       /**
+        * Updates points for historical view
+        */
+       private void updateHistory()
+       {
+          ConsoleJob job = new ConsoleJob(Messages.get().GeoMapViewer_DownloadJob_Title, viewPart, Activator.PLUGIN_ID, null) {
+         @Override
+         protected void runInternal(IProgressMonitor monitor) throws Exception
+         {
+            NXCSession session = (NXCSession)ConsoleSharedData.getSession();
+            if(!strictTime)
+            {
+               till = new Date();
+            }
+            history = session.getLocationHistory(historyObject.getObjectId(), new Date(till.getTime() - timeFrame), till);
+            for(int i = 0; i < history.size(); i++)
+               locationTree.insert(history.get(i).getLatitude(), history.get(i).getLongitude(), history.get(i));
+            
+            runInUIThread(new Runnable() {
+               @Override
+               public void run()
+               {
+                  GeoMapViewer.this.redraw();
+               }
+            });
+         }
+
+         @Override
+         protected String getErrorMessage()
+         {
+            return Messages.get().GeoMapViewer_DownloadError;
+         }
+      };
+      job.setUser(false);
+      job.start();
+       }
+       
+       /**
+        * Sets new view period in minutes
+        */
+       public void changeTimePeriod(int timePeriod)
+       {
+          strictTime = false;
+          timeFrame = timePeriod * 60 * 1000;
+          updateHistory();
+       }
 }
index eb78abc..5c298dd 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Copyright (C) 2003-2013 Victor Kirhenshtein
 **
@@ -349,3 +349,22 @@ bool GeoLocation::parseLongitude(const TCHAR *lon)
                m_lon = 0.0;
        return isValid;
 }
+
+bool GeoLocation::sameLocation(const TCHAR* lon, const TCHAR* lat, int oldAccurasy)
+{
+   bool result =  false;
+   bool isValid;
+
+   int _lon = parse(lon, false, &isValid);
+       if (!isValid)
+               _lon = 0.0;
+
+   int _lat = parse(lat, true, &isValid);
+       if (!isValid)
+               _lat = 0.0;
+
+   if(sqrt(pow(_lon - m_lon, 2) + pow(_lat - m_lat, 2)) <= m_accuracy)
+      result = true;
+
+   return result;
+}
index c226660..14fc8e0 100644 (file)
@@ -334,10 +334,12 @@ TCHAR LIBNETXMS_EXPORTABLE *NXCPMessageCodeName(WORD wCode, TCHAR *pszBuffer)
       _T("CMD_FILEMGR_RENAME_FILE"),
       _T("CMD_FILEMGR_MOVE_FILE"),
       _T("CMD_FILEMGR_UPLOAD"),
-      _T("CMD_GET_SWITCH_FDB")
+      _T("CMD_GET_SWITCH_FDB"),
+      _T("CMD_COMMAND_OUTPUT"),
+      _T("CMD_GET_LOC_HISTORY")
    };
 
-   if ((wCode >= CMD_LOGIN) && (wCode <= CMD_GET_SWITCH_FDB))
+   if ((wCode >= CMD_LOGIN) && (wCode <= CMD_GET_LOC_HISTORY))
       _tcscpy(pszBuffer, pszMsgNames[wCode - CMD_LOGIN]);
    else
       _sntprintf(pszBuffer, 64, _T("CMD_0x%04X"), wCode);
index 060f193..8d74ff3 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Copyright (C) 2003-2013 Victor Kirhenshtein
 **
@@ -251,7 +251,10 @@ void MobileDevice::updateStatus(CSCPMessage *msg)
                m_batteryLevel = -1;
 
        if (msg->isFieldExist(VID_GEOLOCATION_TYPE))
+       {
                m_geoLocation = GeoLocation(*msg);
+               addLocationToHistory();
+   }
 
        if (msg->isFieldExist(VID_IP_ADDRESS))
                m_dwIpAddr = msg->GetVariableLong(VID_IP_ADDRESS);
index 0495ce4..75210f9 100644 (file)
@@ -1004,6 +1004,7 @@ UINT32 NetObj::ModifyFromMessage(CSCPMessage *pRequest, BOOL bAlreadyLocked)
        if (pRequest->isFieldExist(VID_GEOLOCATION_TYPE))
        {
                m_geoLocation = GeoLocation(*pRequest);
+               addLocationToHistory();
        }
 
        if (pRequest->isFieldExist(VID_SUBMAP_ID))
@@ -1610,8 +1611,8 @@ bool NetObj::isEventSource()
 /**
  * Get module data
  */
-ModuleData *NetObj::getModuleData(const TCHAR *module) 
-{ 
+ModuleData *NetObj::getModuleData(const TCHAR *module)
+{
    LockData();
    ModuleData *data = (m_moduleData != NULL) ? m_moduleData->get(module) : NULL;
    UnlockData();
@@ -1629,3 +1630,125 @@ void NetObj::setModuleData(const TCHAR *module, ModuleData *data)
    m_moduleData->set(module, data);
    UnlockData();
 }
+
+/**
+ * Add new location entry
+ */
+void NetObj::addLocationToHistory()
+{
+   TCHAR lat[32], lon[32];
+   DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
+   UINT32 startTimestamp;
+   bool isSamePlace;
+   DB_RESULT hResult;
+   if(!locationTableExists())
+   {
+      DbgPrintf(4, _T("NetObj::addLocationToHistory: Geolocation history table will be created for %d node"), m_dwId);
+      if(!cterateLocationGystoryTable(hdb))
+      {
+         DbgPrintf(4, _T("NetObj::addLocationToHistory: Error while creation geolocation history table for %d node"), m_dwId);
+         return;
+      }
+   }
+       const TCHAR *query;
+       switch(g_dbSyntax)
+       {
+               case DB_SYNTAX_ORACLE:
+                       query = _T("SELECT * FROM (latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC) WHERE ROWNUM<=1");
+                       break;
+               case DB_SYNTAX_MSSQL:
+                       query = _T("SELECT TOP 1 latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC");
+                       break;
+               case DB_SYNTAX_DB2:
+                       query = _T("SELECT latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC FETCH FIRST 200 ROWS ONLY");
+                       break;
+               default:
+                       query = _T("SELECT latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC LIMIT 1");
+                       break;
+       }
+   TCHAR preparedQuery[256];
+       _sntprintf(preparedQuery, 256, query, m_dwId);
+       DB_STATEMENT hStmt = DBPrepare(hdb, preparedQuery);
+
+   if (hStmt == NULL)
+               goto onFail;
+
+   hResult = DBSelectPrepared(hStmt);
+   if(hResult == NULL)
+               goto onFail;
+   if(DBGetNumRows(hResult) > 0)
+   {
+      startTimestamp = DBGetFieldULong(hResult, 0, 3);
+      isSamePlace = m_geoLocation.sameLocation(DBGetField(hResult, 0, 0, lat, 32), DBGetField(hResult, 0, 1, lon, 32), DBGetFieldULong(hResult, 0, 2));
+      DBFreeStatement(hStmt);
+   }
+   else
+   {
+      isSamePlace = false;
+   }
+
+   if(isSamePlace)
+   {
+      TCHAR query[256];
+      _sntprintf(query, 255, _T("UPDATE gps_history_%d SET end_timestamp = ? WHERE start_timestamp =? "), m_dwId);
+      hStmt = DBPrepare(hdb, query);
+      DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
+      DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, startTimestamp);
+   }
+   else
+   {
+      TCHAR query[256];
+      _sntprintf(query, 255, _T("INSERT INTO gps_history_%d (latitude,longitude,")
+                       _T("accuracy,start_timestamp,end_timestamp) VALUES (?,?,?,?,?)"), m_dwId);
+      hStmt = DBPrepare(hdb, query);
+
+      _sntprintf(lat, 32, _T("%f"), m_geoLocation.getLatitude());
+      _sntprintf(lon, 32, _T("%f"), m_geoLocation.getLongitude());
+
+      DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, lat, DB_BIND_STATIC);
+      DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, lon, DB_BIND_STATIC);
+      DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (LONG)m_geoLocation.getAccuracy());
+      DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
+      DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
+       }
+
+       if (hStmt == NULL)
+               goto onFail;
+
+   DBExecute(hStmt);
+   DBFreeStatement(hStmt);
+   DBConnectionPoolReleaseConnection(hdb);
+   return;
+
+onFail:
+   DBFreeStatement(hStmt);
+   DbgPrintf(4, _T("NetObj::addLocationToHistory: Failed to add location to history"));
+   DBConnectionPoolReleaseConnection(hdb);
+   return;
+}
+
+/**
+ * Check if given data table exist
+ */
+bool NetObj::locationTableExists()
+{
+   TCHAR table[256];
+   _sntprintf(table, 256, _T("gps_history_%d"), m_dwId);
+   int rc = DBIsTableExist(g_hCoreDB, table);
+   if (rc == DBIsTableExist_Failure)
+   {
+      _tprintf(_T("WARNING: call to DBIsTableExist(\"%s\") failed\n"), table);
+   }
+   return rc != DBIsTableExist_NotFound;
+}
+
+bool NetObj::cterateLocationGystoryTable(DB_HANDLE hdb)
+{
+   TCHAR szQuery[256], szQueryTemplate[256];
+   MetaDataReadStr(_T("LocationHistory"), szQueryTemplate, 255, _T(""));
+   _sntprintf(szQuery, 256, szQueryTemplate, m_dwId);
+   if (!DBQuery(hdb, szQuery))
+               return false;
+
+   return true;
+}
index 64de1e6..638b638 100644 (file)
@@ -187,6 +187,7 @@ DEFINE_THREAD_STARTER(fileManagerControl)
 DEFINE_THREAD_STARTER(uploadUserFileToAgent)
 DEFINE_THREAD_STARTER(getSwitchForwardingDatabase)
 DEFINE_THREAD_STARTER(getRoutingTable)
+DEFINE_THREAD_STARTER(getLocationHistory)
 
 /**
  * Client communication read thread starter
@@ -1463,6 +1464,9 @@ void ClientSession::processingThread()
          case CMD_GET_ROUTING_TABLE:
             CALL_IN_NEW_THREAD(getRoutingTable, pMsg);
             break;
+         case CMD_GET_LOC_HISTORY:
+            CALL_IN_NEW_THREAD(getLocationHistory, pMsg);
+            break;
          default:
             if ((m_wCurrentCmd >> 8) == 0x11)
             {
@@ -4050,8 +4054,8 @@ void ClientSession::getLastValues(CSCPMessage *pRequest)
          if ((object->Type() == OBJECT_NODE) || (object->Type() == OBJECT_MOBILEDEVICE) ||
              (object->Type() == OBJECT_TEMPLATE) || (object->Type() == OBJECT_CLUSTER))
          {
-            msg.SetVariable(VID_RCC, 
-               ((Template *)object)->getLastValues(&msg, 
+            msg.SetVariable(VID_RCC,
+               ((Template *)object)->getLastValues(&msg,
                   pRequest->getFieldAsBoolean(VID_OBJECT_TOOLTIP_ONLY),
                   pRequest->getFieldAsBoolean(VID_INCLUDE_NOVALUE_OBJECTS)));
          }
@@ -13373,3 +13377,73 @@ void ClientSession::getRoutingTable(CSCPMessage *request)
    // Send response
    sendMessage(&msg);
 }
+
+/**
+ * Get location history for object
+ */
+void ClientSession::getLocationHistory(CSCPMessage *request)
+{
+   CSCPMessage msg;
+
+   // Prepare response message
+   msg.SetCode(CMD_REQUEST_COMPLETED);
+   msg.SetId(request->GetId());
+
+   // Get node id and check object class and access rights
+   Node *node = (Node *)FindObjectById(request->GetVariableLong(VID_OBJECT_ID), OBJECT_MOBILEDEVICE);
+   if (node != NULL)
+   {
+      if (node->checkAccessRights(m_dwUserId, OBJECT_ACCESS_READ))
+      {
+         DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
+         TCHAR query[256];
+         _sntprintf(query, 255, _T("SELECT latitude,longitude,accuracy,start_timestamp,end_timestamp FROM gps_history_%d")
+                                             _T(" WHERE start_timestamp<? AND end_timestamp>?"), request->GetVariableLong(VID_OBJECT_ID));
+
+         DB_STATEMENT hStmt = DBPrepare(hdb, query);
+         if (hStmt != NULL)
+         {
+            DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, request->GetVariableLong(VID_TIME_TO));
+            DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, request->GetVariableLong(VID_TIME_FROM));
+            DB_RESULT hResult = DBSelectPrepared(hStmt);
+            if (hResult != NULL)
+            {
+               int base = VID_LOC_LIST_BASE;
+               TCHAR buffer[32];
+               msg.SetVariable(VID_NUM_RECORDS, (UINT32)DBGetNumRows(hResult));
+               for(int i = 0; i < DBGetNumRows(hResult); i++, base+=10)
+               {
+                  msg.SetVariable(base, DBGetField(hResult, i, 0, buffer, 32));
+                  msg.SetVariable(base+1, DBGetField(hResult, i, 1, buffer, 32));
+                  msg.SetVariable(base+2, DBGetFieldULong(hResult, i, 2));
+                  msg.SetVariable(base+3, DBGetFieldULong(hResult, i, 3));
+                  msg.SetVariable(base+4, DBGetFieldULong(hResult, i, 4));
+               }
+               DBFreeResult(hResult);
+            }
+            else
+            {
+               msg.SetVariable(VID_RCC, RCC_DB_FAILURE);
+            }
+            DBFreeStatement(hStmt);
+         }
+         else
+         {
+            msg.SetVariable(VID_RCC, RCC_DB_FAILURE);
+         }
+         DBConnectionPoolReleaseConnection(hdb);
+      }
+      else
+      {
+         msg.SetVariable(VID_RCC, RCC_ACCESS_DENIED);
+         WriteAuditLog(AUDIT_OBJECTS, FALSE, m_dwUserId, m_workstation, m_id, node->Id(), _T("Access denied on reading routing table"));
+      }
+   }
+   else  // No object with given ID
+   {
+      msg.SetVariable(VID_RCC, RCC_INVALID_OBJECT_ID);
+   }
+
+   // Send response
+   sendMessage(&msg);
+}
index 4d86c79..82ac027 100644 (file)
@@ -473,6 +473,7 @@ private:
    DECLARE_THREAD_STARTER(uploadUserFileToAgent)
    DECLARE_THREAD_STARTER(getSwitchForwardingDatabase)
    DECLARE_THREAD_STARTER(getRoutingTable)
+   DECLARE_THREAD_STARTER(getLocationHistory)
 
    void readThread();
    void writeThread();
@@ -682,6 +683,7 @@ private:
    void uploadUserFileToAgent(CSCPMessage *request);
    void getSwitchForwardingDatabase(CSCPMessage *request);
    void getRoutingTable(CSCPMessage *request);
+   void getLocationHistory(CSCPMessage *request);
 
 public:
    ClientSession(SOCKET hSocket, struct sockaddr *addr);
index bc32f86..914fd9e 100644 (file)
@@ -384,6 +384,9 @@ protected:
 
    virtual void prepareForDeletion();
    virtual void onObjectDelete(UINT32 dwObjectId);
+   void addLocationToHistory();
+   bool locationTableExists();
+   bool cterateLocationGystoryTable(DB_HANDLE hdb);
 
 public:
    NetObj();
index 52ddffb..e4cc396 100644 (file)
@@ -388,6 +388,20 @@ static BOOL RecreateTData(const TCHAR *className, bool multipleTables, bool inde
 }
 
 /**
+ * Upgrade from V332 to V333
+ */
+static BOOL H_UpgradeFromV332(int currVersion, int newVersion)
+{
+    static TCHAR batch[] =
+      _T("INSERT INTO metadata (var_name,var_value)")
+      _T("   VALUES ('LocationHistory','CREATE TABLE gps_history_%d (latitude varchar(20), longitude varchar(20), accuracy integer not null, start_timestamp integer not null, end_timestamp integer not null, PRIMARY KEY(start_timestamp))')\n")
+      _T("<END>");
+   CHK_EXEC(SQLBatch(batch));
+   CHK_EXEC(SQLQuery(_T("UPDATE metadata SET var_value='333' WHERE var_name='SchemaVersion'")));
+   return TRUE;
+}
+
+/**
  * Upgrade from V331 to V332
  */
 static BOOL H_UpgradeFromV331(int currVersion, int newVersion)
@@ -8073,6 +8087,7 @@ static struct
    { 329, 330, H_UpgradeFromV329 },
    { 330, 331, H_UpgradeFromV330 },
    { 331, 332, H_UpgradeFromV331 },
+   { 332, 333, H_UpgradeFromV332 },
    { 0, 0, NULL }
 };
 
index 0cc04d5..eac9441 100644 (file)
@@ -57,7 +57,7 @@ public class GeoMapElement extends ElementWidget
                layout.marginWidth = 0;
                setLayout(layout);
                
-               mapWidget = new GeoMapViewer(this, SWT.NONE);
+               mapWidget = new GeoMapViewer(this, SWT.NONE, false, null);
                mapWidget.setViewPart(viewPart);
                mapWidget.setTitle(config.getTitle());
                mapWidget.showMap(config.getLatitude(), config.getLongitude(), config.getZoom());
index 80d76a7..201f627 100644 (file)
             name="%view.name.WorldMap"
             restorable="true">
       </view>
+      <view
+            allowMultiple="true"
+            class="org.netxms.ui.eclipse.osm.views.HistoryView"
+            icon="icons/map.png"
+            id="org.netxms.ui.eclipse.osm.views.HistoryView"
+            name="Geolocation History">
+      </view>
    </extension>
 
    <extension
                label="%action.label.Geolocation"
                menubarPath="additions">
          </action>
+         <action
+               class="org.netxms.ui.eclipse.osm.actions.OpenHistoryMap"
+               enablesFor="1"
+               icon="icons/map.png"
+               id="org.netxms.ui.eclipse.osm.actions.OpenHistoryMap#MobileDevice"
+               label="Geolocation History"
+               menubarPath="additions">
+         </action>
       </objectContribution>
    </extension>
    <extension
diff --git a/webui/webapp/OSM/src/org/netxms/ui/eclipse/osm/actions/OpenHistoryMap.java b/webui/webapp/OSM/src/org/netxms/ui/eclipse/osm/actions/OpenHistoryMap.java
new file mode 100644 (file)
index 0000000..ba8a5eb
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2003-2011 Victor Kirhenshtein
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.ui.eclipse.osm.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.netxms.base.GeoLocation;
+import org.netxms.client.objects.AbstractObject;
+import org.netxms.ui.eclipse.osm.views.HistoryView;
+import org.netxms.ui.eclipse.tools.MessageDialogHelper;
+
+/**
+ * Object action: open geolocation view
+ */
+public class OpenHistoryMap implements IObjectActionDelegate
+{
+       private IWorkbenchWindow window;
+       private AbstractObject object;
+       
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction, org.eclipse.ui.IWorkbenchPart)
+        */
+       @Override
+       public void setActivePart(IAction action, IWorkbenchPart targetPart)
+       {
+               window = targetPart.getSite().getWorkbenchWindow();
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
+        */
+       @Override
+       public void run(IAction action)
+       {
+               if(window != null)
+               {       
+                  //Open dialog
+                       try 
+                       {
+                               window.getActivePage().showView(HistoryView.ID, Long.toString(object.getObjectId()), IWorkbenchPage.VIEW_ACTIVATE);
+                       } 
+                       catch (PartInitException e) 
+                       {
+                               MessageDialogHelper.openError(window.getShell(), "Error while opening historical map", "Error while opening historical map: " + e.getMessage());
+                       }
+               }
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection)
+        */
+       @Override
+       public void selectionChanged(IAction action, ISelection selection)
+       {
+               Object obj;
+               if ((selection instanceof IStructuredSelection) &&
+                        ((obj = ((IStructuredSelection)selection).getFirstElement()) instanceof AbstractObject))
+               {
+                       object = (AbstractObject)obj;
+               }
+               else
+               {
+                       object = null;
+               }
+               action.setEnabled((object != null) && (object.getGeolocation().getType() != GeoLocation.UNSET));
+       }
+}
index 8779972..d9c6451 100644 (file)
@@ -93,7 +93,7 @@ public abstract class AbstractGeolocationView extends ViewPart
        public void createPartControl(Composite parent)
        {
                // Map control
-               map = new GeoMapViewer(parent, SWT.BORDER);
+               map = new GeoMapViewer(parent, SWT.BORDER, false, null);
                map.setViewPart(this);
                
                createActions();
@@ -24,6 +24,7 @@ import org.eclipse.jface.action.IMenuListener;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Menu;
@@ -33,6 +34,7 @@ import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.part.ViewPart;
 import org.netxms.base.GeoLocation;
 import org.netxms.client.NXCSession;
+import org.netxms.client.objects.AbstractObject;
 import org.netxms.ui.eclipse.console.resources.SharedIcons;
 import org.netxms.ui.eclipse.osm.Messages;
 import org.netxms.ui.eclipse.osm.tools.MapAccessor;
@@ -43,9 +45,15 @@ import org.netxms.ui.eclipse.shared.ConsoleSharedData;
 /**
  * Base class for all geographical views
  */
-public abstract class AbstractGeolocationView extends ViewPart
+public class HistoryView extends ViewPart
 {
+   public static final String ID = "org.netxms.ui.eclipse.osm.views.HistoryView"; //$NON-NLS-1$
        public static final String JOB_FAMILY = "MapViewJob"; //$NON-NLS-1$
+       private static final int[] presetRanges = { 10, 30, 60, 120, 240, 720, 1440, 2880, 7200, 10080, 44640, 525600 };
+   private static final String[] presetNames = 
+      { "10 minutes", "30 minutes", "1 hour", "2 hours", "4 hours", "12 hours", "Today",
+        "Last 2 days", "Last 5 days", "This week", "This month","This Year" };
+
        
        protected GeoMapViewer map;
        
@@ -53,20 +61,28 @@ public abstract class AbstractGeolocationView extends ViewPart
        private int zoomLevel = 15;
        private Action actionZoomIn;
        private Action actionZoomOut;
+   private Action[] presetActions;
+       private AbstractObject object;
        
        /**
         * Get initial center point for displayed map
         * 
         * @return
         */
-       protected abstract GeoLocation getInitialCenterPoint();
+       protected GeoLocation getInitialCenterPoint() 
+       {
+          return object.getGeolocation();
+       }
        
        /**
         * Get initial zoom level
         * 
         * @return
         */
-       protected abstract int getInitialZoomLevel();
+       protected int getInitialZoomLevel()
+       {
+      return 15;
+       }
 
        /* (non-Javadoc)
         * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
@@ -84,6 +100,19 @@ public abstract class AbstractGeolocationView extends ViewPart
                catch(Exception e)
                {
                }
+               
+               try
+      {
+         long id = Long.parseLong(site.getSecondaryId());
+         object = ((NXCSession)ConsoleSharedData.getSession()).findObjectById(id);
+         setPartName(Messages.get().LocationMap_PartNamePrefix + object.getObjectName());
+      }
+      catch(Exception e)
+      {
+         throw new PartInitException(Messages.get().LocationMap_InitError1, e);
+      }
+      if (object == null)
+         throw new PartInitException(Messages.get().LocationMap_InitError2);
        }
        
        /* (non-Javadoc)
@@ -93,7 +122,7 @@ public abstract class AbstractGeolocationView extends ViewPart
        public void createPartControl(Composite parent)
        {
                // Map control
-               map = new GeoMapViewer(parent, SWT.BORDER);
+               map = new GeoMapViewer(parent, SWT.BORDER, true, object);
                map.setViewPart(this);
                
                createActions();
@@ -110,7 +139,7 @@ public abstract class AbstractGeolocationView extends ViewPart
                        @Override
                        public void onZoom(int zoomLevel)
                        {
-                               AbstractGeolocationView.this.zoomLevel = zoomLevel;
+                               HistoryView.this.zoomLevel = zoomLevel;
                                mapAccessor.setZoom(zoomLevel);
                                actionZoomIn.setEnabled(zoomLevel < 18);
                                actionZoomOut.setEnabled(zoomLevel > 0);
@@ -147,6 +176,19 @@ public abstract class AbstractGeolocationView extends ViewPart
                        }
                };
                actionZoomOut.setImageDescriptor(SharedIcons.ZOOM_OUT);
+               
+               presetActions = new Action[presetRanges.length];
+      for(int i = 0; i < presetRanges.length; i++)
+      {
+         final Integer presetIndex = i;
+         presetActions[i] = new Action(presetNames[i]) {
+            @Override
+            public void run()
+            {
+               map.changeTimePeriod(presetRanges[presetIndex]);
+            }
+         };
+      }
        }
 
        /**
@@ -166,6 +208,12 @@ public abstract class AbstractGeolocationView extends ViewPart
         */
        protected void fillLocalPullDown(IMenuManager manager)
        {
+          MenuManager presets = new MenuManager("&Presets");
+      for(int i = 0; i < presetActions.length; i++)
+         presets.add(presetActions[i]);
+      
+      manager.add(presets);
+      manager.add(new Separator());       
                manager.add(actionZoomIn);
                manager.add(actionZoomOut);
        }
@@ -209,6 +257,12 @@ public abstract class AbstractGeolocationView extends ViewPart
         */
        protected void fillContextMenu(final IMenuManager manager)
        {
+          MenuManager presets = new MenuManager("&Presets");
+      for(int i = 0; i < presetActions.length; i++)
+         presets.add(presetActions[i]);
+      
+      manager.add(presets);
+      manager.add(new Separator());
                manager.add(actionZoomIn);
                manager.add(actionZoomOut);
        }
index 7783fc5..29f7588 100644 (file)
 package org.netxms.ui.eclipse.osm.widgets;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
@@ -37,6 +39,7 @@ import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.MouseListener;
 import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackAdapter;
 import org.eclipse.swt.events.PaintEvent;
 import org.eclipse.swt.events.PaintListener;
 import org.eclipse.swt.graphics.Color;
@@ -50,11 +53,13 @@ import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ToolTip;
 import org.eclipse.swt.widgets.Widget;
 import org.eclipse.ui.IViewPart;
 import org.eclipse.ui.model.WorkbenchLabelProvider;
 import org.eclipse.ui.presentations.PresentationUtil;
 import org.netxms.base.GeoLocation;
+import org.netxms.client.NXCSession;
 import org.netxms.client.objects.AbstractObject;
 import org.netxms.ui.eclipse.console.resources.SharedColors;
 import org.netxms.ui.eclipse.console.resources.SharedIcons;
@@ -67,15 +72,22 @@ import org.netxms.ui.eclipse.osm.Messages;
 import org.netxms.ui.eclipse.osm.tools.Area;
 import org.netxms.ui.eclipse.osm.tools.MapAccessor;
 import org.netxms.ui.eclipse.osm.tools.MapLoader;
+import org.netxms.ui.eclipse.osm.tools.QuadTree;
 import org.netxms.ui.eclipse.osm.tools.Tile;
 import org.netxms.ui.eclipse.osm.tools.TileSet;
 import org.netxms.ui.eclipse.osm.widgets.helpers.GeoMapListener;
+import org.netxms.ui.eclipse.shared.ConsoleSharedData;
 
 /**
  * This widget shows map retrieved via OpenStreetMap Static Map API
  */
 public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCacheListener, MouseListener, MouseMoveListener
 {
+   private static final int START = 1;
+   private static final int END = 2;
+   private static final String pointInformation[] = {"Start","End"};
+   
+   
        private static final Color MAP_BACKGROUND = new Color(Display.getCurrent(), 255, 255, 255);
        private static final Color INFO_BLOCK_BACKGROUND = new Color(Display.getCurrent(), 150, 240, 88);
        private static final Color INFO_BLOCK_BORDER = new Color(Display.getCurrent(), 0, 0, 0);
@@ -96,6 +108,8 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
        private static final int DRAG_JITTER = 8;
 
        private ILabelProvider labelProvider;
+       private Image currentImage = null;
+       private Image bufferImage = null;
        private Area coverage = null;
        private List<AbstractObject> objects = new ArrayList<AbstractObject>();
        private MapAccessor accessor;
@@ -104,21 +118,36 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
        private Point currentPoint;
        private Point dragStartPoint = null;
        private Point selectionStartPoint = null;
-       private Point selectionEndPoint = null; 
+       private Point selectionEndPoint = null;
        private Set<GeoMapListener> mapListeners = new HashSet<GeoMapListener>(0);
        private String title = null;
        private int offsetX;
        private int offsetY;
        private TileSet currentTileSet = null;
        private RAPDragTracker tracker;
+       //Historical variables
+       private boolean historycalData;
+   private List<GeoLocation> history = new ArrayList<GeoLocation>();
+   private QuadTree<GeoLocation> locationTree = new QuadTree<GeoLocation>();
+   private AbstractObject historyObject = null;
+   private Date till = new Date();
+   private int timeFrame = 60*60*1000;
+   private boolean strictTime = false;
+   private int highlightobjectID = -1;
+   private ToolTip toolTip;
 
        /**
         * @param parent
         * @param style
         */
-       public GeoMapViewer(Composite parent, int style)
+       public GeoMapViewer(Composite parent, int style, final boolean historycalData, AbstractObject historyObject)
        {
                super(parent, style | SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
+               this.historycalData = historycalData;
+               if(historycalData)
+               {
+                  this.historyObject = historyObject;             
+               }
 
                labelProvider = WorkbenchLabelProvider.getDecoratingWorkbenchLabelProvider();
                mapLoader = new MapLoader(getDisplay());
@@ -208,6 +237,69 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                /* end of mouse drag hack */
 
                GeoLocationCache.getInstance().addListener(this);
+               /*addMouseTrackListener(new MouseTrackAdapter() {
+         @Override
+         public void mouseHover(MouseEvent e)
+         {
+            highlightobjectID = -1;
+            toolTip.setVisible(false);
+            if (!historycalData) //$NON-NLS-1$
+               return;           
+            
+            Point p = new Point(e.x, e.y);
+            p.x -= 5;
+            p.y -= 5;
+            GeoLocation loc1 = getLocationAtPoint(p);
+            p.x += 10;
+            p.y += 10;
+            GeoLocation loc2 = getLocationAtPoint(p);
+            Area area = new Area(loc1.getLatitude(), loc1.getLongitude(), loc2.getLatitude(), loc2.getLongitude());
+            List<GeoLocation> suitablePoints = locationTree.query(area);
+            if(suitablePoints.size() == 0)
+               return;
+            
+            int i = 0;
+            if(suitablePoints.size() > 1)
+            {
+               double minDistance = 100;
+               for(int j = 0; j < suitablePoints.size(); j++)
+               {
+                  double newDistance =  Math.pow( Math.pow(suitablePoints.get(j).getLatitude() - loc1.getLatitude(), 2) + Math.pow(suitablePoints.get(j).getLongitude() - loc1.getLongitude(), 2), 0.5);  
+                  if(minDistance > newDistance)
+                  {
+                     minDistance = newDistance;
+                     i = j;
+                  }
+               }
+            }
+            
+            highlightobjectID = history.indexOf(suitablePoints.get(i)); 
+            redraw();
+         } 
+               
+         @Override
+         public void mouseExit(MouseEvent e)
+         {
+            highlightobjectID = -1;
+            toolTip.setVisible(false);
+            redraw();
+         }
+      });
+               
+               addMouseMoveListener(new MouseMoveListener() {
+         @Override
+         public void mouseMove(MouseEvent e)
+         {
+            if (highlightobjectID != -1)
+            {
+               highlightobjectID = -1;
+               toolTip.setVisible(false);
+               redraw();
+            }
+         }
+      }); */
+               
+      toolTip = new ToolTip(getShell(), SWT.BALLOON);  
        }
        
        /**
@@ -304,8 +396,15 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                                                Rectangle clientArea = getClientArea();
                                                Point mapSize = new Point(clientArea.width, clientArea.height);
                                                coverage = GeoLocationCache.calculateCoverage(mapSize, accessor.getCenterPoint(), GeoLocationCache.CENTER, accessor.getZoom());
-                                               objects = GeoLocationCache.getInstance().getObjectsInArea(coverage);
-                                               GeoMapViewer.this.redraw();
+                                               if(!historycalData)
+                                               {
+                                               objects = GeoLocationCache.getInstance().getObjectsInArea(coverage);
+                                               GeoMapViewer.this.redraw();
+                                               }
+                                               else
+                                               {
+                                                  GeoMapViewer.this.updateHistory();
+                                               }
                                        }
                                });
                        }
@@ -408,13 +507,61 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                        Rectangle rect = getClientArea();
                        
                        final Point centerXY = GeoLocationCache.coordinateToDisplay(accessor.getCenterPoint(), accessor.getZoom());
-                       for(AbstractObject object : objects)
-                       {
-                               final Point virtualXY = GeoLocationCache.coordinateToDisplay(object.getGeolocation(), accessor.getZoom());
-                               final int dx = virtualXY.x - centerXY.x;
-                               final int dy = virtualXY.y - centerXY.y;
-                               drawObject(gc, rect.width / 2 + dx, rect.height / 2 + dy, object);
-                       }
+         if(!historycalData)
+         {
+                       for(AbstractObject object : objects)
+                       {
+                               final Point virtualXY = GeoLocationCache.coordinateToDisplay(object.getGeolocation(), accessor.getZoom());
+                               final int dx = virtualXY.x - centerXY.x;
+                               final int dy = virtualXY.y - centerXY.y;
+                               drawObject(gc, rect.width / 2 + dx, rect.height / 2 + dy, object);
+                       }
+         }
+         else
+         {  
+            int nextX = 0;
+            int nextY = 0;
+            for(int i = 0; i < history.size(); i++)
+            {
+               final Point virtualXY = GeoLocationCache.coordinateToDisplay(history.get(i), accessor.getZoom());
+               final int dx = virtualXY.x - centerXY.x;
+               final int dy = virtualXY.y - centerXY.y;
+               
+               if (i != history.size() - 1)
+               { 
+                  final Point virtualXY2 = GeoLocationCache.coordinateToDisplay(history.get(i+1), accessor.getZoom());
+                  nextX = rect.width / 2 + (virtualXY2.x - centerXY.x);
+                  nextY = rect.height / 2 + (virtualXY2.y - centerXY.y);
+               }
+               
+               int color = SWT.COLOR_RED;
+               if(i ==  highlightobjectID)
+               {
+                  color = SWT.COLOR_GREEN;
+                  toolTip.setText("Start time: " + history.get(i).getTimestamp() + "\nEnd Time: " + history.get(i).getEndTimestamp() + "\nLocation: " + history.get(i));
+                  toolTip.setVisible(true);
+               }
+                  
+               if(i==0)
+               {
+                  if(i == history.size() - 1)
+                  {
+                     nextX = rect.width / 2 + dx;
+                     nextY = rect.height / 2 + dy;
+                  }
+                  drawObject(gc, rect.width / 2 + dx, rect.height / 2 + dy, GeoMapViewer.START, nextX, nextY, color);                  
+                  continue;
+               } 
+               
+               if (i == history.size() - 1)
+               {    
+                  drawObject(gc, rect.width / 2 + dx, rect.height / 2 + dy, GeoMapViewer.END, nextX, nextY, color);
+                  continue;
+               }
+               
+               drawObject(gc, rect.width / 2 + dx, rect.height / 2 + dy, 0, nextX, nextY, color);
+            }
+         }
        
                        final GeoLocation gl = new GeoLocation(accessor.getLatitude(), accessor.getLongitude());
                        final String text = gl.toString();
@@ -474,7 +621,7 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
         * @param y
         * @param object
         */
-       private void drawObject(GC gc, int x, int y, AbstractObject object)
+       private void drawObject(GC gc, int x, int y, AbstractObject object) 
        {
                final String text = object.getObjectName();
                final Point textSize = gc.textExtent(text);
@@ -518,6 +665,65 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                gc.drawText(text, rect.x + LABEL_X_MARGIN + image.getImageData().width + LABEL_SPACING, rect.y + LABEL_Y_MARGIN);
        }
 
+         /**
+    * Draw object on map
+    * 
+    * @param gc
+    * @param x
+    * @param y
+    * @param object
+    */
+   private void drawObject(GC gc, int x, int y, int flag, int prevX, int prevY, int color) 
+   {    
+      if(flag == GeoMapViewer.START || flag == GeoMapViewer.END)
+      {
+         if(flag == GeoMapViewer.START)
+         {
+            gc.drawLine(x, y, prevX, prevY);
+         }
+         gc.setBackground(Display.getCurrent().getSystemColor(color)); 
+         gc.fillOval(x - 5, y -5, 10, 10);
+         
+         final String text = pointInformation[flag -1];
+         final Point textSize = gc.textExtent(text);
+         
+         Rectangle rect = new Rectangle(x - LABEL_ARROW_OFFSET, y - LABEL_ARROW_HEIGHT - textSize.y, textSize.x
+               + LABEL_X_MARGIN * 2 + LABEL_SPACING, textSize.y + LABEL_Y_MARGIN * 2);
+         
+         gc.setBackground(LABEL_BACKGROUND);
+
+         gc.setForeground(BORDER_COLOR);
+         gc.setLineWidth(4);
+         gc.fillRoundRectangle(rect.x, rect.y, rect.width, rect.height, 8, 8);
+         gc.drawRoundRectangle(rect.x, rect.y, rect.width, rect.height, 8, 8);
+         
+         gc.setLineWidth(2);
+         gc.fillRoundRectangle(rect.x, rect.y, rect.width, rect.height, 8, 8);
+         gc.drawRoundRectangle(rect.x, rect.y, rect.width, rect.height, 8, 8);
+         final int[] arrow = new int[] { rect.x + LABEL_ARROW_OFFSET - 4, rect.y + rect.height, x, y, rect.x + LABEL_ARROW_OFFSET + 4,
+               rect.y + rect.height };
+
+         gc.setLineWidth(4);
+         gc.setForeground(BORDER_COLOR);
+         gc.drawPolyline(arrow);
+
+         gc.fillPolygon(arrow);
+         gc.setForeground(LABEL_BACKGROUND);
+         gc.setLineWidth(2);
+         gc.drawLine(arrow[0], arrow[1], arrow[4], arrow[5]);
+         gc.drawPolyline(arrow);
+
+         gc.setForeground(LABEL_TEXT);
+         gc.drawText(text, rect.x + LABEL_X_MARGIN + LABEL_SPACING, rect.y + LABEL_Y_MARGIN);
+      }
+      else 
+      {
+         gc.drawLine(x, y, prevX, prevY);
+         gc.setBackground(Display.getCurrent().getSystemColor(color)); 
+         gc.fillOval(x - 5, y -5, 10, 10);
+      }      
+   }   
+       
        /*
         * (non-Javadoc)
         * 
@@ -552,8 +758,15 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                                || ((prevLocation != null) && (prevLocation.getType() != GeoLocation.UNSET) && coverage.contains(
                                                prevLocation.getLatitude(), prevLocation.getLongitude())))
                {
-                       objects = GeoLocationCache.getInstance().getObjectsInArea(coverage);
-                       redraw();
+                  if(!historycalData)
+                  {
+                       objects = GeoLocationCache.getInstance().getObjectsInArea(coverage);
+                       redraw();
+                  }
+                  else
+                  {
+                     updateHistory();
+                  }
                }
        }
        
@@ -829,4 +1042,51 @@ public class GeoMapViewer extends Canvas implements PaintListener, GeoLocationCa
                        tracking = false;
                }
        }
+
+       /**
+        * Updates points for historical view
+        */
+       private void updateHistory()
+       {
+       final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
+          ConsoleJob job = new ConsoleJob(Messages.get().GeoMapViewer_DownloadJob_Title, viewPart, Activator.PLUGIN_ID, null) {
+         @Override
+         protected void runInternal(IProgressMonitor monitor) throws Exception
+         {
+            if(!strictTime)
+            {
+               till = new Date();
+            }
+            history = session.getLocationHistory(historyObject.getObjectId(), new Date(till.getTime() - timeFrame), till);
+            for(int i = 0; i < history.size(); i++)
+               locationTree.insert(history.get(i).getLatitude(), history.get(i).getLongitude(), history.get(i));
+            
+            runInUIThread(new Runnable() {
+               @Override
+               public void run()
+               {
+                  GeoMapViewer.this.redraw();
+               }
+            });
+         }
+
+         @Override
+         protected String getErrorMessage()
+         {
+            return Messages.get().GeoMapViewer_DownloadError;
+         }
+      };
+      job.setUser(false);
+      job.start();
+       }
+       
+       /**
+        * Sets new view period in minutes
+        */
+       public void changeTimePeriod(int timePeriod)
+       {
+          strictTime = false;
+          timeFrame = timePeriod * 60 * 1000;
+          updateHistory();
+       }
 }