From 0eef59a55de54370273351b078319caf0d4db81a Mon Sep 17 00:00:00 2001
From: Robert Butora <robert.butora@inaf.it>
Date: Tue, 9 Apr 2024 16:11:26 +0300
Subject: [PATCH] includes vlkb-volib and modifies Coord to use volib/VO-params
 (Parser still recognizes VLKB-legacy params)

---
 .../src/main/java/vlkb/common/Coord.java      | 141 ++++++------
 .../src/main/java/vlkb/output/Dataset.java    |  14 +-
 .../main/java/vlkb/output/XmlSerializer.java  |  38 ++--
 .../src/main/java/vlkb/search/DbPSearch.java  | 207 ++++++++++++------
 .../vlkb/webapi/FormatResponseFilter.java     |  24 +-
 .../main/java/vlkb/webapi/SearchServlet.java  |  16 +-
 java-libs/lib/vlkb-volib-0.9-SNAPSHOT.jar     | Bin 0 -> 21965 bytes
 7 files changed, 274 insertions(+), 166 deletions(-)
 create mode 100644 java-libs/lib/vlkb-volib-0.9-SNAPSHOT.jar

diff --git a/data-discovery/src/main/java/vlkb/common/Coord.java b/data-discovery/src/main/java/vlkb/common/Coord.java
index 1dc0caf..3828669 100644
--- a/data-discovery/src/main/java/vlkb/common/Coord.java
+++ b/data-discovery/src/main/java/vlkb/common/Coord.java
@@ -2,13 +2,15 @@ import java.util.logging.Logger;
 import java.util.Map;
 import java.io.PrintWriter;
 
