diff --git a/data-discovery/src/main/java/search/DbObstap.java b/data-discovery/src/main/java/search/DbObstap.java
index ea50b70d70bf19c4576ccbd13a0b2315c30f37c6..5c5f5571d55da156ce18f8b08343a3a284eac984 100644
--- a/data-discovery/src/main/java/search/DbObstap.java
+++ b/data-discovery/src/main/java/search/DbObstap.java
@@ -80,486 +80,515 @@ public class DbObstap
    }
 
 
-
-
-   public String[] queryOverlapingPubdid(QueryArgs qArgs)
-      throws Exception
-   {
-      LOGGER.fine("trace");
-
-      String inputRegion = toPgSphereSqlTypeString(qArgs.pos);
-      String dbRegion    = toRegionColumnName(qArgs.pos.system);
-
-      String theQuery = "SELECT obs_publisher_did FROM obscore WHERE ("+inputRegion+"  && " + dbRegion + ")";
-
-      boolean  vel_valid = (qArgs.band != null) && (qArgs.band.system != Band.System.NONE);
-      if(vel_valid)
-      {
-         String prefix = toSpecColumnNamePrefix(qArgs.band.system);
-         String vel_no_overlap
-            = "((" + prefix + "_min > " + Double.toString(qArgs.band.max)
-            + ") OR (" + prefix + "_max < " + Double.toString(qArgs.band.min) + "))";
-
-         theQuery += " AND ( ("+prefix+"_min is null) OR ("+prefix+"_max is null) OR (NOT " + vel_no_overlap + "))";
-         /* NOTE '... OR (em_min is null)' statement causes to include 2D datasets if they overlap in sky
-          * It is the legacy-search behaviour - however is that useful ?
-          */
-      }
-
-      if(qArgs.collection != null)
-      {
-         String addColl = "";
-         if(qArgs.collection.length() > 0)
-            addColl += "(obs_collection LIKE '%" + qArgs.collection + "%')";
-      
-         if(addColl.length() > 0) theQuery += " AND (" + addColl + ")";
-      }
-
-      theQuery += appendIntervalConstraint(qArgs.fov,     "s_fov");
-      theQuery += appendIntervalConstraint(qArgs.spatres, "s_resolution");
-      theQuery += appendIntervalConstraint(qArgs.specrp,  "em_res_power");
-      theQuery += appendIntervalConstraint(qArgs.exptime, "t_exptime");
-      theQuery += appendIntervalConstraint(qArgs.timeres, "t_resolution");
-
-      theQuery += appendStringMatchConstraint(qArgs.id,         "obs_publisher_did");
-      theQuery += appendStringMatchConstraint(qArgs.facility,   "facility_name");
-      theQuery += appendStringMatchConstraint(qArgs.instrument, "instrument_name");
-      theQuery += appendStringMatchConstraint(qArgs.dptype,     "dataproduct_type");
-
-      theQuery += appendStringMatchConstraint(qArgs.target, "target_name");
-      theQuery += appendStringMatchConstraint(qArgs.format, "access_format");
-
-      if(qArgs.calib != null)
-         theQuery += " AND (" + qArgs.calib + " = calib_level)";
-
-      if(qArgs.maxrec != null)
-         theQuery += " LIMIT " + qArgs.maxrec;
-
-      LOGGER.fine(theQuery);
-
-      List<String> pubdidList = new ArrayList<>();
-
-      LOGGER.fine("Connecting to: " + dbConnArgs.uri() + " with user: " + dbConnArgs.userName() );
-
-      try( 
-            Connection conn = DriverManager.getConnection(dbConnArgs.uri(), dbConnArgs.userName(), dbConnArgs.password());
-            Statement  st   = conn.createStatement();
-            ResultSet  res  = st.executeQuery(theQuery);)
-      {
-         while (res.next())
-         {
-            String pubdid_str = res.getString("obs_publisher_did");
-            pubdidList.add(pubdid_str);
-         }
-      }
-      catch (SQLException se)
-      {
-         dbError(se);
-         // se.printStackTrace();
-         throw new Exception(se.toString());
-      }
-
-      String[] pubdidArr = pubdidList.toArray(new String[0]);
-
-      LOGGER.fine("pubdidArr[] length: " + pubdidArr.length);
-
-      return pubdidArr;
-   }
-
-   private String appendIntervalConstraint(Interval interval, String colName)
-   {
-      if(interval != null)
-      {
-         String no_overlap
-            = "((" + colName + " > " + Double.toString(interval.max)
-            + ") OR (" + colName + " < " + Double.toString(interval.min) + "))";
-
-         return " AND ( (" + colName + " is null) OR (NOT " + no_overlap + "))";
-      }
-      else
-      {
-         return "";
-      }
-   }
-
-   private String appendStringMatchConstraint(String str, String colName)
-   {
-      if(str != null)
-      {
-         return " AND ( (" + colName + " is null) OR ('" + str.trim() + "' = " + colName + "))";
-      }
-      else
-      {
-         return "";
-      }
-   }
-
-
-
-   public Obstap[] queryOutputData(String[] pubdidArr, Pos pos, Band band)
+   public int queryObstapRowCount()
       throws Exception
