From 774166c453f8e4d88f07cde0210f3518d99ec20b Mon Sep 17 00:00:00 2001
From: "Olivier J.N. Bertrand" <olivier.bertrand@uni-bielefeld.de>
Date: Fri, 8 Jun 2018 18:25:42 +0200
Subject: [PATCH] Bug correction Agent was not using convention in a safe
 manner. For example for database the convention should be return by the
 database itself if unique. The speed update in moving was not done with .loc
 and therefore lead to unchanged values the model of hafner have been updated
 to correct a mistakes in the viewing direction

---
 navipy/database/__init__.py  | 55 ++++++++++++++++++++++++------------
 navipy/models/hafner_2000.py | 12 ++++++--
 navipy/moving/agent.py       | 54 ++++++++++++++++++++++++++---------
 navipy/moving/maths.py       | 20 +++++--------
 4 files changed, 93 insertions(+), 48 deletions(-)

diff --git a/navipy/database/__init__.py b/navipy/database/__init__.py
index abeb5ad..431b525 100644
--- a/navipy/database/__init__.py
+++ b/navipy/database/__init__.py
@@ -7,10 +7,10 @@ import numpy as np
 import pandas as pd
 import sqlite3
 import io
-import warnings
 from navipy.scene import is_numeric_array, check_scene
 import navipy.maths.constants as mconst
 from navipy.tools.trajectory import Trajectory
+from navipy.scene import __spherical_indeces__
 import logging
 
 
@@ -151,12 +151,12 @@ class DataBase():
             self._logger.exception(msg)
             raise NameError(msg)
 
-        azimuth = np.linspace(-180, 180, 360)
-        elevation = np.linspace(-90, 90, 180)
+        azimuth = np.deg2rad(np.linspace(-180, 180, 360))
+        elevation = np.deg2rad(np.linspace(-90, 90, 180))
         [ma, me] = np.meshgrid(azimuth, elevation)
         self.viewing_directions = np.zeros((ma.shape[0], ma.shape[1], 2))
-        self.viewing_directions[..., 0] = me
-        self.viewing_directions[..., 1] = ma
+        self.viewing_directions[..., __spherical_indeces__['elevation']] = me
+        self.viewing_directions[..., __spherical_indeces__['azimuth']] = ma
 
     def table_exist(self, tablename):
         """
@@ -465,6 +465,7 @@ class DataBaseLoad(DataBase):
                                   R,G,B or D (Distance)'
                 self._logger.exception(msg)
                 raise ValueError(msg)
+        self.__convention = None
 
     @property
     def create(self):
@@ -491,6 +492,35 @@ class DataBaseLoad(DataBase):
             toyield.drop('id', inplace=True)
             yield toyield
 
+    @property
+    def rotation_convention(self):
+        """ Return the convention in the database
+
+        The database can technically contains more than one convention.
+        Although it is discourage to do so, it is not forbidden.
+
+        If more than one convention is found in the database, this function
+        will issue awarning when more than one convention is present in the database.
+        """
+        posorient = pd.read_sql_query(
+            "select * from position_orientation;", self.db)
+        posorient.set_index('id', inplace=True)
+        if self.__convention is None:
+            if 'rotconv_id' in posorient.columns:
+                rotconv = posorient.loc[:, 'rotconv_id']
+                if np.all(rotconv == rotconv.iloc[0]):
+                    self.__convention = rotconv
+                else:
+                    self._logger.warning(
+                        'More than one convention have been found in database')
+                    self.__convention = None
+            else:
+                self._logger.warning("you are loading a database with old\
+                                   conventions, it will be transformed\
+                                   automatically into the new one")
+                self.__convention = 'rxyz'
+        return self.__convention
+
     @property
     def posorients(self):
         """Return the position orientations of all points in the \
@@ -501,20 +531,9 @@ class DataBaseLoad(DataBase):
         posorient = pd.read_sql_query(
             "select * from position_orientation;", self.db)
         posorient.set_index('id', inplace=True)
-        if 'rotconv_id' in posorient.columns:
-            rotconv = posorient.loc[:, 'rotconv_id']
-            if np.all(rotconv == rotconv.iloc[0]):
-                posorients = Trajectory(
-                    rotconv.iloc[0], indeces=posorient.index)
-                posorients.from_dataframe(posorient)
-            else:
-                posorients = posorient
-        else:
-            warnings.warn("you are loading a database with old\
-                               conventions, it will be transformed\
-                               automatically into the new one")
+        if self.rotation_convention is not None:
             posorients = Trajectory()