+import vo.parameter.*;
+
 class Coord
 {
    private static final Logger LOGGER = Logger.getLogger(Coord.class.getName());
 
-   String skySystem; // FIXME make enum
+//   String skySystem; // FIXME make enum
 
-   // center
+   /*/ center
    double lon;
    double lat;
 
@@ -21,10 +23,22 @@ class Coord
    public boolean vel_valid;
    String vel_type; // FIXME make enum
    double vel_up;
-   double vel_low;
+   double vel_low;*/
 
-   private boolean API_VLKB_legacy = false;
+// Begin ..................................
+
+//   String specSystem; // FIXME enum VELO_LSRK | WAVE_Barycentric | NONE
+
+   Pos  pos;
+   Band band;
+   Time time;
+   Pol  pol;
 
+// End ....................................
+
+
+   private boolean API_VLKB_legacy = false;
+/*
    Coord(Map<String, String[]> params)
    {
       String api_l = getFirstValue(params, "l");
@@ -39,8 +53,8 @@ class Coord
 
       LOGGER.info("Parse result: " + toQueryString());
    }
-
-
+*/
+/*
    protected void parseSoda(Map<String, String[]> params)
    {
       LOGGER.info("trace");
@@ -72,12 +86,12 @@ class Coord
          if(specSystem == null) specSystem = "2"; // 2=WAVE BARY
          if( (vel != null) && (vel.length >= 2) )
          {
-            this.vel_type = specSystem; // FIXME add sanity checks / use enum
+            this.specSystem = specSystem; // FIXME add sanity checks / use enum
 
             if((vel[0] != null) && (vel[1] != null))
             {
-               this.vel_low = Double.parseDouble(vel[0]);
-               this.vel_up  = Double.parseDouble(vel[1]);
+               this.band.wavelength[0] = Double.parseDouble(vel[0]);
+               this.band.wavelength[1]  = Double.parseDouble(vel[1]);
                this.vel_valid = true;
             }
             else
@@ -92,8 +106,8 @@ class Coord
          throw new IllegalArgumentException("Request with incorrect parameters: " + illArg.getMessage());
       }
    } 
-
-
+*/
+/*
    protected void parseVlkb(Map<String, String[]> params)
    {
       LOGGER.info("trace");
@@ -144,12 +158,12 @@ class Coord
          vel_type = cvtype;
       }
    }
+*/
 
 
-
-   void setSkySystem(String skySystem) { this.skySystem = skySystem; }
-   void setSpecSystem(String velType) { this.vel_type = velType; }
-
+//   void setSkySystem(String skySystem) { this.skySystem = skySystem; }
+//   void setSpecSystem(String velType) { this.vel_type = velType; }
+/*
    void setVelocity(double vel_low, double vel_up, String vel_type)
    {
       this.vel_type  = vel_type;
@@ -157,7 +171,7 @@ class Coord
       this.vel_up    = vel_up;
       this.vel_valid = true;
    }
-
+*/
 
 
 
@@ -166,47 +180,47 @@ class Coord
 
    String toQueryString()
    {
-      if(API_VLKB_legacy)
-         return toVlkbLegacyQueryString();
-      else
+      //if(API_VLKB_legacy)
+      //   return toVlkbLegacyQueryString();
+      //else
          return toVoQueryString();
    }
 
-
+/*
    String toVlkbLegacyQueryString()
    {
       LOGGER.info("trace");
 
       StringBuilder sb = new StringBuilder();
 
-      sb.append("skysystem=" + skySystem);
-      sb.append("&l=" + lon );
-      sb.append("&b=" + lat );
+      sb.append("skysystem=" + pos.skySystem);
+      sb.append("&l=" + pos.circle.lon );
+      sb.append("&b=" + pos.circle.lat );
 
       switch(shape)
       {
-         case "CIRCLE" : sb.append("&r="  + radius   );
+         case "CIRCLE" : sb.append("&r="  + pos.circle.radius   );
                          break;
          case "RECT"   :
-                         sb.append("&dl=" + dlon  );
-                         sb.append("&db=" + dlat  );
+//                         sb.append("&dl=" + dlon  );
+//                         sb.append("&db=" + dlat  );
                          break;
          default:
             LOGGER.info("Coord::toVlkbLegacyQueryString: unknown shape: " + shape);
       }
 
-      if(vel_valid)
+ /*     if(vel_valid)
       {
          sb.append("&vl=" + vel_low);
          sb.append("&vu=" + vel_up );
          sb.append("&specsystem=" + vel_type );
       }
-
+* /
       sb.append("&pubdid="); // FIXME id-value will be added in FormatResponseFilter
 
       return sb.toString();
    }
-
+*/
 
    String toVoQueryString()
    {
@@ -214,35 +228,34 @@ class Coord
 
       StringBuilder sb = new StringBuilder();
 
-      sb.append("skysystem=" + skySystem);
+      sb.append("skysystem=" + pos.system);
 
-      switch(shape)
+      switch(pos.shape)
       {
          case "CIRCLE" :
-            sb.append("&POS=CIRCLE "  + lon + " " + lat + " " + + radius );
+            sb.append("&POS=CIRCLE "  + pos.circle.lon + " " + pos.circle.lat + " " + pos.circle.radius);
             break;
 
          case "RECT" :
-            if(vel_valid)
+            if(band != null)
                sb.append("&POS=RANGE="
-                     + " " + (lon - dlon)  + " " + (lon + dlon)  
-                     + " " + (lat - dlat)  + " " + (lat + dlat)  
-                     + " " + vel_low  + " " + vel_up );
-
-            else       
+                     + " " + pos.range.lon1 + " " + pos.range.lon2
+                     + " " + pos.range.lat1 + " " + pos.range.lat2
+                     + " " + band.wavelength[0]  + " " + band.wavelength[1]);
+            else
                sb.append("&POS=RANGE="
-                     + " " + (lon - dlon)  + " " + (lon + dlon)  
-                     + " " + (lat - dlat)  + " " + (lat + dlat) );
+                     + " " + pos.range.lon1 + " " + pos.range.lon2
+                     + " " + pos.range.lat1 + " " + pos.range.lat2);
             break;
 
          default:
-            LOGGER.info("Coord::toVoQueryString: unknown shape: " + shape);
+            LOGGER.info("Coord::toVoQueryString: unknown shape: " + pos.shape);
       }
 