-   {
-      LOGGER.fine("trace");
-
-      String commaSepPubdids  = String.join("\',\'", pubdidArr);
-
-      String inputRegion = toPgSphereSqlTypeString(pos);
-      String dbRegion    = toRegionColumnName(pos.system);
-
-      String theQuery ="SELECT *," 
-         + inputRegion + " && " + dbRegion + " AS inputOverlapsDb, "
-         + inputRegion + " <@ " + dbRegion + " AS inputInsideDb, "
-         + inputRegion + " @> " + dbRegion + " AS dbInsideInput FROM obscore WHERE (obs_publisher_did IN (\'"
-         +commaSepPubdids+"\'))";
-
-      List<Obstap> obstapList = new ArrayList<>();
-
-      LOGGER.fine(theQuery);
-
-      LOGGER.fine("Connecting to: " + dbConnArgs.uri()
-            + " with optional user/pwd: " + dbConnArgs.userName() +" / "+ dbConnArgs.password() );
-      try(
-            Connection conn = DriverManager.getConnection(dbConnArgs.uri(), dbConnArgs.userName(), dbConnArgs.password());
-            Statement  st   = conn.createStatement();
-            ResultSet  res  = st.executeQuery(theQuery);)
-      {
-         while (res.next())
-         {
-            Obstap obstap = new Obstap();
-
-            obstap.dataproduct_type  = this.getString(res,"dataproduct_type");
-            obstap.calib_level       = this.getInt(res,"calib_level");
-            obstap.obs_collection    = this.getString(res,"obs_collection");
-            obstap.obs_title         = this.getString(res,"obs_title");
-            obstap.obs_id            = this.getString(res,"obs_id");
-            obstap.obs_publisher_did = this.getString(res,"obs_publisher_did");
-
-            obstap.bib_reference = this.getString(res,"bib_reference");
-            obstap.data_rights   = this.getString(res,"data_rights");
-
-            obstap.access_url     = this.getString(res,"access_url");
-            obstap.access_format  = this.getString(res,"access_format");
-            obstap.access_estsize = this.getLong(res,"access_estsize");
-
-            obstap.target_name = this.getString(res,"target_name");
-
-            obstap.s_ra         = this.getDouble(res,"s_ra");
-            obstap.s_dec        = this.getDouble(res,"s_dec");
-            obstap.s_fov        = this.getDouble(res,"s_fov");
-            obstap.s_region     = this.getString(res,"s_region");
-            obstap.s_xel1       = this.getLong(res,"s_xel1");
-            obstap.s_xel2       = this.getLong(res,"s_xel2");
-            obstap.s_resolution = this.getDouble(res,"s_resolution");
-
-            obstap.t_min         = this.getDouble(res,"t_min");
-            obstap.t_max         = this.getDouble(res,"t_max");
-            obstap.t_exptime     = this.getDouble(res,"t_exptime");
-            obstap.t_resolution  = this.getDouble(res,"t_resolution");
-            obstap.t_xel         = this.getLong(res,"t_xel");
-
-            obstap.em_min       = this.getDouble(res,"em_min");
-            obstap.em_max       = this.getDouble(res,"em_max");
-            obstap.em_res_power = this.getDouble(res,"em_res_power");
-            obstap.em_xel       = this.getLong(res,"em_xel");
-
-            obstap.o_ucd = this.getString(res,"o_ucd");
-
-            obstap.pol_states = this.getString(res,"pol_states");
-            obstap.pol_xel    = this.getLong(res ,"pol_xel");
-
-            obstap.facility_name = this.getString(res,"facility_name");
-            obstap.instrument_name = this.getString(res,"instrument_name");
-
-            // VLKB extension
-
-            obstap.s_region_galactic = this.getString(res,"s_region_galactic");
-            obstap.vel_min           = this.getDouble(res,"vel_min");
-            obstap.vel_max           = this.getDouble(res,"vel_max");
-
-            boolean inputOverlapsDb = res.getBoolean("inputOverlapsDb");
-            boolean inputInsideDb = res.getBoolean("inputInsideDb");
-            boolean dbInsideInput = res.getBoolean("dbInsideInput");
-            LOGGER.finest("inpOverlapsDb, inpInsideDb, dbInsideInp : "
-                  + inputOverlapsDb + " " + inputInsideDb + " " + dbInsideInput);
-
-            obstap.overlapCodeSky = convertToOverlapCodeSky(inputOverlapsDb, inputInsideDb, dbInsideInput);
-
-            obstap.overlapCodeVel = convertToOverlapCodeVel(band,
-                  obstap.em_min,obstap.em_max,
-                  obstap.vel_min,obstap.vel_max );
-
-            obstap.overlapCode = convertToOverlapCode(obstap.overlapCodeSky, obstap.overlapCodeVel);
-
-
-            obstapList.add(obstap);
-         }
-
-         LOGGER.fine("From DB collected # of Obstap : " + obstapList.size());
-      }
-      catch (SQLException se)
-      {
-         dbError(se);
-         // se.printStackTrace();
-         throw new Exception(se.toString());
-      }
-
-      Obstap[] cubes = obstapList.toArray(new Obstap[0]);
-
-      return cubes;
-   }
-
-   /* convert overlap codes (AST definition):
-
-   "0 - The check could not be performed because the second Region could not be mapped into the coordinate system of the first Region.",
-   "1 - There is no overlap between the two Regions.",
-   "2 - The first Region is completely inside the second Region.",
-   "3 - The second Region is completely inside the first Region.",
-   "4 - There is partial overlap between the two Regions.",
-   "5 - The Regions are identical to within their uncertainties.",
-   "6 - The second Region is the exact negation of the first Region to within their uncertainties."
-   */
-   private Integer convertToOverlapCodeSky(boolean inpOverlapsDb, boolean inpInDb, boolean dbInInp)
-   {
-      if(inpOverlapsDb)
-      {
-         if(!inpInDb && !dbInInp) return 4;
-         else if( inpInDb && !dbInInp) return 3;
-         else if(!inpInDb &&  dbInInp) return 2;
-         else return 5;
-      }
-      else
-      {
-         return 1;
-      }
-   }
-
-   private boolean intervalOverlap(double amin, double amax, double bmin, double bmax)
-   {
-      boolean AoverB 
-          = (amin <= bmin) && (bmin <= amax)
-         || (amin <= bmax) && (bmax <= amax);
-
-      boolean BoverA
-          = (bmin <= amin) && (amin <= bmax)
-         || (bmin <= amax) && (amax <= bmax);
-
-      return  AoverB || BoverA;
-   }
-
-   private Integer convertToOverlapCodeVel(Band band,
-         Double w_min, Double w_max, // WAVE
-         Double v_min, Double v_max) // VELO
-   {
-      if(band != null)
-      {
-         boolean v_valid = (v_min != null) && (v_max != null);
-         boolean w_valid = (w_min != null) && (w_max != null);
-
-         if( v_valid && (band.system == Band.System.VELO_LSRK) )
-         {
-            boolean overlap = intervalOverlap(band.min, band.max, v_min, v_max);;
-
-            boolean dbInInp = (band.min <= v_min) && (v_min <= band.max)
-               && (band.min <= v_max) && (v_max <= band.max);
-
-            boolean inpInDb = (v_min <= band.min) && (band.min <= v_max)
-               && (v_min <= band.max ) && (band.max <= v_max);
-
-            // do as in Sky-overlap
-            return convertToOverlapCodeSky(overlap, inpInDb, dbInInp);
-         }
-         else if( w_valid && (band.system == Band.System.WAVE_Barycentric) )
-         {
-            boolean overlap = intervalOverlap(band.min, band.max, w_min, w_max);;
-
-            boolean dbInInp = (band.min <= w_min) && (w_min <= band.max)
-               && (band.min <= w_max) && (w_max <= band.max);
-
-            boolean inpInDb = (w_min <= band.min) && (band.min <= w_max)
-               && (w_min <= band.max ) && (band.max <= w_max);
-
-            // do as in Sky-overlap
-            return convertToOverlapCodeSky(overlap, inpInDb, dbInInp);
-         }
-         else
-         {
-            return null;
-         }
-      }
-      else
-      {
-         return null;
-      }
-   }
-
-   private Integer convertToOverlapCode(Integer ovcSky, Integer ovcVel)
-   {
-      if(ovcSky == null) return ovcVel; // spectral images or both null
-      else if(ovcVel == null) return ovcSky; // 2D sky images or both null
-      else if((ovcSky != null) && (ovcVel != null))
-      {
-         if((ovcSky == 1) || (ovcVel == 1) ) return 1; // no overlap
-         else if(ovcSky == ovcVel) return ovcSky;
-         else return 4;
-      }
-      else return null; // both null
-   }
-
-
-   /* conversions tolerate missing columns */
-
-   private Integer getInt(ResultSet res, String colLabel)
-   {
-      try
-      {
-         Integer value = res.getInt(colLabel);
-         return (res.wasNull() ? null : value);
-      }
-      catch(SQLException se)
-      {
-         dbError(se);
-         return null;
-      }
-   }
-
-   private Long getLong(ResultSet res, String colLabel)
-   {
-      try
-      {
-         Long value = res.getLong(colLabel);
-         return (res.wasNull() ? null : value);
-      }
-      catch(SQLException se)
-      {
-         dbError(se);
-         return null;
-      }
-   }
-
-   private Double getDouble(ResultSet res, String colLabel)
-   {
-      try
-      {
-         Double value = res.getDouble(colLabel);
-         return (res.wasNull() ? null : value);
-      }
-      catch(SQLException se)
-      {
-         dbError(se);
-         return null;
-      }
-   }
-
-   private String getString(ResultSet res, String colLabel)
-   {
-      try
-      {
-         String value = res.getString(colLabel);
-         return (res.wasNull() ? null : value);
-      }
-      catch(SQLException se)
-      {
-         dbError(se);
-         return null;
-      }
-   }
-
-
-
-   private String toPgSphereSqlTypeString(Pos pos)
-   {
-      double lon;
-      double lat;
-      double radius;
-      double dlon;
-      double dlat;
-
-      String inputRegion = null;
-
-      switch(pos.shape)
-      {
-         case CIRCLE:
-            lon = pos.circle.lon;
-            lat = pos.circle.lat;
-            radius = pos.circle.radius;
-            inputRegion = "scircle '<(" + Double.toString(lon) + "d," + Double.toString(lat) + "d),"
-               + Double.toString(radius) + "d>'";
-            break;
-
-         case RANGE:
-            lon =  (pos.range.lon1 + pos.range.lon2)/2.0;
-            lat =  (pos.range.lat1 + pos.range.lat2)/2.0;
-            dlon = (pos.range.lon2 - pos.range.lon1)/2.0;
-            dlat = (pos.range.lat2 - pos.range.lat1)/2.0;
-
-            /* South-West and North-East corners of a box */
-            String sw_lon = Double.toString(lon - dlon/2.0);
-            String sw_lat = Double.toString(lat - dlat/2.0);
-            String ne_lon = Double.toString(lon + dlon/2.0);
-            String ne_lat = Double.toString(lat + dlat/2.0);
-
-            inputRegion = "sbox '( ("+ sw_lon + "d, " + sw_lat + "d), (" + ne_lon +"d, " + ne_lat + "d) )'";
-            break;
-
-         case POLYGON:
-            // FIXME redefine Polygon as point-array:
-            assert(pos.polygon.lon.length == pos.polygon.lat.length);
-
-            // Polygon has at least 3 points
-            inputRegion = "spoly '( (" + pos.polygon.lon[0] + "d, " + pos.polygon.lat[0] + "d),";
-            for(int ii=1; ii < pos.polygon.lon.length; ii++)
-            {
-               inputRegion += ", (" + pos.polygon.lon[ii] + "d, " + pos.polygon.lat[ii] + "d)";
-            }
-            inputRegion += " )'";
-            break;
-
-         default:
-            throw new IllegalArgumentException("Pos::shape was: " + pos.shape.toString()
-                  + " but valid is CIRCLE or RANGE or POLYGON");
-      }
-
-      return inputRegion;
-   }
-
-   private String toRegionColumnName(Pos.System system)
-   {
-      String dbRegion;
-      switch(system)
-      {
-         case GALACTIC:
-            dbRegion = "polygon_region_galactic";
-            break;
-         default:
-            dbRegion = "polygon_region";
-      }
-      return dbRegion;
-   }
-
-   private String toSpecColumnNamePrefix(Band.System system)
-   {
-      // vlkb-volib/Band.System: WAVE_Barycentric, VELO_LSRK, GRID, NONE
-      String prefix;
-      switch(system)
-      {
-         case VELO_LSRK: prefix = "vel"; break;
-         default: prefix = "em";
-      }
-      return prefix;
-   }
-
-
-   private void dbError(SQLException se)
-   {
-      LOGGER.fine("SQLState : " + se.getSQLState());
-      LOGGER.fine("ErrorCode: " + se.getErrorCode());
-      LOGGER.warning("Message: " + se.getMessage());
-      Throwable t = se.getCause();
-      while(t != null) {
-         LOGGER.fine("Cause: " + t);
-         t = t.getCause();
-      }
-   }
+		{
+			LOGGER.fine("trace");
+
+			String theQuery = "SELECT count(*) AS total FROM obscore";
+			LOGGER.fine(theQuery);
+			LOGGER.fine("Connecting to: " + dbConnArgs.uri() + " with user: " + dbConnArgs.userName() );
+
+			int rowCount = -1;
+			try(
+					Connection conn = DriverManager.getConnection(dbConnArgs.uri(), dbConnArgs.userName(), dbConnArgs.password());
+					Statement  st   = conn.createStatement();
+					ResultSet  res  = st.executeQuery(theQuery);)
+			{
+				while (res.next())
+				{
+					rowCount = res.getInt("total");
+				}
+			}
+			catch (SQLException se)
+			{
+				dbError(se);
+				// se.printStackTrace();
+				throw new Exception(se.toString());
+			}
+
+			return rowCount;
+		}
+
+
+	public String[] queryOverlapingPubdid(QueryArgs qArgs)
+		throws Exception
+		{
+			LOGGER.fine("trace");
+
+			String inputRegion = toPgSphereSqlTypeString(qArgs.pos);
+			String dbRegion    = toRegionColumnName(qArgs.pos.system);
+
+			String theQuery = "SELECT obs_publisher_did FROM obscore WHERE ("+inputRegion+"  && " + dbRegion + ")";
+
+			boolean  vel_valid = (qArgs.band != null) && (qArgs.band.system != Band.System.NONE);
+			if(vel_valid)
+			{
+				String prefix = toSpecColumnNamePrefix(qArgs.band.system);
+				String vel_no_overlap
+					= "((" + prefix + "_min > " + Double.toString(qArgs.band.max)
+					+ ") OR (" + prefix + "_max < " + Double.toString(qArgs.band.min) + "))";
+
+				theQuery += " AND ( ("+prefix+"_min is null) OR ("+prefix+"_max is null) OR (NOT " + vel_no_overlap + "))";
+				/* NOTE '... OR (em_min is null)' statement causes to include 2D datasets if they overlap in sky
+				 * It is the legacy-search behaviour - however is that useful ?
+				 */
+			}
+
+			if(qArgs.collection != null)
+			{
+				String addColl = "";
+				if(qArgs.collection.length() > 0)
+					addColl += "(obs_collection LIKE '%" + qArgs.collection + "%')";
+
+				if(addColl.length() > 0) theQuery += " AND (" + addColl + ")";
+			}
+
+			theQuery += appendIntervalConstraint(qArgs.fov,     "s_fov");
+			theQuery += appendIntervalConstraint(qArgs.spatres, "s_resolution");
+			theQuery += appendIntervalConstraint(qArgs.specrp,  "em_res_power");
+			theQuery += appendIntervalConstraint(qArgs.exptime, "t_exptime");
+			theQuery += appendIntervalConstraint(qArgs.timeres, "t_resolution");
+
+			theQuery += appendStringMatchConstraint(qArgs.id,         "obs_publisher_did");
+			theQuery += appendStringMatchConstraint(qArgs.facility,   "facility_name");
+			theQuery += appendStringMatchConstraint(qArgs.instrument, "instrument_name");
+			theQuery += appendStringMatchConstraint(qArgs.dptype,     "dataproduct_type");
+
+			theQuery += appendStringMatchConstraint(qArgs.target, "target_name");
+			theQuery += appendStringMatchConstraint(qArgs.format, "access_format");
+
+			if(qArgs.calib != null)
+				theQuery += " AND (" + qArgs.calib + " = calib_level)";
+
+			if(qArgs.maxrec != null)
+				theQuery += " LIMIT " + qArgs.maxrec;
+
+			LOGGER.fine(theQuery);
+
+			List<String> pubdidList = new ArrayList<>();
+
+			LOGGER.fine("Connecting to: " + dbConnArgs.uri() + " with user: " + dbConnArgs.userName() );
+
+			try( 
+					Connection conn = DriverManager.getConnection(dbConnArgs.uri(), dbConnArgs.userName(), dbConnArgs.password());
+					Statement  st   = conn.createStatement();
+					ResultSet  res  = st.executeQuery(theQuery);)
+			{
+				while (res.next())
+				{
+					String pubdid_str = res.getString("obs_publisher_did");
+					pubdidList.add(pubdid_str);
+				}
+			}
+			catch (SQLException se)
+			{
+				dbError(se);
+				// se.printStackTrace();
+				throw new Exception(se.toString());
+			}
+
+			String[] pubdidArr = pubdidList.toArray(new String[0]);
+
+			LOGGER.fine("pubdidArr[] length: " + pubdidArr.length);
+
+			return pubdidArr;
+		}
+
+	private String appendIntervalConstraint(Interval interval, String colName)
+	{
+		if(interval != null)
+		{
+			String no_overlap
+				= "((" + colName + " > " + Double.toString(interval.max)
+				+ ") OR (" + colName + " < " + Double.toString(interval.min) + "))";
+
+			return " AND ( (" + colName + " is null) OR (NOT " + no_overlap + "))";
+		}
+		else
+		{
+			return "";
+		}
+	}
+
+	private String appendStringMatchConstraint(String str, String colName)
+	{
+		if(str != null)
+		{
+			return " AND ( (" + colName + " is null) OR ('" + str.trim() + "' = " + colName + "))";
+		}
+		else
+		{
+			return "";
+		}
+	}
+
+
+
+	public Obstap[] queryOutputData(String[] pubdidArr, Pos pos, Band band)
+		throws Exception
+		{
+			LOGGER.fine("trace");
+
+			String commaSepPubdids  = String.join("\',\'", pubdidArr);
+
+			String inputRegion = toPgSphereSqlTypeString(pos);
+			String dbRegion    = toRegionColumnName(pos.system);
+
+			String theQuery ="SELECT *," 
+				+ inputRegion + " && " + dbRegion + " AS inputOverlapsDb, "
+				+ inputRegion + " <@ " + dbRegion + " AS inputInsideDb, "
+				+ inputRegion + " @> " + dbRegion + " AS dbInsideInput FROM obscore WHERE (obs_publisher_did IN (\'"
+				+commaSepPubdids+"\'))";
+
+			List<Obstap> obstapList = new ArrayList<>();
+
+			LOGGER.fine(theQuery);
+
+			LOGGER.fine("Connecting to: " + dbConnArgs.uri()
+					+ " with optional user/pwd: " + dbConnArgs.userName() +" / "+ dbConnArgs.password() );
+			try(
+					Connection conn = DriverManager.getConnection(dbConnArgs.uri(), dbConnArgs.userName(), dbConnArgs.password());
+					Statement  st   = conn.createStatement();
+					ResultSet  res  = st.executeQuery(theQuery);)
+			{
+				while (res.next())
+				{
+					Obstap obstap = new Obstap();
+
+					obstap.dataproduct_type  = this.getString(res,"dataproduct_type");
+					obstap.calib_level       = this.getInt(res,"calib_level");
+					obstap.obs_collection    = this.getString(res,"obs_collection");
+					obstap.obs_title         = this.getString(res,"obs_title");
+					obstap.obs_id            = this.getString(res,"obs_id");
+					obstap.obs_publisher_did = this.getString(res,"obs_publisher_did");
+
+					obstap.bib_reference = this.getString(res,"bib_reference");
+					obstap.data_rights   = this.getString(res,"data_rights");
+
+					obstap.access_url     = this.getString(res,"access_url");
+					obstap.access_format  = this.getString(res,"access_format");
+					obstap.access_estsize = this.getLong(res,"access_estsize");
+
+					obstap.target_name = this.getString(res,"target_name");
+
+					obstap.s_ra         = this.getDouble(res,"s_ra");
+					obstap.s_dec        = this.getDouble(res,"s_dec");
+					obstap.s_fov        = this.getDouble(res,"s_fov");
+					obstap.s_region     = this.getString(res,"s_region");
+					obstap.s_xel1       = this.getLong(res,"s_xel1");
+					obstap.s_xel2       = this.getLong(res,"s_xel2");
+					obstap.s_resolution = this.getDouble(res,"s_resolution");
+
+					obstap.t_min         = this.getDouble(res,"t_min");
+					obstap.t_max         = this.getDouble(res,"t_max");
+					obstap.t_exptime     = this.getDouble(res,"t_exptime");
+					obstap.t_resolution  = this.getDouble(res,"t_resolution");
+					obstap.t_xel         = this.getLong(res,"t_xel");
+
+					obstap.em_min       = this.getDouble(res,"em_min");
+					obstap.em_max       = this.getDouble(res,"em_max");
+					obstap.em_res_power = this.getDouble(res,"em_res_power");
+					obstap.em_xel       = this.getLong(res,"em_xel");
+
+					obstap.o_ucd = this.getString(res,"o_ucd");
+
+					obstap.pol_states = this.getString(res,"pol_states");
+					obstap.pol_xel    = this.getLong(res ,"pol_xel");
+
+					obstap.facility_name = this.getString(res,"facility_name");
+					obstap.instrument_name = this.getString(res,"instrument_name");
+
+					// VLKB extension
+
+					obstap.s_region_galactic = this.getString(res,"s_region_galactic");
+					obstap.vel_min           = this.getDouble(res,"vel_min");
+					obstap.vel_max           = this.getDouble(res,"vel_max");
+
+					boolean inputOverlapsDb = res.getBoolean("inputOverlapsDb");
+					boolean inputInsideDb = res.getBoolean("inputInsideDb");
+					boolean dbInsideInput = res.getBoolean("dbInsideInput");
+					LOGGER.finest("inpOverlapsDb, inpInsideDb, dbInsideInp : "
+							+ inputOverlapsDb + " " + inputInsideDb + " " + dbInsideInput);
+
+					obstap.overlapCodeSky = convertToOverlapCodeSky(inputOverlapsDb, inputInsideDb, dbInsideInput);
+
+					obstap.overlapCodeVel = convertToOverlapCodeVel(band,
+							obstap.em_min,obstap.em_max,
+							obstap.vel_min,obstap.vel_max );
+
+					obstap.overlapCode = convertToOverlapCode(obstap.overlapCodeSky, obstap.overlapCodeVel);
+
+
+					obstapList.add(obstap);
+				}
+
+				LOGGER.fine("From DB collected # of Obstap : " + obstapList.size());
+			}
+			catch (SQLException se)
+			{
+				dbError(se);
+				// se.printStackTrace();
+				throw new Exception(se.toString());
+			}
+
+			Obstap[] cubes = obstapList.toArray(new Obstap[0]);
+
+			return cubes;
+		}
+
+	/* convert overlap codes (AST definition):
+
+		"0 - The check could not be performed because the second Region could not be mapped into the coordinate system of the first Region.",
+		"1 - There is no overlap between the two Regions.",
+		"2 - The first Region is completely inside the second Region.",
+		"3 - The second Region is completely inside the first Region.",
+		"4 - There is partial overlap between the two Regions.",
+		"5 - The Regions are identical to within their uncertainties.",
+		"6 - The second Region is the exact negation of the first Region to within their uncertainties."
+	 */
+	private Integer convertToOverlapCodeSky(boolean inpOverlapsDb, boolean inpInDb, boolean dbInInp)
+	{
+		if(inpOverlapsDb)
+		{
+			if(!inpInDb && !dbInInp) return 4;
+			else if( inpInDb && !dbInInp) return 3;
+			else if(!inpInDb &&  dbInInp) return 2;
+			else return 5;
+		}
+		else
+		{
+			return 1;
+		}
+	}
+
+	private boolean intervalOverlap(double amin, double amax, double bmin, double bmax)
+	{
+		boolean AoverB 
+			= (amin <= bmin) && (bmin <= amax)
+			|| (amin <= bmax) && (bmax <= amax);
+
+		boolean BoverA
+			= (bmin <= amin) && (amin <= bmax)
+			|| (bmin <= amax) && (amax <= bmax);
+
+		return  AoverB || BoverA;
+	}
+
+	private Integer convertToOverlapCodeVel(Band band,
+			Double w_min, Double w_max, // WAVE
+			Double v_min, Double v_max) // VELO
+	{
+		if(band != null)
+		{
+			boolean v_valid = (v_min != null) && (v_max != null);
+			boolean w_valid = (w_min != null) && (w_max != null);
+
+			if( v_valid && (band.system == Band.System.VELO_LSRK) )
+			{
+				boolean overlap = intervalOverlap(band.min, band.max, v_min, v_max);;
+
+				boolean dbInInp = (band.min <= v_min) && (v_min <= band.max)
+					&& (band.min <= v_max) && (v_max <= band.max);
+
+				boolean inpInDb = (v_min <= band.min) && (band.min <= v_max)
+					&& (v_min <= band.max ) && (band.max <= v_max);
+
+				// do as in Sky-overlap
+				return convertToOverlapCodeSky(overlap, inpInDb, dbInInp);
+			}
+			else if( w_valid && (band.system == Band.System.WAVE_Barycentric) )
+			{
+				boolean overlap = intervalOverlap(band.min, band.max, w_min, w_max);;
+
+				boolean dbInInp = (band.min <= w_min) && (w_min <= band.max)
+					&& (band.min <= w_max) && (w_max <= band.max);
+
+				boolean inpInDb = (w_min <= band.min) && (band.min <= w_max)
+					&& (w_min <= band.max ) && (band.max <= w_max);
+
+				// do as in Sky-overlap
+				return convertToOverlapCodeSky(overlap, inpInDb, dbInInp);
+			}
+			else
+			{
+				return null;
+			}
+		}
+		else
+		{
+			return null;
+		}
+	}
+
+	private Integer convertToOverlapCode(Integer ovcSky, Integer ovcVel)
+	{
+		if(ovcSky == null) return ovcVel; // spectral images or both null
+		else if(ovcVel == null) return ovcSky; // 2D sky images or both null
+		else if((ovcSky != null) && (ovcVel != null))
+		{
+			if((ovcSky == 1) || (ovcVel == 1) ) return 1; // no overlap
+			else if(ovcSky == ovcVel) return ovcSky;
+			else return 4;
+		}
+		else return null; // both null
+	}
+
+
+	/* conversions tolerate missing columns */
+
+	private Integer getInt(ResultSet res, String colLabel)
+	{
+		try
+		{
+			Integer value = res.getInt(colLabel);
+			return (res.wasNull() ? null : value);
+		}
+		catch(SQLException se)
+		{
+			dbError(se);
+			return null;
+		}
+	}
+
+	private Long getLong(ResultSet res, String colLabel)
+	{
+		try
+		{
+			Long value = res.getLong(colLabel);
+			return (res.wasNull() ? null : value);
+		}
+		catch(SQLException se)
+		{
+			dbError(se);
+			return null;
+		}
+	}
+
+	private Double getDouble(ResultSet res, String colLabel)
+	{
+		try
+		{
+			Double value = res.getDouble(colLabel);
+			return (res.wasNull() ? null : value);
+		}
+		catch(SQLException se)
+		{
+			dbError(se);
+			return null;
+		}
+	}
+
+	private String getString(ResultSet res, String colLabel)
+	{
+		try
+		{
+			String value = res.getString(colLabel);
+			return (res.wasNull() ? null : value);
+		}
+		catch(SQLException se)
+		{
+			dbError(se);
+			return null;
+		}
+	}
+
+
+
+	private String toPgSphereSqlTypeString(Pos pos)
+	{
+		double lon;
+		double lat;
+		double radius;
+		double dlon;
+		double dlat;
+
+		String inputRegion = null;
+
+		switch(pos.shape)
+		{
+			case CIRCLE:
+				lon = pos.circle.lon;
+				lat = pos.circle.lat;
+				radius = pos.circle.radius;
+				inputRegion = "scircle '<(" + Double.toString(lon) + "d," + Double.toString(lat) + "d),"
+					+ Double.toString(radius) + "d>'";
+				break;
+
+			case RANGE:
+				lon =  (pos.range.lon1 + pos.range.lon2)/2.0;
+				lat =  (pos.range.lat1 + pos.range.lat2)/2.0;
+				dlon = (pos.range.lon2 - pos.range.lon1)/2.0;
+				dlat = (pos.range.lat2 - pos.range.lat1)/2.0;
+
+				/* South-West and North-East corners of a box */
+				String sw_lon = Double.toString(lon - dlon/2.0);
+				String sw_lat = Double.toString(lat - dlat/2.0);
+				String ne_lon = Double.toString(lon + dlon/2.0);
+				String ne_lat = Double.toString(lat + dlat/2.0);
+
+				inputRegion = "sbox '( ("+ sw_lon + "d, " + sw_lat + "d), (" + ne_lon +"d, " + ne_lat + "d) )'";
+				break;
+
+			case POLYGON:
+				// FIXME redefine Polygon as point-array:
+				assert(pos.polygon.lon.length == pos.polygon.lat.length);
+
+				// Polygon has at least 3 points
+				inputRegion = "spoly '( (" + pos.polygon.lon[0] + "d, " + pos.polygon.lat[0] + "d),";
+				for(int ii=1; ii < pos.polygon.lon.length; ii++)
+				{
+					inputRegion += ", (" + pos.polygon.lon[ii] + "d, " + pos.polygon.lat[ii] + "d)";
+				}
+				inputRegion += " )'";
+				break;
+
+			default:
+				throw new IllegalArgumentException("Pos::shape was: " + pos.shape.toString()
+						+ " but valid is CIRCLE or RANGE or POLYGON");
+		}
+
+		return inputRegion;
+	}
+
+	private String toRegionColumnName(Pos.System system)
+	{
+		String dbRegion;
+		switch(system)
+		{
+			case GALACTIC:
+				dbRegion = "polygon_region_galactic";
+				break;
+			default:
+				dbRegion = "polygon_region";
+		}
+		return dbRegion;
+	}
+
+	private String toSpecColumnNamePrefix(Band.System system)
+	{
+		// vlkb-volib/Band.System: WAVE_Barycentric, VELO_LSRK, GRID, NONE
+		String prefix;
+		switch(system)
+		{
+			case VELO_LSRK: prefix = "vel"; break;
+			default: prefix = "em";
+		}
+		return prefix;
+	}
+
+
+	private void dbError(SQLException se)
+	{
+		LOGGER.fine("SQLState : " + se.getSQLState());
+		LOGGER.fine("ErrorCode: " + se.getErrorCode());
+		LOGGER.warning("Message: " + se.getMessage());
+		Throwable t = se.getCause();
+		while(t != null) {
+			LOGGER.fine("Cause: " + t);
+			t = t.getCause();
+		}
+	}
 
 
 