-            posorients.from_dataframe(posorient, rotconv='rxyz')
+            posorients.from_dataframe(posorient, rotconv=self.__convention)
         return posorients
 
     @property
diff --git a/navipy/models/hafner_2000.py b/navipy/models/hafner_2000.py
index 4e72a80..3c94b92 100644
--- a/navipy/models/hafner_2000.py
+++ b/navipy/models/hafner_2000.py
@@ -1,6 +1,8 @@
 from navipy import Brain
 from navipy.processing.pcode import apcv, skyline
 import pandas as pd
+import numpy as np
+from navipy.scene import __spherical_indeces__
 # 0) Define a class heriting from Brain
 
 
@@ -24,8 +26,10 @@ def processing(scene, viewing_directions, channel):
     scene[..., 3, :] = 1/scene[..., 3, :]
     # Calculate the skyline
     scene = skyline(scene)
-
-    comanv = apcv(scene, viewing_directions)
+    # skyline viewing direction
+    viewdir = viewing_directions[1, ...][np.newaxis, ...]
+    viewdir[..., __spherical_indeces__['elevation']] = 0
+    comanv = apcv(scene, viewdir)
     return comanv[..., channel, :]
 
 
@@ -34,6 +38,7 @@ def comparing(current, memory):
 
     homing vector  = current vector - memory vector
     """
+    return current-memory
 
 
 class ASVBrain(Brain):
@@ -53,9 +58,10 @@ class ASVBrain(Brain):
                              self.vision.viewing_directions,
                              self.channel)
         homing_vector = comparing(current, self.memory)
+        homing_vector = np.squeeze(homing_vector)
         indeces = [('location', 'dx'), ('location', 'dy'),
                    ('location', 'dz'), (convention, 'dalpha_0'),
                    (convention, 'dalpha_1'), (convention, 'dalpha_2')]
         velocity = pd.Series(data=0, index=pd.MultiIndex.from_tuples(indeces))
-        velocity['location'] = homing_vector
+        velocity.loc['location'] = homing_vector
         return velocity
diff --git a/navipy/moving/agent.py b/navipy/moving/agent.py
index baa501a..0c4775c 100644
--- a/navipy/moving/agent.py
+++ b/navipy/moving/agent.py
@@ -28,6 +28,7 @@ import multiprocessing
 from multiprocessing import Queue, JoinableQueue, Process
 import inspect
 import navipy.moving.maths as navimomath
+from navipy.database import DataBaseLoad
 
 version = float(nx.__version__)
 
@@ -52,25 +53,33 @@ class AbstractAgent():
     def __init__(self, convention='rzyx'):
         self._brain = DefaultBrain()
         self._alter_posorientvel = defaultcallback
-        tuples = [('location', 'x'), ('location', 'y'),
-                  ('location', 'z'), (convention, 'alpha_0'),
-                  (convention, 'alpha_1'), (convention, 'alpha_2')]
+        tuples = [('location', 'x'),
+                  ('location', 'y'),
+                  ('location', 'z'),
+                  (convention, 'alpha_0'),
+                  (convention, 'alpha_1'),
+                  (convention, 'alpha_2')]
         index = pd.MultiIndex.from_tuples(tuples,
                                           names=['position',
                                                  'orientation'])
         self._posorient_col = index
 
-        tuples_vel = [('location', 'x'), ('location', 'y'),
-                      ('location', 'z'), (convention, 'alpha_0'),
-                      (convention, 'alpha_1'), (convention, 'alpha_2'),
-                      ('location', 'dx'), ('location', 'dy'),
-                      ('location', 'dz'), (convention, 'dalpha_0'),
-                      (convention, 'dalpha_1'), (convention, 'dalpha_2')]
+        tuples_vel = [('location', 'dx'),
+                      ('location', 'dy'),
+                      ('location', 'dz'),
+                      (convention, 'dalpha_0'),
+                      (convention, 'dalpha_1'),
+                      (convention, 'dalpha_2')]
         index_vel = pd.MultiIndex.from_tuples(tuples_vel,
                                               names=['position',
                                                      'orientation'])
+        tuples_posvel = tuples
+        tuples_posvel.extend(tuples_vel)
+        index_posvel = pd.MultiIndex.from_tuples(tuples_posvel,
+                                                 names=['position',
+                                                        'orientation'])
         self._velocity_col = index_vel
-        self._posorient_vel_col = self._velocity_col.copy()
+        self._posorient_vel_col = index_posvel
         self._posorient_vel = pd.Series(
             index=self._posorient_vel_col,
             data=np.nan)
@@ -105,6 +114,15 @@ class AbstractAgent():
         self._posorient_vel.loc[self._velocity_col] = \
             velocity.loc[self._velocity_col]
 
+    @property
+    def posorient_vel(self):
+        return self._posorient_vel.copy()
+
+    @posorient_vel.setter
+    def posorient_vel(self, posorient_vel):
+        self.posorient = posorient_vel
+        self.velocity = posorient_vel
+
     @property
     def brain(self):
         return inspect.getsourcelines(self._brain)
@@ -176,11 +194,14 @@ GridAgent is a close loop agent here its position is snap to a grid.
 
     """
 