-      if(vel_valid)
+      if(band != null)
       {
-         sb.append("&BAND= " + vel_low + " " + vel_up);
-         sb.append("&specsystem=" + vel_type );
+         sb.append("&BAND= " + band.wavelength[0] + " " + band.wavelength[1]);
+         sb.append("&specsystem=" + band.system );
       }
 
       sb.append("&ID="); // FIXME id-value will be added in FormatResponseFilter
@@ -258,17 +271,17 @@ class Coord
 
    public String toString()
    {
-      String area = null;
-      switch(shape)
+      String area = "";//null;
+      /*switch(shape)
       {
-         case "CIRCLE" : area = String.valueOf(radius); break;
+         case "CIRCLE" : area = String.valueOf(pos.circle.radius); break;
          case "RECT"   : area = dlon + ", " + dlat; break;
          default: // FIXME leave with exception
                          area = "err: " + shape;
       }
-
+      */
       String resourceSearchArea 
-         = "(P; area) = (" + lon + ", " + lat + "; " + area + ") [deg]";
+         = "(P; area) = (" + pos.circle.lon + ", " + pos.circle.lat + "; " + area + ") [deg]";
 
       return resourceSearchArea;
    }
@@ -277,25 +290,27 @@ class Coord
    void toXML(PrintWriter writer)
    {
       // center is mandatory -> create no Coord if center not valid
-      writer.println("<SkySystem>"+skySystem+"</SkySystem>");
-      writer.println("<l>"+lon+"</l>");
-      writer.println("<b>"+lat+"</b>");
-
-      switch(shape)
+      writer.println("<SkySystem>"+pos.system+"</SkySystem>");
+      switch(pos.shape)
       {
-         case "CIRCLE" :  writer.println("<r>"+String.valueOf(radius)+"</r>"); break;
+         case "CIRCLE" :  
+            writer.println("<l>"+pos.circle.lon+"</l>");
+            writer.println("<b>"+pos.circle.lat+"</b>");
+            writer.println("<r>"+String.valueOf(pos.circle.radius)+"</r>"); break;
          case "RECT"   :
-                          writer.println("<dl>"+String.valueOf(dlon)+"</dl>");
-                          writer.println("<db>"+String.valueOf(dlat)+"</db>");
-                          break;
+            writer.println("<l>"  + (pos.range.lon1 + pos.range.lon2)/2.0 + "</l>");
+            writer.println("<b>"  + (pos.range.lat1 + pos.range.lat2)/2.0 + "</b>");
+            writer.println("<dl>" + (pos.range.lon2 - pos.range.lon1) + "</dl>");
+            writer.println("<db>" + (pos.range.lat2 - pos.range.lat1) + "</db>");
+            break;
          default:
-                          writer.println("<shape> unknown shape: "+ shape +" </shape>");
+            writer.println("<shape> unknown shape: " + pos.shape + " </shape>");
       }
-      if(vel_valid)
+      if(band != null)
       {
-         writer.println("<vl>"   + String.valueOf(vel_low)  +"</vl>");
-         writer.println("<vu>"   + String.valueOf(vel_up)   +"</vu>");
-         writer.println("<vtype>"+ vel_type                 +"</vtype>");
+         writer.println("<vl>"   + String.valueOf(band.wavelength[0])  +"</vl>");
+         writer.println("<vu>"   + String.valueOf(band.wavelength[1])   +"</vu>");
+         writer.println("<vtype>"+ band.system                 +"</vtype>");
       }
    }
 
