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