diff --git a/data-discovery/src/main/java/webapi/VosiServlet.java b/data-discovery/src/main/java/webapi/VosiServlet.java
index 989a90ba1344874c78a632dc3584f6e6caeed48e..728e3d7eb270fae7df3e840e9e0f2141f2344264 100644
--- a/data-discovery/src/main/java/webapi/VosiServlet.java
+++ b/data-discovery/src/main/java/webapi/VosiServlet.java
@@ -27,123 +27,146 @@ import static java.nio.file.StandardCopyOption.*;
 public class VosiServlet
     extends javax.servlet.http.HttpServlet
 {
-    // for logs and debug
-    String className = this.getClass().getSimpleName();
+	String className = this.getClass().getSimpleName();
+	private static final SearchSettings settings = SearchSettings.getInstance("search.properties");
 
-// VOSI
-// String accessURL = null; // FIXME now read from MERGEURL later introduce own param
-// String funcName = "vlkb_cutout"; // FIXME read from config file
 
-	private static final String availStr = 
-		  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-		+ "<vosi:availability  "
-		+ " xmlns:vosi=\"http://www.ivoa.net/xml/VOSIAvailability/v1.0\">"
-		+ " <vosi:available>true</vosi:available>"
-		+ " <vosi:note>VLKB-SIAv2 " + Version.asString + " is accepting queries</vosi:note>"
-		+ "</vosi:availability>";
+	private String availString()
+	{ 
+		boolean available;
+		int obstapRowCount = -1;
+		DbObstap dbObstap;
+		String note;
+		try
+		{
+			synchronized(DbObstap.class)
+			{
+				dbObstap = new DbObstap(settings.dbConnArgs);
+			}
+			obstapRowCount = dbObstap.queryObstapRowCount();
+			available = (obstapRowCount >= 0);
+			note = "Table obstap in " + settings.dbConnArgs.toString() + " has "
+					+ String.valueOf(obstapRowCount)  + " rows";
+		}
+		catch(Exception ex)
+		{
+			note = "Accessing " + settings.dbConnArgs.toString()
+				+ " failed with " + ex.toString();
+			available = false;
+		}
+		String availStr = 
+			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+			+ "<vosi:availability xmlns:vosi=\"http://www.ivoa.net/xml/VOSIAvailability/v1.0\">"
+			+ "<vosi:available> "+ (available ? "true":"false") +" </vosi:available>"
+			+ "<vosi:note> VLKB-SIAv2 "+ Version.asString +" is accepting queries </vosi:note>"
+			+ "<vosi:note> " + note + " </vosi:note>"
+			+ "</vosi:availability>";
+
+		return availStr;
+	}
 
-	private String capsStr = null;
 
+	private String capsStr = null;
 
 	protected void SetCapsStr(String URL, String funcName)
 	{
 		if(URL != null)
 		{
-		
-            String accessURL = stripTrailingSlash(URL);
-
-    capsStr =
-		  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-
-		+ "<vosi:capabilities "
-		+    "xmlns:vosi=\"http://www.ivoa.net/xml/VOSICapabilities/v1.0\" "
-		+    "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
-		+    "xmlns:vod=\"http://www.ivoa.net/xml/VODataService/v1.1\">"
-
-		+ " <capability standardID=\"ivo://ivoa.net/std/VOSI#capabilities\">"
-		+ "   <interface xsi:type=\"vod:ParamHTTP\" version=\"1.0\">"
-		+ "     <accessURL use=\"full\">"
-		+          accessURL + "/capabilities"
-		+ "     </accessURL>"
-		+ "   </interface>"
-		+ " </capability>"
-
-		+ " <capability standardID=\"ivo://ivoa.net/std/VOSI#availability\">"
-		+ "   <interface xsi:type=\"vod:ParamHTTP\" version=\"1.0\">"
-		+ "     <accessURL use=\"full\">"
-		+          accessURL + "/availability"
-		+ "     </accessURL>"
-		+ "   </interface>"
-		+ " </capability>"
-
-		+ " <capability standardID=\"ivo://ivoa.net/std/SODA#sync-1.0\">"
-		+ "   <interface xsi:type=\"vod:ParamHTTP\" role=\"std\" version=\"1.0\">"
-		+ "     <accessURL use=\"full\">"
-		+          accessURL + "/" + funcName
-		+ "     </accessURL>"
-		+ "   </interface>"
-		+ " </capability>"
-
-		+ " <capability standardID=\"ivo://ivoa.net/std/SODA#async-1.0\">"
-		+ "   <interface xsi:type=\"vod:ParamHTTP\" role=\"std\" version=\"1.0\">"
-		+ "     <accessURL use=\"full\">"
-		+          accessURL + "/" + funcName + "_uws/soda_cuts"
-		+ "     </accessURL>"
-		+ "   </interface>"
-		+ " </capability>"
-
-	+ "</vosi:capabilities>";
+
+			String accessURL = stripTrailingSlash(URL);
+
+			capsStr =
+				"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+
+				+ "<vosi:capabilities "
+				+    "xmlns:vosi=\"http://www.ivoa.net/xml/VOSICapabilities/v1.0\" "
+				+    "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
+				+    "xmlns:vod=\"http://www.ivoa.net/xml/VODataService/v1.1\">"
+
+				+ " <capability standardID=\"ivo://ivoa.net/std/VOSI#capabilities\">"
+				+ "   <interface xsi:type=\"vod:ParamHTTP\" version=\"1.0\">"
+				+ "     <accessURL use=\"full\">"
+				+          accessURL + "/capabilities"
+				+ "     </accessURL>"
+				+ "   </interface>"
+				+ " </capability>"
+
+				+ " <capability standardID=\"ivo://ivoa.net/std/VOSI#availability\">"
+				+ "   <interface xsi:type=\"vod:ParamHTTP\" version=\"1.0\">"
+				+ "     <accessURL use=\"full\">"
+				+          accessURL + "/availability"
+				+ "     </accessURL>"
+				+ "   </interface>"
+				+ " </capability>"
+
+				+ " <capability standardID=\"ivo://ivoa.net/std/SODA#sync-1.0\">"
+				+ "   <interface xsi:type=\"vod:ParamHTTP\" role=\"std\" version=\"1.0\">"
+				+ "     <accessURL use=\"full\">"
+				+          accessURL + "/" + funcName
+				+ "     </accessURL>"
+				+ "   </interface>"
+				+ " </capability>"
+
+				+ " <capability standardID=\"ivo://ivoa.net/std/SODA#async-1.0\">"
+				+ "   <interface xsi:type=\"vod:ParamHTTP\" role=\"std\" version=\"1.0\">"
+				+ "     <accessURL use=\"full\">"
+				+          accessURL + "/" + funcName + "_uws/soda_cuts"
+				+ "     </accessURL>"
+				+ "   </interface>"
+				+ " </capability>"
+
+				+ "</vosi:capabilities>";
 		}
 	}
 
 
 	String stripTrailingSlash(String path)
 	{
-       		if (path.endsWith("/"))
-           		return path.substring(0,path.length()-1);
-       		else
-           		return path;
-    	}
+		if (path.endsWith("/"))
+			return path.substring(0,path.length()-1);
+		else
+			return path;
+	}
 
 
-    protected void doGet(HttpServletRequest request,
-                         HttpServletResponse response)
-        throws ServletException, IOException {
+	protected void doGet(HttpServletRequest request,
+			HttpServletResponse response)
+		throws ServletException, IOException {
 
-            doPost(request, response);
-        }
+			doPost(request, response);
+		}
 
 
 