diff --git a/data-discovery/src/main/java/vlkb/output/Dataset.java b/data-discovery/src/main/java/vlkb/output/Dataset.java
index f94ec44..f2ca86e 100644
--- a/data-discovery/src/main/java/vlkb/output/Dataset.java
+++ b/data-discovery/src/main/java/vlkb/output/Dataset.java
@@ -104,23 +104,23 @@ class Dataset
    }
 
 /*
-   private Vertices mergeVertices(List<Dataset> datasetList, Coord coord)
+//   private Vertices mergeVertices(List<Dataset> datasetList, Coord coord)
    {
       // FIXME for now simply return input defined rectangle vertices
       // which is not correct on edges of survey coverage
 
-      double ll=coord.lon, bb=coord.lat;
+//      double ll=coord.lon, bb=coord.lat;
       double dll=0, dbb=0; // FIXME why compilers errors (not warning): need to be inited ?
 
-      switch(coord.shape)
+//      switch(coord.shape)
       {
          case "CIRCLE" :
-            dll = coord.radius;
-            dbb = coord.radius;
+//            dll = coord.radius;
+//            dbb = coord.radius;
             break;
          case "RECT" :
-            dll = coord.dlon;
-            dbb = coord.dlat;
+ //           dll = coord.dlon;
+ //           dbb = coord.dlat;
             break;
          default:
             // FIXME internnal error
diff --git a/data-discovery/src/main/java/vlkb/output/XmlSerializer.java b/data-discovery/src/main/java/vlkb/output/XmlSerializer.java
index aa039c1..eea0ac4 100644
--- a/data-discovery/src/main/java/vlkb/output/XmlSerializer.java
+++ b/data-discovery/src/main/java/vlkb/output/XmlSerializer.java
@@ -167,25 +167,33 @@ final class XmlSerializer
    private static String serialize(Coord coord)
    {
       StringBuilder xml = new StringBuilder();
-      xml.append("<SkySystem>"+coord.skySystem+"</SkySystem>");
-      xml.append("<l>"+coord.lon+"</l>");
-      xml.append("<b>"+coord.lat+"</b>");
-      switch(coord.shape)
+      if(coord.pos != null)
       {
-         case "CIRCLE" :  xml.append("<r>"+String.valueOf(coord.radius)+"</r>"); break;
-         case "RECT"   :
-                          xml.append("<dl>"+String.valueOf(coord.dlon)+"</dl>");
-                          xml.append("<db>"+String.valueOf(coord.dlat)+"</db>");
-                          break;
-         default:
-                          xml.append("<shape> unknown shape: "+ coord.shape +" </shape>");
+         xml.append("<SkySystem>"+coord.pos.system+"</SkySystem>");
+         switch(coord.pos.shape)
+         {
+            case "CIRCLE" :
+               xml.append("<l>"+coord.pos.circle.lon+"</l>");
+               xml.append("<b>"+coord.pos.circle.lat+"</b>");
+               xml.append("<r>"+String.valueOf(coord.pos.circle.radius)+"</r>"); break;
+            case "RECT"   :
+               xml.append("<l>"+(coord.pos.range.lon1+ coord.pos.range.lon2)/2.0 + "</l>");
+               xml.append("<b>"+(coord.pos.range.lat1+ coord.pos.range.lat2)/2.0 + "</b>");
+               xml.append("<dl>"+String.valueOf(coord.pos.range.lon2 - coord.pos.range.lon1)+"</dl>");
+               xml.append("<db>"+String.valueOf(coord.pos.range.lat2 - coord.pos.range.lat1)+"</db>");
+               break;
+            default:
+               xml.append("<shape> unknown shape: "+ coord.pos.shape +" </shape>");
+         }
       }
-      if(coord.vel_valid)
+
+      if(coord.band != null)
       {
-         xml.append("<vl>"   + String.valueOf(coord.vel_low)  +"</vl>");
-         xml.append("<vu>"   + String.valueOf(coord.vel_up)   +"</vu>");
-         xml.append("<vtype>"+ coord.vel_type                 +"</vtype>");
+         xml.append("<vl>"   + String.valueOf(coord.band.wavelength[0])  +"</vl>");
+         xml.append("<vu>"   + String.valueOf(coord.band.wavelength[1])   +"</vu>");
+         xml.append("<vtype>"+ coord.band.system                 +"</vtype>");
       }
+
       return xml.toString();
    }
 
diff --git a/data-discovery/src/main/java/vlkb/search/DbPSearch.java b/data-discovery/src/main/java/vlkb/search/DbPSearch.java
index 9de69b6..4355231 100644
--- a/data-discovery/src/main/java/vlkb/search/DbPSearch.java
+++ b/data-discovery/src/main/java/vlkb/search/DbPSearch.java
@@ -19,6 +19,7 @@ import java.util.ArrayList;
 
 import java.lang.ClassNotFoundException;
 
+import vo.parameter.*;
 
 public class DbPSearch
 {
@@ -34,56 +35,60 @@ public class DbPSearch
 
    public String[] queryOverlapingPubdid(Coord coord, SubsurveyId subsurveyId)
    {
-	   LOGGER.info("trace");
-
-	   double lon = coord.lon;
-	   double lat = coord.lat;
-	   double radius = coord.radius;
-	   double dlon = coord.dlon;
-	   double dlat = coord.dlat;
-
-	   boolean  vel_valid   = coord.vel_valid;
-	   String   vel_type    = coord.vel_type;
-	   double   vel_low     = coord.vel_low;
-	   double   vel_up      = coord.vel_up;
-
-	   String inputRegion = null;
-
-	   if(coord.shape.equals("CIRCLE"))
-	   {
-		   inputRegion = "scircle '<(" + Double.toString(lon) + "d," + Double.toString(lat) + "d),"
-			   + Double.toString(radius) + "d>'";
-	   }
-	   else if( coord.shape.equals("RECT") )
-	   {
-		   /*Vert vert[] = toVertices(lon, lat, dlon, dlat);
-
-		     inputRegion = "spoly '{" 
-		     + "(" + Double.toString(vert[0].lon) + "d," + Double.toString(vert[0].lat) + "d)," 
-		     + "(" + Double.toString(vert[1].lon) + "d," + Double.toString(vert[1].lat) + "d)," 
-		     + "(" + Double.toString(vert[2].lon) + "d," + Double.toString(vert[2].lat) + "d)," 
-		     + "(" + Double.toString(vert[3].lon) + "d," + Double.toString(vert[3].lat) + "d)" 
-		     + "}'";
-		     */
-
-		   /* 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) )'";
-	   }
-	   else
-	   {
-		   throw new IllegalArgumentException("Coord::shape was: " + coord.shape + " but valid is CIRCLE or RECT");
-	   }
+      LOGGER.info("trace");
 
+//      String skySystem = coord.skySystem;
+      String shape = coord.pos.shape;
+
+      double lon;
+      double lat;
+      double radius;
+      double dlon;
+      double dlat;
+
+      String inputRegion = null;
+
+      if(shape.equals("CIRCLE"))
+      {
+         lon = coord.pos.circle.lon;
+         lat = coord.pos.circle.lat;
+         radius = coord.pos.circle.radius;
+          inputRegion = "scircle '<(" + Double.toString(lon) + "d," + Double.toString(lat) + "d),"
+            + Double.toString(radius) + "d>'";
+      }
+      else if (shape.equals("RECT"))
+      {
+         lon =  (coord.pos.range.lon1 + coord.pos.range.lon2)/2.0;
+         lat =  (coord.pos.range.lat1 + coord.pos.range.lat2)/2.0;
+         dlon = (coord.pos.range.lon2 - coord.pos.range.lon1)/2.0;
+         dlat = (coord.pos.range.lat2 - coord.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) )'";
+      }
+      else
+      { // FIXME how to deal with Polygon ? -> use enclose-RECT -> RANGE
+        /* lon = coord.lon;
+         lat = coord.lat;
+         radius = coord.radius;
+         dlon = coord.dlon;
+         dlat = coord.dlat;*/
+
+         throw new IllegalArgumentException("Coord::shape was: " + shape + " but valid is CIRCLE or RECT");
+      }
+
+      boolean  vel_valid = (coord.band != null) && (coord.band.system != Band.System.NONE);//coord.vel_valid;
+//      String   vel_type    = coord.vel_type;
 
       String theQuery;
