From cda6732070cd3c6a66900ff6dec200f133593daa Mon Sep 17 00:00:00 2001 From: Robert Butora <robert.butora@inaf.it> Date: Thu, 11 Apr 2024 14:04:43 +0300 Subject: [PATCH] links to vlkb-volib with support for coord-system params (POSSYS BANDSYS TIMESYS) and their defaults from settings-file --- .../servlet/src/main/java/common/Coord.java | 1 + .../src/main/java/common/vo/soda/Band.java | 35 --- .../src/main/java/common/vo/soda/Circle.java | 61 ----- .../vo/soda/MultiValuedParamNotSupported.java | 8 - .../src/main/java/common/vo/soda/Parser.java | 90 ------- .../src/main/java/common/vo/soda/Pol.java | 40 --- .../src/main/java/common/vo/soda/Polygon.java | 89 ------- .../src/main/java/common/vo/soda/Pos.java | 93 ------- .../src/main/java/common/vo/soda/Range.java | 98 -------- .../main/java/common/vo/soda/SodaParam.java | 11 - .../main/java/common/vo/soda/SodaParser.java | 232 ------------------ .../src/main/java/common/vo/soda/Time.java | 27 -- .../src/main/java/datasets/Cutout.java | 2 + .../src/main/java/datasets/CutoutImpl.java | 2 + .../java/datasets/json-rpc/JsonEncoder.java | 1 + .../datasets/json-rpc/JsonEncoderMerge.java | 1 + .../src/main/java/webapi/MonitorFilter.java | 2 + .../src/main/java/webapi/ServletCutout.java | 29 ++- .../src/main/java/webapi/ServletMerge.java | 19 +- java-libs/lib/vlkb-volib-0.9-SNAPSHOT.jar | Bin 0 -> 23078 bytes 20 files changed, 46 insertions(+), 795 deletions(-) delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/Band.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/Circle.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/MultiValuedParamNotSupported.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/Parser.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/Pol.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/Polygon.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/Pos.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/Range.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/SodaParam.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/SodaParser.java delete mode 100644 data-access/servlet/src/main/java/common/vo/soda/Time.java create mode 100644 java-libs/lib/vlkb-volib-0.9-SNAPSHOT.jar diff --git a/data-access/servlet/src/main/java/common/Coord.java b/data-access/servlet/src/main/java/common/Coord.java index bd82e90..91983b1 100644 --- a/data-access/servlet/src/main/java/common/Coord.java +++ b/data-access/servlet/src/main/java/common/Coord.java @@ -1,5 +1,6 @@ +import vo.parameter.*; class Coord { diff --git a/data-access/servlet/src/main/java/common/vo/soda/Band.java b/data-access/servlet/src/main/java/common/vo/soda/Band.java deleted file mode 100644 index 8963025..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/Band.java +++ /dev/null @@ -1,35 +0,0 @@ - -/* REC-SODA BAND parameter defines the interval(s) to be extracted -from the data using a floating point interval (xtype="interval") as defined -in DALI. */ - - -class Band -{ - public enum System {WAVE_Barycentric, VELO_LSRK, NONE}; - - System system; - double wavelength[]; - - - public Band(String str) - { - this.wavelength = Parser.getDaliIntervalPositiveValues(str, "BAND"); - } - - public Band(double low, double up) - { - wavelength = new double[2]; - wavelength[0] = low; - wavelength[1] = up; - } - - public void setSystem(Band.System system) { this.system = system; } - - public String toString() - { - return "BAND " + wavelength[0] + " " + wavelength[1]; - } - -} - diff --git a/data-access/servlet/src/main/java/common/vo/soda/Circle.java b/data-access/servlet/src/main/java/common/vo/soda/Circle.java deleted file mode 100644 index dd5b406..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/Circle.java +++ /dev/null @@ -1,61 +0,0 @@ - -/* REC-SODA: UCD describing the CIRCLE parameter is pos.outline;obs.*/ -/* REC-DALI Sect 3.3.6 Circle: definition */ - - -class Circle -{ - - double lon; - double lat; - double radius; - - public Circle(String value) - { - parseCircle(value); - } - - private void parseCircle(String str) - { - String[] arr = str.strip().split(" +"); - - if(arr == null) - throw new IllegalArgumentException("CIRCLE : no value, or value contains no space"); - else - { - final int len = 3; - if(arr.length != len) - throw new IllegalArgumentException( - "CIRCLE : must have " + len + " elements delimited by space, but found " + arr.length); - else - { - - double dbl = Double.parseDouble(arr[0]); - if ((dbl < 0) || (dbl > 360)) - throw new IllegalArgumentException("CIRCLE : first number must be in range [0,360] but found " + dbl); - else - this.lon = dbl; - - dbl = Double.parseDouble(arr[1]); - if ((dbl < -90) || (dbl > 90)) - throw new IllegalArgumentException("CIRCLE : second number must be in range [-90,90] but found " + dbl); - else - this.lat = dbl; - - dbl = Double.parseDouble(arr[2]); - if ((dbl <= 0) || (dbl > 180)) - throw new IllegalArgumentException("CIRCLE : third number must be in range (0,180] but found " + dbl); - else - this.radius = dbl; - - } - } - } - - public String toString() - { - return "CIRCLE " + Double.valueOf(lon) + " " + Double.valueOf(lat) + " " + Double.valueOf(radius); - } - -} - diff --git a/data-access/servlet/src/main/java/common/vo/soda/MultiValuedParamNotSupported.java b/data-access/servlet/src/main/java/common/vo/soda/MultiValuedParamNotSupported.java deleted file mode 100644 index 2b3dd3c..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/MultiValuedParamNotSupported.java +++ /dev/null @@ -1,8 +0,0 @@ - - - -public class MultiValuedParamNotSupported extends IllegalArgumentException { - public MultiValuedParamNotSupported(String errorMessage){//, Throwable err) { - super(errorMessage);//, err); - } -} diff --git a/data-access/servlet/src/main/java/common/vo/soda/Parser.java b/data-access/servlet/src/main/java/common/vo/soda/Parser.java deleted file mode 100644 index 66d0d67..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/Parser.java +++ /dev/null @@ -1,90 +0,0 @@ - -import java.util.Map; - - -class Parser -{ - - public static String getFirstString(Map<String, String[]> params, String key) - { - String[] values = params.get(key); - if (values == null) return null; - - if (values.length < 1) - throw new IllegalArgumentException(key + " has no valid value"); - else - return values[0];// FIXME if values[0] is null -> canot distinguish from key not found - } - - - public static String[] getFirstStringArray(Map<String, String[]> params, String key, String separator, int arrayLength) - { - String array = getFirstString(params, key); - if (array == null) return null; - - String[] stringArray = array.split(separator); - - if(stringArray.length != arrayLength) - throw new IllegalArgumentException( - key + " parameter has incorrect number of elements (" - + stringArray.length + " vs " + arrayLength + ") or incorrect separator used"); - - return stringArray; - } - - - public static double[] getDaliIntervalPositiveValues(String value, String errorMsgPrefix) - { - String[] arr = value.strip().split(" +"); - - double[] dblArr = new double[2]; - - if(arr == null) - throw new IllegalArgumentException(errorMsgPrefix + " : no value, or value contains no space"); - else - { - final int len = 2; - if(arr.length != len) - throw new IllegalArgumentException( - errorMsgPrefix + " : must have " + len + " space-delimited elements, but found " + arr.length); - else - { - - - String val = arr[0]; - if(val.equals("Inf") || val.equals("+Inf")) - { - dblArr[0] = Double.POSITIVE_INFINITY; - } - else - { - double dbl = Double.parseDouble(val); - if (dbl < 0) - throw new IllegalArgumentException(errorMsgPrefix + " : values must be positive, but first value was " + dbl); - else - dblArr[0] = dbl; - } - - - val = arr[1]; - if(val.equals("-Inf")) - { - // dblArr[1] = Double.NEGATIVE_INFINITY; - throw new IllegalArgumentException(errorMsgPrefix + " : values must be positive, but second value was " + val); - } - else - { - double dbl = Double.parseDouble(val); - if (dbl < 0) - throw new IllegalArgumentException(errorMsgPrefix + " : values must be positive, but second value was " + dbl); - else - dblArr[1] = dbl; - } - } - } - - return dblArr; - } - - -} diff --git a/data-access/servlet/src/main/java/common/vo/soda/Pol.java b/data-access/servlet/src/main/java/common/vo/soda/Pol.java deleted file mode 100644 index 189fe7f..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/Pol.java +++ /dev/null @@ -1,40 +0,0 @@ - -/* REC ObsCore B.6.5.1. List of polarization states (pol_states) -... polarization labels inspired from the FITS specification. See Table 7 in FITS WCS Paper 1 -(Greisen et Calabretta 2002) . Labels are combined using symbols from the - -{I Q U V RR LL RL LR XX YY XY YX POLI POLA} set - -and separated by a / character. A leading / character must start the list and -a trailing / character must end it. It should be ordered following the above list, compatible with -the FITS list table for polarization definition. -*/ - -// NOTE POLI POLA is not in FITS STOKES other match and are defined in FITS as: I=1...V=4 and RR=-1...YX=-8 - -import java.util.Arrays; -import java.util.Set; -import java.util.HashSet; - -class Pol -{ - final static Set<String> POL_STATES - = new HashSet<>(Arrays.asList("I", "Q", "U", "V", "RR", "LL", "RL", "LR", "XX", "YY", "XY", "YX")); - - String[] states; - - Pol(String[] values) - { - for(String pol : values) - if(!POL_STATES.contains(pol)) - throw new IllegalArgumentException("POL value is " + pol +" but must be one of " + String.join(" ", POL_STATES)); - - this.states = values; - } - - - public String toString() - { - return "POL " + String.join(" ", states); - } -} diff --git a/data-access/servlet/src/main/java/common/vo/soda/Polygon.java b/data-access/servlet/src/main/java/common/vo/soda/Polygon.java deleted file mode 100644 index 745b581..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/Polygon.java +++ /dev/null @@ -1,89 +0,0 @@ -/* REC DALI 3.3.7 Polygon -In spherical coordinates, all longitude values must fall within [0,360] and -all latitude values within [-90,90]. Vertices must be ordered such that the -polygon winding direction is counter-clockwise (when viewed from the origin -toward the sky) as described in (Rots, 2007). - - -Rots, A. (2007), 'Space-time coordinate metadata for the virtual observa- -tory', IVOA Recommendation. -http://www.ivoa.net/documents/latest/STC.html : - -4.5.1.4 Polygon -.... - In order to avoid ambiguities in direction, vertices need to be less -than 180 deg apart in both coordinates. Great circles or small circles spanning 180 deg -require specification of an extra intermediate vertex. -.... -The boundaries are considered part of the region. The inside of the region is -defined as that part of coordinate space that is encircled by the polygon in a -counter-clockwise sense. -... -*/ - -class Polygon -{ - - double[] lon; - double[] lat; - - public Polygon(String value) - { - parsePolygon(value); - } - - private void parsePolygon(String str) - { - String[] arr = str.strip().split(" +"); - - if(arr == null) - throw new IllegalArgumentException("POLYGON : no value, or value contains no space"); - else - { - final int minLen = 3*2; // REC SODA : at least 3 (lon,lat) points - if(arr.length < minLen) - throw new IllegalArgumentException( - "POLYGON : must have at least " + minLen + " elements delimited by space, but found " + arr.length); - else - { - boolean isEven = ((arr.length % 2) == 0); - if(!isEven) - throw new IllegalArgumentException("POLYGON must have even number of values, but has " + arr.length); - - lon = new double[arr.length/2]; - lat = new double[arr.length/2]; - - for(int ii=0; ii<(arr.length-1); ii+=2) - { - - double dbl = Double.parseDouble(arr[ii]); - if ((dbl < 0) || (dbl > 360)) - throw new IllegalArgumentException("POLYGON : first number must be in range [0,360] but found " + dbl); - else - this.lon[ii/2] = dbl; - - dbl = Double.parseDouble(arr[ii+1]); - if ((dbl < -90) || (dbl > 90)) - throw new IllegalArgumentException("POLYGON : second number must be in range [-90,90] but found " + dbl); - else - this.lat[ii/2] = dbl; - - } - - } - } - } - - - public String toString() - { - StringBuilder sb = new StringBuilder("POLYGON"); - int ii = 0; - for(ii = 0; ii<lon.length; ii++) - sb.append(" (" + String.valueOf(lon[ii]) + ", " + String.valueOf(lat[ii]) + ")"); - - return sb.toString(); - } - -} - diff --git a/data-access/servlet/src/main/java/common/vo/soda/Pos.java b/data-access/servlet/src/main/java/common/vo/soda/Pos.java deleted file mode 100644 index 55aaf52..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/Pos.java +++ /dev/null @@ -1,93 +0,0 @@ - -import java.util.logging.Logger; - - -class Pos -{ - protected static final Logger LOGGER = Logger.getLogger("Pos"); - - enum System {NONE, ICRS, GALACTIC}; - - String shape; - String value; - - System system; - - Circle circle; - Range range; - Polygon polygon; - - - public Pos(Circle circle) {this.shape = "CIRCLE"; this.circle = circle;}; - public Pos(Range range) {this.shape = "RANGE"; this.range = range;}; - public Pos(Polygon polygon) {this.shape = "POLYGON"; this.polygon = polygon;}; - - - public Pos(String value) - { - LOGGER.info("trace: " + value); - parsePos(value); - } - - - private void parsePos(String str) - { - String strArr[] = str.strip().split(" +", 2); - - if(strArr.length > 1) - { - this.shape = strArr[0].strip(); - this.value = strArr[1].strip(); - } - else - { - throw new IllegalArgumentException("POS value must have more then one space-separated elements but had " - + strArr.length + " elements)"); - } - - if(this.shape.equals("CIRCLE")) - { - this.circle = new Circle(this.value); - } - else if(this.shape.equals("RANGE")) - { - this.range = new Range(this.value); - } - else if(this.shape.equals("POLYGON")) - { - this.polygon = new Polygon(this.value); - } - else - { - throw new IllegalArgumentException("Valid POS shape is CIRCLE or RANGE or POLYGON but was: " + this.shape); - } - - } - - public void setSystem(Pos.System system) { this.system = system; } - - public String toString() - { - String shapeStr; - if(this.shape.equals("CIRCLE")) - { - shapeStr = this.circle.toString(); - } - else if(this.shape.equals("RANGE")) - { - shapeStr = this.range.toString(); - } - else if(this.shape.equals("POLYGON")) - { - shapeStr = this.polygon.toString(); - } - else - { - throw new IllegalArgumentException("Valid POS shape is CIRCLE or RANGE or POLYGON but was: " + this.shape); - } - - return "POS: " + shapeStr; - } - -} - diff --git a/data-access/servlet/src/main/java/common/vo/soda/Range.java b/data-access/servlet/src/main/java/common/vo/soda/Range.java deleted file mode 100644 index 8f74921..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/Range.java +++ /dev/null @@ -1,98 +0,0 @@ - -import java.util.Arrays; - -class Range -{ - - double lon1, lon2; - double lat1, lat2; - - public Range(String value) - { - parseRange(value); - } - - public Range(double lonCenter, double latCenter, double lonWidth, double latWidth) - { - lon1 = lonCenter - lonWidth/2.0; - lon2 = lonCenter + lonWidth/2.0; - lat1 = latCenter - latWidth/2.0; - lat2 = latCenter + latWidth/2.0; - } - - public Range(Circle circle) - { - lon1 = circle.lon - circle.radius; - lon2 = circle.lon + circle.radius; - - lat1 = circle.lat - circle.radius; - lat2 = circle.lat + circle.radius; - } - - - - public Range(Polygon polygon) - { - lon1 = Arrays.stream(polygon.lon).min().getAsDouble(); - lon2 = Arrays.stream(polygon.lon).max().getAsDouble(); - - lat1 = Arrays.stream(polygon.lat).min().getAsDouble(); - lat2 = Arrays.stream(polygon.lat).max().getAsDouble(); - } - - - - private void parseRange(String str) - { - String[] arr = str.strip().split(" +"); - - if(arr == null) - throw new IllegalArgumentException("RANGE : no value, or value contains no space"); - else - { - final int len = 4; - if(arr.length != len) - throw new IllegalArgumentException( - "RANGE : must have " + len + " elements delimited by space, but found " + arr.length); - else - { - - double dbl = Double.parseDouble(arr[0]); - if ((dbl < 0) || (dbl > 360)) - throw new IllegalArgumentException("RANGE : first number must be in range [0,360] but found " + dbl); - else - this.lon1 = dbl; - - dbl = Double.parseDouble(arr[1]); - if ((dbl < 0) || (dbl > 360)) - throw new IllegalArgumentException("RANGE : first number must be in range [0,360] but found " + dbl); - else - this.lon2 = dbl; - - - dbl = Double.parseDouble(arr[2]); - if ((dbl < -90) || (dbl > 90)) - throw new IllegalArgumentException("RANGE : second number must be in range [-90,90] but found " + dbl); - else - this.lat1 = dbl; - - dbl = Double.parseDouble(arr[3]); - if ((dbl < -90) || (dbl > 90)) - throw new IllegalArgumentException("RANGE : second number must be in range [-90,90] but found " + dbl); - else - this.lat2 = dbl; - - } - } - } - - - public String toString() - { - String str = "RANGE " + Double.valueOf(lon1) + " " + Double.valueOf(lon2) - + " " + Double.valueOf(lat1) + " " + Double.valueOf(lat2); - return str; - } - -} - diff --git a/data-access/servlet/src/main/java/common/vo/soda/SodaParam.java b/data-access/servlet/src/main/java/common/vo/soda/SodaParam.java deleted file mode 100644 index 6abd844..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/SodaParam.java +++ /dev/null @@ -1,11 +0,0 @@ - - -/* FIXME contains also VLKB-legacy params which will be removed once clients can do SODA */ - -public enum SodaParam -{ -ID, POS, CIRCLE, POLYGON, BAND, TIME, POL, -skysystem, specsystem, -pubdid,l,b,r,dl,db,vtype,vl,vu,nullvals -} - diff --git a/data-access/servlet/src/main/java/common/vo/soda/SodaParser.java b/data-access/servlet/src/main/java/common/vo/soda/SodaParser.java deleted file mode 100644 index 76378cc..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/SodaParser.java +++ /dev/null @@ -1,232 +0,0 @@ - -import java.util.logging.Logger; - -import java.util.Map; -import java.util.HashMap; -import java.util.Arrays; - - - - -class SodaParser -{ - protected static final Logger LOGGER = Logger.getLogger(ServletCutout.class.getName()); - - //public class SodaParamMap extends HashMap<SodaParam, String[]> {}; - Map<SodaParam, String[]> params; - - - Pos pos; - Band band; - Time time; - Pol pol; - - - public SodaParser(Map<SodaParam, String[]> params) - { - LOGGER.info("trace - there are " + params.size() + " params"); - this.params = params; - } - - - - /* return null if value not present or the value if present exactly once - * else throw MultiplicityNotSupoorted SODA_error - */ - private String soda_getSingleValue(SodaParam name) - { - LOGGER.info("trace"); - - String[] valArr = params.get(name); - - if(valArr == null) - return null; - else - if(valArr.length == 0) - return null; - else if(valArr.length == 1) - { - LOGGER.info("ParamFound " + name.toString() + " : " + valArr[0]); - return valArr[0]; - } - else - throw new MultiValuedParamNotSupported(name + " was found " + valArr.length + " times"); - } - -/* - public String sodaReq_getResponseFormat(SodaParamMap params, String defaultResponseFormat) - { - String respFormat = soda_getSingleValue(params, "RESPONSEFORMAT"); - return ((respFormat == null) ? defaultResponseFormat : respFormat); - } -*/ - - public boolean sodaReq_hasSodaId() - { - String id = soda_getSingleValue(SodaParam.ID); - return (id != null); - } - - - public String sodaReq_getId() - { - String pubdid = soda_getSingleValue(SodaParam.ID); - if(pubdid == null) - throw new IllegalArgumentException("ID is missing, but is mandatory"); - else - return pubdid; - } - - public Pos sodaReq_getPosCirclePolygon() - { - String valuePos = soda_getSingleValue(SodaParam.POS); - String valueCircle = soda_getSingleValue(SodaParam.CIRCLE); - String valuePolygon = soda_getSingleValue(SodaParam.POLYGON); - - Pos pos = null; - - if( (valuePos != null) && (valueCircle == null) && (valuePolygon == null) ) - { - pos = new Pos(valuePos); - LOGGER.info(pos.toString()); - } - else if( (valuePos == null) && (valueCircle != null) && (valuePolygon == null) ) - { - Circle circle = new Circle(valueCircle); - LOGGER.info(circle.toString()); - pos = new Pos(circle); - } - else if( (valuePos == null) && (valueCircle == null) && (valuePolygon != null) ) - { - Polygon polygon = new Polygon(valuePolygon); - LOGGER.info(polygon.toString()); - pos = new Pos(polygon); - } - else - { - throw new IllegalArgumentException("Exactly one of POS | CIRCLE | POLYGON must be given."); - } - - return pos; - } - - - public Band sodaReq_getBand() - { - String value = soda_getSingleValue(SodaParam.BAND); - if(value == null) - return null; - else - return new Band(value); - } - - - public Time sodaReq_getTime() - { - String value = soda_getSingleValue(SodaParam.TIME); - if(value == null) - return null; - else - return new Time(value); - } - - public Pol sodaReq_getPol() - { - String[] valArr = params.get(SodaParam.POL); - - - if(valArr == null) - return null; - else if(valArr.length < 1) - return null; - else - { - LOGGER.info("ParamFound " + SodaParam.POL.toString() + " : " + Arrays.toString(valArr)); - return new Pol(valArr); - } - } - - - /* VLKB */ - - - public String vlkbReq_getPubdid() - { - String pubdid = soda_getSingleValue(SodaParam.pubdid); - if(pubdid == null) - throw new IllegalArgumentException(SodaParam.pubdid.toString() + " is missing, but is mandatory"); - else - return pubdid; - } - - public Pos vlkbReq_getCircleRect() - { - Pos pos = null; - - String l_value = soda_getSingleValue(SodaParam.l); - String b_value = soda_getSingleValue(SodaParam.b); - - if( (l_value != null) && (b_value != null ) ) - { - String r_value = soda_getSingleValue(SodaParam.r); - if(r_value != null) - { - Circle circle = new Circle(l_value + " " + b_value + " " + r_value); - pos = new Pos(circle); - } - else - { - String dl_value = soda_getSingleValue(SodaParam.dl); - String db_value = soda_getSingleValue(SodaParam.db); - if((dl_value != null) && (db_value != null)) - { - double l = Double.parseDouble(l_value); - double b = Double.parseDouble(b_value); - double dl = Double.parseDouble(dl_value); - double db = Double.parseDouble(db_value); - - Range range = new Range(l, b, dl, db); - pos = new Pos(range); - } - else - { - throw new IllegalArgumentException("one of 'r' or '(dl,db)' pair must be provided to designate sky area"); - } - } - } - else - { - throw new IllegalArgumentException("VLKB sky position center (l,b) is missing however is mandatory"); - } - - return pos; - } - - - public Band vlkbReq_getVelocity() - { - Band band = null; - - String cvlow = soda_getSingleValue(SodaParam.vl); - String cvup = soda_getSingleValue(SodaParam.vu); - //String cvtype = soda_getSingleValue(params, "vtype"); // "1"=VELO_LSRK or "2"=WAVE_Barycentirc - - boolean vel_valid = (cvlow != null) && (cvup != null); - - if(vel_valid) - { - double vel_low = Double.parseDouble(cvlow); - double vel_up = Double.parseDouble(cvup); - band = new Band(vel_low, vel_up); - } - - return band; - } - - public boolean vlkbReq_getNullValues() - { - return (null != soda_getSingleValue(SodaParam.nullvals)); - } - -} - diff --git a/data-access/servlet/src/main/java/common/vo/soda/Time.java b/data-access/servlet/src/main/java/common/vo/soda/Time.java deleted file mode 100644 index 69afc8c..0000000 --- a/data-access/servlet/src/main/java/common/vo/soda/Time.java +++ /dev/null @@ -1,27 +0,0 @@ - -/* SODA 3.3.5 TIME -...numeric values interpreted as Modified Julian Date(s) in UTC. -As in DALI, open intervals use -Inf or +Inf as one limit. -*/ - -class Time -{ - enum System {MJD_UTC, NONE}; - - System system; - double mjdUtc[]; - - public Time(String value) - { - mjdUtc = Parser.getDaliIntervalPositiveValues(value, "TIME"); - } - - public void setSystem(Time.System system) { this.system = system; } - - public String toString() - { - return "TIME " + mjdUtc[0] + " " + mjdUtc[1]; - } - -} - diff --git a/data-access/servlet/src/main/java/datasets/Cutout.java b/data-access/servlet/src/main/java/datasets/Cutout.java index 096312f..33d5080 100644 --- a/data-access/servlet/src/main/java/datasets/Cutout.java +++ b/data-access/servlet/src/main/java/datasets/Cutout.java @@ -9,6 +9,8 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.time.Instant;//Timestamp in cut-filename +import vo.parameter.*; + public interface Cutout { diff --git a/data-access/servlet/src/main/java/datasets/CutoutImpl.java b/data-access/servlet/src/main/java/datasets/CutoutImpl.java index b584ed6..cca2b70 100644 --- a/data-access/servlet/src/main/java/datasets/CutoutImpl.java +++ b/data-access/servlet/src/main/java/datasets/CutoutImpl.java @@ -26,6 +26,8 @@ import java.nio.file.Paths; import java.time.*;// Timestamp in cut-filename import java.io.ByteArrayOutputStream; // for SODA direct streaming doSubimgStream +import vo.parameter.*; + class CutoutImpl implements Cutout { static final Logger LOGGER = Logger.getLogger(DatasetsImpl.class.getName()); diff --git a/data-access/servlet/src/main/java/datasets/json-rpc/JsonEncoder.java b/data-access/servlet/src/main/java/datasets/json-rpc/JsonEncoder.java index c76e020..f81bccd 100644 --- a/data-access/servlet/src/main/java/datasets/json-rpc/JsonEncoder.java +++ b/data-access/servlet/src/main/java/datasets/json-rpc/JsonEncoder.java @@ -8,6 +8,7 @@ import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import vo.parameter.*; public class JsonEncoder { diff --git a/data-access/servlet/src/main/java/datasets/json-rpc/JsonEncoderMerge.java b/data-access/servlet/src/main/java/datasets/json-rpc/JsonEncoderMerge.java index 9615118..32e6ba2 100644 --- a/data-access/servlet/src/main/java/datasets/json-rpc/JsonEncoderMerge.java +++ b/data-access/servlet/src/main/java/datasets/json-rpc/JsonEncoderMerge.java @@ -6,6 +6,7 @@ import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import vo.parameter.*; public class JsonEncoderMerge { diff --git a/data-access/servlet/src/main/java/webapi/MonitorFilter.java b/data-access/servlet/src/main/java/webapi/MonitorFilter.java index c20a1da..4e98544 100644 --- a/data-access/servlet/src/main/java/webapi/MonitorFilter.java +++ b/data-access/servlet/src/main/java/webapi/MonitorFilter.java @@ -21,6 +21,8 @@ import javax.servlet.http.Part; import javax.servlet.http.HttpServletRequestWrapper; import java.security.Principal; +import vo.parameter.*; + @javax.servlet.annotation.MultipartConfig public class MonitorFilter implements Filter { diff --git a/data-access/servlet/src/main/java/webapi/ServletCutout.java b/data-access/servlet/src/main/java/webapi/ServletCutout.java index 42a2e50..bdeb704 100644 --- a/data-access/servlet/src/main/java/webapi/ServletCutout.java +++ b/data-access/servlet/src/main/java/webapi/ServletCutout.java @@ -41,6 +41,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import vo.parameter.*; public class ServletCutout extends javax.servlet.http.HttpServlet { @@ -104,7 +105,7 @@ public class ServletCutout extends javax.servlet.http.HttpServlet Resolver rsl = new ResolverFromId(); rsl.resolve(id); - if(pos != null) pos.setSystem(Pos.System.valueOf(DEFAULT_SKY_SYSTEM)); + // if(pos != null) pos.setSystem(Pos.System.valueOf(DEFAULT_SKY_SYSTEM)); if(band != null) band.setSystem(Band.System.valueOf(DEFAULT_SPEC_SYSTEM)); if(time != null) time.setSystem(Time.System.valueOf(DEFAULT_TIME_SYSTEM)); @@ -261,18 +262,18 @@ public class ServletCutout extends javax.servlet.http.HttpServlet writer.close(); } - - private Map<SodaParam, String[]> collectSodaParams(HttpServletRequest req) +/* + private Map<VoParam, String[]> collectSodaParams(HttpServletRequest req) { - Map<SodaParam, String[]> params = new HashMap<SodaParam, String[]>(); - for(SodaParam paramToken : SodaParam.values()) + Map<VoParam, String[]> params = new HashMap<VoParam, String[]>(); + for(VoParam paramToken : VoParam.values()) { String[] paramValue = req.getParameterValues(paramToken.toString()); params.put(paramToken, paramValue); } return params; } - +*/ protected void convertHttpToSoda(HttpServletRequest request, HttpServletResponse response) @@ -283,7 +284,7 @@ public class ServletCutout extends javax.servlet.http.HttpServlet try { - Map<SodaParam, String[]> params = collectSodaParams(request); + /*/Map<SodaParam, String[]> params = collectSodaParams(request); SodaParser parser = new SodaParser(params); String id = null; @@ -306,6 +307,20 @@ public class ServletCutout extends javax.servlet.http.HttpServlet pos = parser.vlkbReq_getCircleRect(); band = parser.vlkbReq_getVelocity(); } +*/ + Map<String, String[]> params = request.getParameterMap(); + + String id = SingleStringParam.parseSingleStringParam(params, "ID"); + Pos pos = Pos.parsePos(params, DEFAULT_SKY_SYSTEM); + Band band = Band.parseBand(params, DEFAULT_SPEC_SYSTEM); + Time time = Time.parseTime(params, DEFAULT_TIME_SYSTEM); + Pol pol = Pol.parsePol(params); + + + + + + String respFormat = sodaReq_getResponseFormat(request, DEFAULT_RESPONSEFORMAT); diff --git a/data-access/servlet/src/main/java/webapi/ServletMerge.java b/data-access/servlet/src/main/java/webapi/ServletMerge.java index 2415e54..7fce57f 100644 --- a/data-access/servlet/src/main/java/webapi/ServletMerge.java +++ b/data-access/servlet/src/main/java/webapi/ServletMerge.java @@ -34,7 +34,7 @@ import java.util.TimeZone; import java.security.Principal; - +import vo.parameter.*; public class ServletMerge extends javax.servlet.http.HttpServlet { @@ -88,7 +88,7 @@ public class ServletMerge extends javax.servlet.http.HttpServlet try { - Map<SodaParam, String[]> params = collectSodaParams(request); + /* Map<SodaParam, String[]> params = collectSodaParams(request); SodaParser parser = new SodaParser(params); String id = null; @@ -111,6 +111,17 @@ public class ServletMerge extends javax.servlet.http.HttpServlet pos = parser.vlkbReq_getCircleRect(); band = parser.vlkbReq_getVelocity(); } +*/ + Map<String, String[]> params = request.getParameterMap(); + String id = null; + Pos pos = null; + Band band = null; + Time time = null; + Pol pol = null; + + + + String respFormat = DEFAULT_RESPONSEFORMAT;//sodaReq_getResponseFormat(request, DEFAULT_RESPONSEFORMAT); @@ -179,7 +190,7 @@ public class ServletMerge extends javax.servlet.http.HttpServlet LOGGER.info("processRequest normal exit"); } - +/* private Map<SodaParam, String[]> collectSodaParams(HttpServletRequest req) { Map<SodaParam, String[]> params = new HashMap<SodaParam, String[]>(); @@ -190,7 +201,7 @@ public class ServletMerge extends javax.servlet.http.HttpServlet } return params; } - +*/ /* semi-colon separated list of pudids convert to arra */ private String[] parseLegacyPubdidArr(String pubdids) 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..9ba5160584a847c38f4cae9a2732f69845e30032 GIT binary patch literal 23078 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 literal 0 HcmV?d00001 -- GitLab