-    protected void doPost(HttpServletRequest request,
-                          HttpServletResponse response)
-        throws ServletException, IOException
-	{
-		StringBuffer requestURL = request.getRequestURL();
-	
-   	//System.out.println(className + " vlkb req from: " + request.getRemoteAddr()
-      //                         + " doGet: " + requestURL.toString());
-
-		PrintWriter writer = response.getWriter();
-		response.setContentType("text/xml");
-	
-		if(-1 != requestURL.lastIndexOf("/capabilities"))
+	protected void doPost(HttpServletRequest request,
+			HttpServletResponse response)
+		throws ServletException, IOException
 		{
-            String fullURL = request.getRequestURL().toString();
-            String baseURL = fullURL.substring(0,requestURL.lastIndexOf("/"));
+			StringBuffer requestURL = request.getRequestURL();
 
-            SetCapsStr(baseURL, "soda");
-			writer.println(capsStr);	
+			//System.out.println(className + " vlkb req from: " + request.getRemoteAddr()
+			//                         + " doGet: " + requestURL.toString());
 
+			PrintWriter writer = response.getWriter();
+			response.setContentType("text/xml");
+
+			if(-1 != requestURL.lastIndexOf("/capabilities"))
+			{
+				String fullURL = request.getRequestURL().toString();
+				String baseURL = fullURL.substring(0,requestURL.lastIndexOf("/"));
+
+				SetCapsStr(baseURL, "soda");
+				writer.println(capsStr);	
+
+			}
+			else if(-1 != requestURL.lastIndexOf("/availability"))
+			{
+				writer.println(availString());	
+			}
+			// error FIXME what to do if none of above given ? e.g. misconfigured web.xml
+
+			writer.close();
+			return;
 		}
-		else if(-1 != requestURL.lastIndexOf("/availability"))
-		{
-			writer.println(availStr);	
-		}
-		// error FIXME what to do if none of above given ? e.g. misconfigured web.xml
-		
-		writer.close();
-		return;
-        }
 }