-      if(coord.skySystem.equals("GALACTIC"))
+      if(coord.pos.system == Pos.System.GALACTIC)
       {
-         theQuery ="SELECT obs_publisher_did FROM obscore WHERE (" + inputRegion + "  && polygon_region_galactic)";
+         theQuery ="SELECT obs_publisher_did FROM obscore WHERE ("+inputRegion+"  && polygon_region_galactic)";
       }
       else
       {
@@ -92,10 +97,12 @@ public class DbPSearch
 
       if(vel_valid)
       {
-         String vel_no_overlap = "((em_min > " + Double.toString(vel_up) + ") OR (em_max < " + Double.toString(vel_low) + "))";
+         String vel_no_overlap
+            = "((em_min > " + Double.toString(coord.band.wavelength[1])
+            + ") OR (em_max < " + Double.toString(coord.band.wavelength[0]) + "))";
 
          theQuery += " AND ( (NOT " + vel_no_overlap + ") OR (em_min is null) OR (em_max is null))";
-         /* NOTE '... OR (em_min is null)' statement causes to include 2D-continuum datasets if they overlap in sky
+         /* 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 ?
           */
       }
@@ -162,35 +169,32 @@ public class DbPSearch
    public FormatResponseFilter.ObsCore[] queryOutputData(String[] pubdidArr, Coord coord/*, SubsurveyId subsurveyId*/)
    {
       LOGGER.info("");
-      double lon = coord.lon;
-      double lat = coord.lat;
-      double radius = coord.radius;
-      double dlon = coord.dlon;
-      double dlat = coord.dlat;
 
-      boolean  vel_valid   = coord.vel_valid;
-      String   vel_type    = coord.vel_type;
-      double   vel_low     = coord.vel_low;
-      double   vel_up      = coord.vel_up;
+      String skySystem = coord.pos.system.toString();
+      String shape = coord.pos.shape;
+
+      double lon;
+      double lat;
+      double radius;
+      double dlon;
+      double dlat;
 
       String inputRegion = null;
 
-      if(coord.shape.equals("CIRCLE"))
+      if(shape.equals("CIRCLE"))
       {
+         lon = coord.pos.circle.lon;
+         lat = coord.pos.circle.lat;
+         radius = coord.pos.circle.radius;
          inputRegion = "scircle '<(" + Double.toString(lon) + "d," + Double.toString(lat) + "d),"
             + Double.toString(radius) + "d>'";
       }
-      else if( coord.shape.equals("RECT") )
+      else if (shape.equals("RECT"))
       {
-         /*Vert vert[] = toVertices(lon, lat, dlon, dlat);
-
-           inputRegion = "spoly '{" 
-           + "(" + Double.toString(vert[0].lon) + "d," + Double.toString(vert[0].lat) + "d)," 
-           + "(" + Double.toString(vert[1].lon) + "d," + Double.toString(vert[1].lat) + "d)," 
-           + "(" + Double.toString(vert[2].lon) + "d," + Double.toString(vert[2].lat) + "d)," 
-           + "(" + Double.toString(vert[3].lon) + "d," + Double.toString(vert[3].lat) + "d)" 
-           + "}'";
-           */
+         lon =  (coord.pos.range.lon1 + coord.pos.range.lon2)/2.0;
+         lat =  (coord.pos.range.lat1 + coord.pos.range.lat2)/2.0;
+         dlon = (coord.pos.range.lon2 - coord.pos.range.lon1)/2.0;
+         dlat = (coord.pos.range.lat2 - coord.pos.range.lat1)/2.0;
 
          /* South-West and North-East corners of a box */
          String sw_lon = Double.toString(lon - dlon/2.0);
@@ -201,16 +205,77 @@ public class DbPSearch
          inputRegion = "sbox '( ("+ sw_lon + "d, " + sw_lat + "d), (" + ne_lon +"d, " + ne_lat + "d) )'";
       }
       else
-      {
-         throw new IllegalArgumentException("Coord::shape was: " + coord.shape + " but valid is CIRCLE or RECT");
+      { // FIXME how to deal with Polygon ? -> use enclose-RECT -> RANGE
+         /* lon = coord.lon;
+            lat = coord.lat;
+            radius = coord.radius;
+            dlon = coord.dlon;
+            dlat = coord.dlat;*/
+
+         throw new IllegalArgumentException("Coord::shape was: " + shape + " but valid is CIRCLE or RECT");
       }
 
+      boolean  vel_valid = (coord.band != null) && (coord.band.system != Band.System.NONE);//coord.vel_valid;
+     //      String   vel_type    = coord.vel_type;
+    //  double   vel_low   = coord.band.wavelength[0];//vel_low;
+    //  double   vel_up    = coord.band.wavelength[1];//vel_up;
+
+
+      /*
+         double lon = coord.lon;
+         double lat = coord.lat;
+         double radius = coord.radius;
+         double dlon = coord.dlon;
+         double dlat = coord.dlat;
+
+         boolean  vel_valid   = coord.vel_valid;
+         String   vel_type    = coord.vel_type;
+         double   vel_low     = coord.vel_low;
+         double   vel_up      = coord.vel_up;
+
+         String inputRegion = null;
+
+         if(coord.shape.equals("CIRCLE"))
+         {
+         inputRegion = "scircle '<(" + Double.toString(lon) + "d," + Double.toString(lat) + "d),"
+         + Double.toString(radius) + "d>'";
+         }
+         else if( coord.shape.equals("RECT") )
+         {
+      /*Vert vert[] = toVertices(lon, lat, dlon, dlat);
+
+      inputRegion = "spoly '{" 
+      + "(" + Double.toString(vert[0].lon) + "d," + Double.toString(vert[0].lat) + "d)," 
+      + "(" + Double.toString(vert[1].lon) + "d," + Double.toString(vert[1].lat) + "d)," 
+      + "(" + Double.toString(vert[2].lon) + "d," + Double.toString(vert[2].lat) + "d)," 
+      + "(" + Double.toString(vert[3].lon) + "d," + Double.toString(vert[3].lat) + "d)" 
+      + "}'";
+      */
+
+      /* 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) )'";
+         }
+         else
+         {
+         throw new IllegalArgumentException("Coord::shape was: " + coord.shape + " but valid is CIRCLE or RECT");
+         }
+         */
+
+
+
+
+
       String commaSepPubdids  = String.join("\',\'", pubdidArr);
 
 
       //String theQuery ="SELECT dataproduct_type,obs_publisher_did,obs_collection,polygon_region_galactic,access_url,em_min,em_max," 
       String theQuery;
-      if(coord.skySystem.equals("GALACTIC"))
+      if(coord.pos.system == Pos.System.GALACTIC)
       {
          theQuery ="SELECT *," 
             + inputRegion + " <@ polygon_region_galactic AS inputInsideDb, " 
diff --git a/data-discovery/src/main/java/vlkb/webapi/FormatResponseFilter.java b/data-discovery/src/main/java/vlkb/webapi/FormatResponseFilter.java
index c6b4899..2b9e011 100644
--- a/data-discovery/src/main/java/vlkb/webapi/FormatResponseFilter.java
+++ b/data-discovery/src/main/java/vlkb/webapi/FormatResponseFilter.java
@@ -19,6 +19,7 @@ import javax.servlet.http.HttpServletResponseWrapper;
  
 import java.nio.charset.Charset;
 
+import vo.parameter.*;
 
 class FormatResponseWrapper extends HttpServletResponseWrapper
 {
@@ -95,10 +96,17 @@ public class FormatResponseFilter implements Filter
          PrintWriter responseWriter = ((HttpServletResponse)response).getWriter();
 
          Map<String, String[]> params = request.getParameterMap();
-         Coord       coord            = new Coord(params);
+//         Coord       coord            = new Coord(params);
          SubsurveyId subsurveyId      = new SubsurveyId(params);
          // FIXME add invalid param excpetions -> params already parsed in servlet
 
+         Coord coord = new Coord();
+         coord.pos  = Pos.parsePos(params);
+         coord.pos.setSystem(Pos.System.ICRS);
+         coord.band = Band.parseBand(params);
+         coord.time = Time.parseTime(params);
+         coord.pol  = Pol.parsePol(params);
+
          ObsCore[] obsCoreArr = queryObsCore(pubdidArr,
                coord); // VLKB: calc overlap-code for sky
 
@@ -254,7 +262,7 @@ public class FormatResponseFilter implements Filter
 
          dataset.subsurvey_id   = obsCore.obs_collection;
          dataset.overlapCodeSky = convertToOverlapCodeSky(obsCore.inputInsideDb, obsCore.dbInsideInput);
-         dataset.overlapCodeVel = convertToOverlapCodeVel(coord, obsCore.em_valid, obsCore.em_min, obsCore.em_max);
+         dataset.overlapCodeVel = convertToOverlapCodeVel(coord,obsCore.em_valid,obsCore.em_min,obsCore.em_max);
          dataset.overlapCode    = convertToOverlapCode(dataset.overlapCodeSky, dataset.overlapCodeVel);
          dataset.dataType       = obsCore.dataproduct_type;
          dataset.publisherDid   = obsCore.obs_publisher_did;
@@ -283,18 +291,18 @@ public class FormatResponseFilter implements Filter
 
    private int convertToOverlapCodeVel(Coord coord, boolean v_valid, double v_min, double v_max)
    {
-      if(coord.vel_valid && v_valid)
+      if((coord.band != null) && v_valid)
       {
-         if(coord.vel_type.equals("1"))
+         if(coord.band.system == Band.System.VELO_LSRK)
          {
             // FIXME assert coord: vel_min <= vel_max
             // FIXME assert cube:  v_min   <= v_max
 
-            boolean dbInInp = (coord.vel_low <= v_min) && (v_min <= coord.vel_up)
-               && (coord.vel_low <= v_max) && (v_max <= coord.vel_up);
+            boolean dbInInp = (coord.band.wavelength[0] <= v_min) && (v_min <= coord.band.wavelength[1])
+               && (coord.band.wavelength[0] <= v_max) && (v_max <= coord.band.wavelength[1]);
 
-            boolean inpInDb = (v_min <= coord.vel_low) && (coord.vel_low <= v_max)
-               && (v_min <= coord.vel_up ) && (coord.vel_up  <= v_max);
+            boolean inpInDb = (v_min <= coord.band.wavelength[0]) && (coord.band.wavelength[0] <= v_max)
+               && (v_min <= coord.band.wavelength[1] ) && (coord.band.wavelength[1] <= v_max);
 
             return convertToOverlapCodeSky(inpInDb, dbInInp);
 
diff --git a/data-discovery/src/main/java/vlkb/webapi/SearchServlet.java b/data-discovery/src/main/java/vlkb/webapi/SearchServlet.java
index f66ca87..8bda8da 100644
--- a/data-discovery/src/main/java/vlkb/webapi/SearchServlet.java
+++ b/data-discovery/src/main/java/vlkb/webapi/SearchServlet.java
@@ -17,6 +17,7 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import vo.parameter.*;
 
 public class SearchServlet extends javax.servlet.http.HttpServlet
 {
@@ -56,8 +57,19 @@ public class SearchServlet extends javax.servlet.http.HttpServlet
       try
       {
          Map<String, String[]> params = request.getParameterMap();
-         Coord                  coord = new Coord(params);
-         SubsurveyId      subsurveyId = new SubsurveyId(params);
+
+         // VLKB-legacy init
+         //Coord       coord = new Coord(params);
+         SubsurveyId subsurveyId = new SubsurveyId(params);
+
+// FIXME check this!            String id   = SingleStringParam.parseSingleStringParam(params, "ID");
+
+         // new based on vlkb-volib
+         Coord coord = new Coord();
+         coord.pos  = Pos.parsePos(params);
+         coord.band = Band.parseBand(params);
+         coord.time = Time.parseTime(params);
+         coord.pol  = Pol.parsePol(params);
 
          /* query Obscore table */
 
diff --git a/java-libs/lib/vlkb-volib-0.9-SNAPSHOT.jar b/java-libs/lib/vlkb-volib-0.9-SNAPSHOT.jar
new file mode 100644
index 0000000000000000000000000000000000000000..94a329a806bb4c43df0d25c3d36896d6b28ccce4
GIT binary patch
literal 21965
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
HAH)Lyis+9O

literal 0
HcmV?d00001

-- 
GitLab