-    def __init__(self, brain, convention,
+    def __init__(self, brain,
                  posorients_queue=None,
                  results_queue=None):
-        if convention is None:
-            raise Exception("convention must be set")
+        if not isinstance(brain.renderer, DataBaseLoad):
+            msg = 'GridAgent only works with a brain having '
+            msg += 'a renderer of type DataBaseLoad'
+            raise TypeError(msg)
+        convention = brain.renderer.rotation_convention
         if (posorients_queue is not None) and (results_queue is not None):
             multiprocessing.Process.__init__(self)
         AbstractAgent.__init__(self, convention)
@@ -283,14 +304,19 @@ the agent motion, or
 
     """
 
-    def __init__(self, brain):
+    def __init__(self, brain, mode_of_motion):
         self._brain = copy.copy(brain)
         # Init the graph
         self._graph = nx.DiGraph()
+        if not isinstance(self._brain.renderer, DataBaseLoad):
+            msg = 'GraphAgent only works with a brain having '
+            msg += 'a renderer of type DataBaseLoad'
+            raise TypeError(msg)
         for row_id, posor in self._brain.posorients.iterrows():
             posor.name = row_id
             self._graph.add_node(row_id,
                                  posorient=posor)
+        self.mode_of_motion = mode_of_motion
 
     @property
     def graph(self):
diff --git a/navipy/moving/maths.py b/navipy/moving/maths.py
index 55389c7..d2d14da 100644
--- a/navipy/moving/maths.py
+++ b/navipy/moving/maths.py
@@ -49,7 +49,7 @@ def next_pos(motion_vec, move_mode, move_param=None):
         # speed in spherical coord
         epsilon = np.arctan2(speed['location']['dz'],
                              np.sqrt(speed['location']['dx']**2 +
-                             speed['location']['dy']**2))
+                                     speed['location']['dy']**2))
         phi = np.arctan2(speed['location']['dy'], speed['location']['dx'])
         radius = np.sqrt(np.sum(speed**2))
         if np.isclose(radius, 0):
@@ -88,15 +88,9 @@ def next_pos(motion_vec, move_mode, move_param=None):
     else:
         raise ValueError('grid_mode is not supported')
     toreturn = motion_vec
-    toreturn['location']['x'] = (toreturn['location']['x'] +
-                                 speed['location']['dx'])
-    toreturn['location']['y'] = (toreturn['location']['y'] +
-                                 speed['location']['dy'])
-    toreturn['location']['z'] = (toreturn['location']['z'] +
-                                 speed['location']['dz'])
-    # toreturn.loc[['x', 'y', 'z']] += speed.rename({'dx': 'x',
-    #                                               'dy': 'y',
-    #                                               'dz': 'z'}) * scaling
+    toreturn.loc[('location', 'x')] += speed['location']['dx']
+    toreturn.loc[('location', 'y')] += speed['location']['dy']
+    toreturn.loc[('location', 'z')] += speed['location']['dz']
     return toreturn
 
 
@@ -110,9 +104,9 @@ def closest_pos(pos, positions):
     """
 
     euclidian_dist = np.sqrt(
-             (pos['location']['x'] - positions['location']['x'])**2
-             + (pos['location']['y'] - positions['location']['y'])**2
-             + (pos['location']['z'] - positions['location']['z'])**2)
+        (pos['location']['x'] - positions['location']['x'])**2
+        + (pos['location']['y'] - positions['location']['y'])**2
+        + (pos['location']['z'] - positions['location']['z'])**2)
     return positions.loc[euclidian_dist.idxmin()]
 
 
-- 
GitLab