diff --git a/doc/en/user/source/webadmin/data/layers.rst b/doc/en/user/source/webadmin/data/layers.rst index bfce94ed849..22f545614a8 100644 --- a/doc/en/user/source/webadmin/data/layers.rst +++ b/doc/en/user/source/webadmin/data/layers.rst @@ -286,4 +286,8 @@ For each enabled dimension the following configuration options are available: For time dimension the value must be in ISO 8601 DateTime format ``yyyy-MM-ddThh:mm:ss.SSSZ`` For elevation dimension, the value must be and integer of floating point number. +Only for the "Reference value" strategy, it is also possible to use ranges or times and ranges of elevation, in the form ``fromValue/toValue``. +Only for the "Reference value" strategy, and limited to times, it's also possible to use relative times like ``P1M/PRESENT``, but caution is given that the reference value +is copied verbatim into the capabilities document, and as a result, not all client might be recognizing that syntax. + .. note:: For more information on specifying times, please see the section on :ref:`wms_time`. diff --git a/src/ows/src/main/java/org/geoserver/ows/kvp/ElevationKvpParser.java b/src/ows/src/main/java/org/geoserver/ows/kvp/ElevationKvpParser.java index c0d23c53520..37e0f575e55 100644 --- a/src/ows/src/main/java/org/geoserver/ows/kvp/ElevationKvpParser.java +++ b/src/ows/src/main/java/org/geoserver/ows/kvp/ElevationKvpParser.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -6,20 +6,9 @@ package org.geoserver.ows.kvp; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.logging.Level; import org.geoserver.ows.KvpParser; -import org.geoserver.platform.GeoServerExtensions; -import org.geotools.util.DateRange; -import org.geotools.util.NumberRange; /** * Parses the {@code elevation} parameter of the request. @@ -30,29 +19,7 @@ */ public class ElevationKvpParser extends KvpParser { - /** - * Built-in limits - */ - private final static int MAX_ELEMENTS_ELEVATIONS_KVP; - - private final static int DEFAULT_MAX_ELEMENTS_ELEVATIONS_KVP = 100; - - static { - // initialization of the renderer choice flag - String value = GeoServerExtensions.getProperty("MAX_ELEMENTS_ELEVATIONS_KVP"); - // default to true, but allow switching on - if (value == null) - MAX_ELEMENTS_ELEVATIONS_KVP = DEFAULT_MAX_ELEMENTS_ELEVATIONS_KVP; - else { - int iVal = -1; - try { - iVal = Integer.parseInt(value.trim()); - } catch (Exception e) { - iVal = DEFAULT_MAX_ELEMENTS_ELEVATIONS_KVP; - } - MAX_ELEMENTS_ELEVATIONS_KVP = iVal; - } - } + ElevationParser parser = new ElevationParser(); /** * Creates the parser specifying the name of the key to latch to. @@ -64,137 +31,9 @@ public ElevationKvpParser(String key) { super(key, List.class); } - /** - * Parses the elevation given in parameter. The string may contains either a single double, or a - * start value, end value and a period. In the first case, this method returns a singleton - * containing only the parsed value. In the second case, this method returns a list including - * all elevations from start value up to the end value with the interval specified in the - * {@code value} string. - * - * @param value - * The elevation item or items to parse. - * @return A list of doubles, or an empty list of the {@code value} string is null or empty. - * @throws ParseException - * if the string can not be parsed. - */ @SuppressWarnings( { "unchecked", "rawtypes" }) public Object parse(String value) throws ParseException { - if (value == null) { - return Collections.emptyList(); - } - value = value.trim(); - if (value.length() == 0) { - return Collections.emptyList(); - } - final Set values = new TreeSet(new Comparator() { - - public int compare(Object o1, Object o2) { - final boolean o1Double = o1 instanceof Double; - final boolean o2Double = o2 instanceof Double; - - // o1 date - if (o1Double) { - final Double left = (Double) o1; - if (o2Double) { - // o2 date - return left.compareTo((Double) o2); - } - // o2 number range - return left.compareTo(((NumberRange) o2).getMinValue()); - } - - // o1 number range - final NumberRange left = (NumberRange) o1; - if (o2Double) { - // o2 date - return left.getMinValue().compareTo(((Double) o2)); - } - // o2 daterange - return left.getMinValue().compareTo(((NumberRange) o2).getMinValue()); - } - }); - final String[] listValues = value.split(","); - for (String d : listValues) { - if (d.indexOf("/") <= 0) { - addValue(values, Double.valueOf(d.trim())); - } else { - // period - - String[] period = d.split("/"); - // Only one value given. - if (period.length == 2) { - // Period continuous - final Double begin = Double.valueOf(period[0]); - final Double end = Double.valueOf(period[1]); - addPeriod(values, NumberRange.create(begin, end)); - } else if (period.length == 3) { - // Period discrete - final Double begin = Double.valueOf(period[0]); - final Double end = Double.valueOf(period[1]); - final Double increment = Double.valueOf(period[2]); - - Double step; - int j = 0; - while ((step = j * increment + begin) <= end) { - addValue(values, step); - j++; - - // limiting discrete period elements - if (j >= MAX_ELEMENTS_ELEVATIONS_KVP) { - if (LOGGER.isLoggable(Level.INFO)) - LOGGER.info("Lmiting number of elements in this periodo to " - + MAX_ELEMENTS_ELEVATIONS_KVP); - break; - - } - } - } else { - throw new ParseException("Invalid elevation parameter: " + period, 0); - } - } - } - - return new ArrayList(values); + return parser.parse(value); } - private void addValue(Collection result, Double step) { - for (Iterator it = result.iterator(); it.hasNext();) { - final Object element = it.next(); - if (element instanceof Double) { - // convert - final Double local = (Double) element; - if (local.equals(step)) - return; - } else { - // convert - final DateRange local = (DateRange) element; - if (local.contains(step)) - return; - } - } - result.add(step); - - } - - private void addPeriod(Collection result, NumberRange newRange) { - for (Iterator it = result.iterator(); it.hasNext();) { - final Object element = it.next(); - if (element instanceof Double) { - // convert - if (newRange.contains((Number) element)) { - it.remove(); - } - } else { - // convert - final NumberRange local = (NumberRange) element; - if (local.contains(newRange)) - return; - if (newRange.contains(local)) - it.remove(); - } - } - result.add(newRange); - - } - } diff --git a/src/ows/src/main/java/org/geoserver/ows/kvp/ElevationParser.java b/src/ows/src/main/java/org/geoserver/ows/kvp/ElevationParser.java new file mode 100644 index 00000000000..f3f86493cad --- /dev/null +++ b/src/ows/src/main/java/org/geoserver/ows/kvp/ElevationParser.java @@ -0,0 +1,192 @@ +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved + * (c) 2001 - 2013 OpenPlans + * This code is licensed under the GPL 2.0 license, available at the root + * application directory. + */ +package org.geoserver.ows.kvp; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.geoserver.platform.GeoServerExtensions; +import org.geotools.util.DateRange; +import org.geotools.util.NumberRange; +import org.geotools.util.logging.Logging; + +/** + * Parses the {@code elevation} parameter of the request. + * + * @author Ariel Nunez, GeoSolutions S.A.S. + * @author Simone Giannecchini, GeoSolutions S.A.S. + * @version $Id$ + */ +public class ElevationParser { + + static final Logger LOGGER = Logging.getLogger(ElevationParser.class); + + /** + * Built-in limits + */ + private final static int MAX_ELEMENTS_ELEVATIONS_KVP; + + private final static int DEFAULT_MAX_ELEMENTS_ELEVATIONS_KVP = 100; + + static { + // initialization of the renderer choice flag + String value = GeoServerExtensions.getProperty("MAX_ELEMENTS_ELEVATIONS_KVP"); + // default to true, but allow switching on + if (value == null) + MAX_ELEMENTS_ELEVATIONS_KVP = DEFAULT_MAX_ELEMENTS_ELEVATIONS_KVP; + else { + int iVal = -1; + try { + iVal = Integer.parseInt(value.trim()); + } catch (Exception e) { + iVal = DEFAULT_MAX_ELEMENTS_ELEVATIONS_KVP; + } + MAX_ELEMENTS_ELEVATIONS_KVP = iVal; + } + } + + /** + * Parses the elevation given in parameter. The string may contains either a single double, or a + * start value, end value and a period. In the first case, this method returns a singleton + * containing only the parsed value. In the second case, this method returns a list including + * all elevations from start value up to the end value with the interval specified in the + * {@code value} string. + * + * @param value + * The elevation item or items to parse. + * @return A list of doubles, or an empty list of the {@code value} string is null or empty. + * @throws ParseException + * if the string can not be parsed. + */ + @SuppressWarnings( { "unchecked", "rawtypes" }) + public Collection parse(String value) throws ParseException { + if (value == null) { + return Collections.emptyList(); + } + value = value.trim(); + if (value.length() == 0) { + return Collections.emptyList(); + } + final Set values = new TreeSet(new Comparator() { + + public int compare(Object o1, Object o2) { + final boolean o1Double = o1 instanceof Double; + final boolean o2Double = o2 instanceof Double; + + // o1 date + if (o1Double) { + final Double left = (Double) o1; + if (o2Double) { + // o2 date + return left.compareTo((Double) o2); + } + // o2 number range + return left.compareTo(((NumberRange) o2).getMinValue()); + } + + // o1 number range + final NumberRange left = (NumberRange) o1; + if (o2Double) { + // o2 date + return left.getMinValue().compareTo(((Double) o2)); + } + // o2 daterange + return left.getMinValue().compareTo(((NumberRange) o2).getMinValue()); + } + }); + final String[] listValues = value.split(","); + for (String d : listValues) { + if (d.indexOf("/") <= 0) { + addValue(values, Double.valueOf(d.trim())); + } else { + // period + + String[] period = d.split("/"); + // Only one value given. + if (period.length == 2) { + // Period continuous + final Double begin = Double.valueOf(period[0]); + final Double end = Double.valueOf(period[1]); + addPeriod(values, NumberRange.create(begin, end)); + } else if (period.length == 3) { + // Period discrete + final Double begin = Double.valueOf(period[0]); + final Double end = Double.valueOf(period[1]); + final Double increment = Double.valueOf(period[2]); + + Double step; + int j = 0; + while ((step = j * increment + begin) <= end) { + addValue(values, step); + j++; + + // limiting discrete period elements + if (j >= MAX_ELEMENTS_ELEVATIONS_KVP) { + if (LOGGER.isLoggable(Level.INFO)) + LOGGER.info("Lmiting number of elements in this periodo to " + + MAX_ELEMENTS_ELEVATIONS_KVP); + break; + + } + } + } else { + throw new ParseException("Invalid elevation parameter: " + period, 0); + } + } + } + + return new ArrayList(values); + } + + private void addValue(Collection result, Double step) { + for (Iterator it = result.iterator(); it.hasNext();) { + final Object element = it.next(); + if (element instanceof Double) { + // convert + final Double local = (Double) element; + if (local.equals(step)) + return; + } else { + // convert + final DateRange local = (DateRange) element; + if (local.contains(step)) + return; + } + } + result.add(step); + + } + + private void addPeriod(Collection result, NumberRange newRange) { + for (Iterator it = result.iterator(); it.hasNext();) { + final Object element = it.next(); + if (element instanceof Double) { + // convert + if (newRange.contains((Number) element)) { + it.remove(); + } + } else { + // convert + final NumberRange local = (NumberRange) element; + if (local.contains(newRange)) + return; + if (newRange.contains(local)) + it.remove(); + } + } + result.add(newRange); + + } + +} diff --git a/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java b/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java index 79987087122..63c81fab980 100644 --- a/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java +++ b/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -6,29 +6,9 @@ package org.geoserver.ows.kvp; import java.text.ParseException; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Iterator; import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.TimeZone; -import java.util.TreeSet; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.geoserver.ows.KvpParser; -import org.geoserver.platform.GeoServerExtensions; -import org.geotools.util.DateRange; /** @@ -41,78 +21,10 @@ * @author Jonathan Meyer, Applied Information Sciences, jon@gisjedi.com * @version $Id$ */ -public class TimeKvpParser extends KvpParser { - private static enum FormatAndPrecision { - MILLISECOND("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Calendar.MILLISECOND), - SECOND("yyyy-MM-dd'T'HH:mm:ss'Z'", Calendar.SECOND), - MINUTE("yyyy-MM-dd'T'HH:mm'Z'", Calendar.MINUTE), - HOUR("yyyy-MM-dd'T'HH'Z'", Calendar.HOUR_OF_DAY), - DAY("yyyy-MM-dd", Calendar.DAY_OF_MONTH), - MONTH("yyyy-MM", Calendar.MONTH), - YEAR("yyyy", Calendar.YEAR); - - public final String format; - public final int precision; - - FormatAndPrecision(final String format, int precision) { - this.format = format; - this.precision = precision; - } - - public SimpleDateFormat getFormat() { - SimpleDateFormat sdf = new SimpleDateFormat(format); - sdf.setTimeZone(UTC_TZ); - return sdf; - } - - public DateRange expand(Date d) { - Calendar c = new GregorianCalendar(UTC_TZ); - c.setTime(d); - c.add(this.precision, 1); - c.add(Calendar.MILLISECOND, -1); - return new DateRange(d, c.getTime()); - } - } - - /** - * UTC timezone to serve as reference - */ - static final TimeZone UTC_TZ = TimeZone.getTimeZone("UTC"); - - /** - * pattern used to match back parameter - */ - private static final Pattern pattern = Pattern.compile("(back)(\\d+)([hdw])"); +public class TimeKvpParser extends KvpParser { - /** - * Amount of milliseconds in a day. - */ - static final long MILLIS_IN_DAY = 24*60*60*1000; - + TimeParser parser = new TimeParser(); - /** - * Built-in limits to avoid exploding on too large requests - */ - private final static int MAX_ELEMENTS_TIMES_KVP; - private final static int DEFAULT_MAX_ELEMENTS_TIMES_KVP = 100; - - static { - // initialization of the renderer choice flag - String value = GeoServerExtensions.getProperty("MAX_ELEMENTS_TIMES_KVP"); - // default to true, but allow switching on - if (value == null) - MAX_ELEMENTS_TIMES_KVP = DEFAULT_MAX_ELEMENTS_TIMES_KVP; - else { - int iVal = -1; - try { - iVal = Integer.parseInt(value.trim()); - } catch (Exception e) { - iVal = DEFAULT_MAX_ELEMENTS_TIMES_KVP; - } - MAX_ELEMENTS_TIMES_KVP = iVal; - } - } - /** * Creates the parser specifying the name of the key to latch to. * @@ -122,334 +34,9 @@ public TimeKvpParser(String key) { super(key, List.class); } - /** - * Parses the date given in parameter. The date format should comply to - * ISO-8601 standard. The string may contains either a single date, or - * a start time, end time and a period. In the first case, this method - * returns a singleton containing only the parsed date. In the second - * case, this method returns a list including all dates from start time - * up to the end time with the interval specified in the {@code value} - * string. - * - * @param value The date, time and period to parse. - * @return A list of dates, or an empty list of the {@code value} string - * is null or empty. - * @throws ParseException if the string can not be parsed. - */ @SuppressWarnings({ "unchecked", "rawtypes" }) public Object parse(String value) throws ParseException { - if (value == null) { - return Collections.emptyList(); - } - value = value.trim(); - if (value.length() == 0) { - return Collections.emptyList(); - } - - final Set result = new TreeSet(new Comparator() { - - public int compare(Object o1, Object o2) { - final boolean o1Date= o1 instanceof Date; - final boolean o2Date= o2 instanceof Date; - - if(o1 == o2) { - return 0; - } - - // o1 date - if(o1Date){ - final Date dateLeft=(Date) o1; - if(o2Date){ - // o2 date - return dateLeft.compareTo((Date) o2); - } - // o2 daterange - return dateLeft.compareTo(((DateRange)o2).getMinValue()); - } - - // o1 date range - final DateRange left= (DateRange) o1; - if(o2Date){ - // o2 date - return left.getMinValue().compareTo(((Date) o2)); - } - // o2 daterange - return left.getMinValue().compareTo(((DateRange)o2).getMinValue()); - } - }); - String[] listDates = value.split(","); - for(String date: listDates){ - // is it a date or a period? - if(date.indexOf("/")<=0){ - Object o = getFuzzyDate(date); - if (o instanceof Date) { - addDate(result, (Date)o); - } else { - addPeriod(result, (DateRange)o); - } - } else { - // period - String[] period = date.split("/"); - - // - // Period like one of the following: - // yyyy-MM-ddTHH:mm:ssZ/yyyy-MM-ddTHH:mm:ssZ/P1D - // May be one of the following possible ISO 8601 Time Interval formats with trailing period for - // breaking the interval by given period: - // TIME/TIME/PERIOD - // DURATION/TIME/PERIOD - // TIME/DURATION/PERIOD - // - if (period.length == 3) { - Date[] range = parseTimeDuration(period); - - final long millisIncrement = parsePeriod(period[2]); - final long startTime = range[0].getTime(); - final long endTime = range[1].getTime(); - long time; - int j = 0; - while ((time = j * millisIncrement + startTime) <= endTime) { - final Calendar calendar = new GregorianCalendar(UTC_TZ); - calendar.setTimeInMillis(time); - addDate(result, calendar.getTime()); - j++; - - // limiting number of elements we can create - if(j>= MAX_ELEMENTS_TIMES_KVP){ - if(LOGGER.isLoggable(Level.INFO)) - LOGGER.info("Lmiting number of elements in this periodo to "+MAX_ELEMENTS_TIMES_KVP); - break; - } - } - } - // Period like : yyyy-MM-ddTHH:mm:ssZ/yyyy-MM-ddTHH:mm:ssZ, it is an extension - // of WMS that works with continuos period [Tb, Te]. - // May be one of the following possible ISO 8601 Time Interval formats, as in ECQL Time Period: - // TIME/DURATION - // DURATION/TIME - // TIME/TIME - else if (period.length == 2) { - Date[] range = parseTimeDuration(period); - addPeriod(result, new DateRange(range[0], range[1])); - } else { - throw new ParseException("Invalid time period: " + Arrays.toString(period), 0); - } - } - } - - return new ArrayList(result); - } - - private static Date[] parseTimeDuration(final String[] period) throws ParseException { - Date[] range = null; - - if (period.length == 2 || period.length == 3) { - Date begin = null; - Date end = null; - - // Check first to see if we have any duration value within TIME parameter - if (period[0].toUpperCase().startsWith("P") || period[1].toUpperCase().startsWith("P")) { - long durationOffset = Long.MIN_VALUE; - - // Attempt to parse a time or duration from the first portion of the - if (period[0].toUpperCase().startsWith("P")) { - durationOffset = parsePeriod(period[0]); - } else { - begin = beginning(getFuzzyDate(period[0])); - } - - if (period[1].toUpperCase().startsWith("P") - && !period[1].toUpperCase().startsWith("PRESENT")) { - // Invalid time period of the format: - // DURATION/DURATION[/PERIOD] - if (durationOffset != Long.MIN_VALUE) { - throw new ParseException( - "Invalid time period containing duration with no paired time value: " - + Arrays.toString(period), 0); - } - // Time period of the format: - // DURATION/TIME[/PERIOD] - else { - durationOffset = parsePeriod(period[1]); - final Calendar calendar = new GregorianCalendar(); - calendar.setTimeInMillis(begin.getTime() + durationOffset); - end = calendar.getTime(); - } - } - // Time period of the format: - // TIME/DURATION[/PERIOD] - else { - end = end(getFuzzyDate(period[1])); - final Calendar calendar = new GregorianCalendar(); - calendar.setTimeInMillis(end.getTime() - durationOffset); - begin = calendar.getTime(); - } - } - // Time period of the format: - // TIME/TIME[/PERIOD] - else { - begin = beginning(getFuzzyDate(period[0])); - end = end(getFuzzyDate(period[1])); - } - - range = new Date[2]; - range[0] = begin; - range[1] = end; - - } - - return range; - } - - private static Date beginning(Object dateOrDateRange) { - if (dateOrDateRange instanceof DateRange) { - return ((DateRange) dateOrDateRange).getMinValue(); - } else { - return (Date) dateOrDateRange; - } - } - - private static Date end(Object dateOrDateRange) { - if (dateOrDateRange instanceof DateRange) { - return ((DateRange) dateOrDateRange).getMaxValue(); - } else { - return (Date) dateOrDateRange; - } - } - - /** - * Tries to avoid insertion of multiple time values. - * - * @param result - * @param newRange - */ - private static void addPeriod(Collection result, DateRange newRange) { - for(Iterator it=result.iterator();it.hasNext();){ - final Object element=it.next(); - if(element instanceof Date){ - // convert - final Date local= (Date) element; - if(newRange.contains(local)){ - it.remove(); - } - } else { - // convert - final DateRange local= (DateRange) element; - if(local.contains(newRange)) - return; - if(newRange.contains(local)) - it.remove(); - } - } - result.add(newRange); - } - - private static void addDate(Collection result, Date newDate) { - for (Iterator it = result.iterator(); it.hasNext(); ) { - final Object element = it.next(); - if (element instanceof Date) { - if (newDate.equals(element)) return; - } else if (((DateRange) element).contains(newDate)) { - return; - } - } - result.add(newDate); - } - - /** - * Parses date given in parameter according the ISO-8601 standard. This parameter should follow - * a syntax defined in the {@link #PATTERNS} array to be validated. - * - * @param value The date to parse. - * @return A date found in the request. - * @throws ParseException if the string can not be parsed. - */ - static Object getFuzzyDate(final String value) throws ParseException { - String computedValue = value; - - // special handling for current keyword (we accept both wms and wcs ways) - if (computedValue.equalsIgnoreCase("current") || computedValue.equalsIgnoreCase("now")) { - return null; - } - - // Accept new "present" keyword, which actually fills in present time as now should have - if (computedValue.equalsIgnoreCase("present")) { - Calendar now = Calendar.getInstance(); - now.set(Calendar.MILLISECOND, 0); - computedValue = FormatAndPrecision.MILLISECOND.getFormat().format(now.getTime()); - } - - for (FormatAndPrecision f : FormatAndPrecision.values()) { - ParsePosition pos = new ParsePosition(0); - Date time = f.getFormat().parse(computedValue, pos); - if (pos.getIndex() == computedValue.length()) { - DateRange range = f.expand(time); - if (range.getMinValue().equals(range.getMaxValue())) { - return range.getMinValue(); - } else { - return range; - } - } - } - - throw new ParseException("Invalid date: " + value, 0); + return parser.parse(value); } - /** - * Parses the increment part of a period and returns it in milliseconds. - * - * @param period A string representation of the time increment according the ISO-8601:1988(E) - * standard. For example: {@code "P1D"} = one day. - * @return The increment value converted in milliseconds. - * @throws ParseException if the string can not be parsed. - */ - static long parsePeriod(final String period) throws ParseException { - final int length = period.length(); - if (length!=0 && Character.toUpperCase(period.charAt(0)) != 'P') { - throw new ParseException("Invalid period increment given: " + period, 0); - } - long millis = 0; - boolean time = false; - int lower = 0; - while (++lower < length) { - char letter = Character.toUpperCase(period.charAt(lower)); - if (letter == 'T') { - time = true; - if (++lower >= length) { - break; - } - } - int upper = lower; - letter = period.charAt(upper); - while (!Character.isLetter(letter) || letter == 'e' || letter == 'E') { - if (++upper >= length) { - throw new ParseException("Missing symbol in \"" + period + "\".", lower); - } - letter = period.charAt(upper); - } - letter = Character.toUpperCase(letter); - final double value = Double.parseDouble(period.substring(lower, upper)); - final double factor; - if (time) { - switch (letter) { - case 'S': factor = 1000; break; - case 'M': factor = 60*1000; break; - case 'H': factor = 60*60*1000; break; - default: throw new ParseException("Unknown time symbol: " + letter, upper); - } - } else { - switch (letter) { - case 'D': factor = MILLIS_IN_DAY; break; - case 'W': factor = 7 * MILLIS_IN_DAY; break; - // TODO: handle months in a better way than just taking the average length. - case 'M': factor = 30 * MILLIS_IN_DAY; break; - case 'Y': factor = 365.25 * MILLIS_IN_DAY; break; - default: throw new ParseException("Unknown period symbol: " + letter, upper); - } - } - millis += Math.round(value * factor); - lower = upper; - } - return millis; - } } diff --git a/src/ows/src/main/java/org/geoserver/ows/kvp/TimeParser.java b/src/ows/src/main/java/org/geoserver/ows/kvp/TimeParser.java new file mode 100644 index 00000000000..c5fbda4ef44 --- /dev/null +++ b/src/ows/src/main/java/org/geoserver/ows/kvp/TimeParser.java @@ -0,0 +1,446 @@ +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved + * (c) 2001 - 2013 OpenPlans + * This code is licensed under the GPL 2.0 license, available at the root + * application directory. + */ +package org.geoserver.ows.kvp; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Iterator; +import java.util.Set; +import java.util.TimeZone; +import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import org.geoserver.platform.GeoServerExtensions; +import org.geotools.util.DateRange; +import org.geotools.util.logging.Logging; + + +/** + * Parses the {@code time} parameter of the request. The date, time and period + * are expected to be formatted according ISO-8601 standard. + * + * @author Cedric Briancon + * @author Martin Desruisseaux + * @author Simone Giannecchini, GeoSolutions SAS + * @author Jonathan Meyer, Applied Information Sciences, jon@gisjedi.com + * @version $Id$ + */ +public class TimeParser { + static final Logger LOGGER = Logging.getLogger(TimeParser.class); + + private static enum FormatAndPrecision { + MILLISECOND("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Calendar.MILLISECOND), + SECOND("yyyy-MM-dd'T'HH:mm:ss'Z'", Calendar.SECOND), + MINUTE("yyyy-MM-dd'T'HH:mm'Z'", Calendar.MINUTE), + HOUR("yyyy-MM-dd'T'HH'Z'", Calendar.HOUR_OF_DAY), + DAY("yyyy-MM-dd", Calendar.DAY_OF_MONTH), + MONTH("yyyy-MM", Calendar.MONTH), + YEAR("yyyy", Calendar.YEAR); + + public final String format; + public final int precision; + + FormatAndPrecision(final String format, int precision) { + this.format = format; + this.precision = precision; + } + + public SimpleDateFormat getFormat() { + SimpleDateFormat sdf = new SimpleDateFormat(format); + sdf.setTimeZone(UTC_TZ); + return sdf; + } + + public DateRange expand(Date d) { + Calendar c = new GregorianCalendar(UTC_TZ); + c.setTime(d); + c.add(this.precision, 1); + c.add(Calendar.MILLISECOND, -1); + return new DateRange(d, c.getTime()); + } + } + + /** + * UTC timezone to serve as reference + */ + static final TimeZone UTC_TZ = TimeZone.getTimeZone("UTC"); + + /** + * pattern used to match back parameter + */ + private static final Pattern pattern = Pattern.compile("(back)(\\d+)([hdw])"); + + /** + * Amount of milliseconds in a day. + */ + static final long MILLIS_IN_DAY = 24*60*60*1000; + + + /** + * Built-in limits to avoid exploding on too large requests + */ + private final static int MAX_ELEMENTS_TIMES_KVP; + private final static int DEFAULT_MAX_ELEMENTS_TIMES_KVP = 100; + + static { + // initialization of the renderer choice flag + String value = GeoServerExtensions.getProperty("MAX_ELEMENTS_TIMES_KVP"); + // default to true, but allow switching on + if (value == null) + MAX_ELEMENTS_TIMES_KVP = DEFAULT_MAX_ELEMENTS_TIMES_KVP; + else { + int iVal = -1; + try { + iVal = Integer.parseInt(value.trim()); + } catch (Exception e) { + iVal = DEFAULT_MAX_ELEMENTS_TIMES_KVP; + } + MAX_ELEMENTS_TIMES_KVP = iVal; + } + } + + /** + * Parses the date given in parameter. The date format should comply to + * ISO-8601 standard. The string may contains either a single date, or + * a start time, end time and a period. In the first case, this method + * returns a singleton containing only the parsed date. In the second + * case, this method returns a list including all dates from start time + * up to the end time with the interval specified in the {@code value} + * string. + * + * @param value The date, time and period to parse. + * @return A list of dates, or an empty list of the {@code value} string + * is null or empty. + * @throws ParseException if the string can not be parsed. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Collection parse(String value) throws ParseException { + if (value == null) { + return Collections.emptyList(); + } + value = value.trim(); + if (value.length() == 0) { + return Collections.emptyList(); + } + + final Set result = new TreeSet(new Comparator() { + + public int compare(Object o1, Object o2) { + final boolean o1Date= o1 instanceof Date; + final boolean o2Date= o2 instanceof Date; + + if(o1 == o2) { + return 0; + } + + // o1 date + if(o1Date){ + final Date dateLeft=(Date) o1; + if(o2Date){ + // o2 date + return dateLeft.compareTo((Date) o2); + } + // o2 daterange + return dateLeft.compareTo(((DateRange)o2).getMinValue()); + } + + // o1 date range + final DateRange left= (DateRange) o1; + if(o2Date){ + // o2 date + return left.getMinValue().compareTo(((Date) o2)); + } + // o2 daterange + return left.getMinValue().compareTo(((DateRange)o2).getMinValue()); + } + }); + String[] listDates = value.split(","); + for(String date: listDates){ + // is it a date or a period? + if(date.indexOf("/")<=0){ + Object o = getFuzzyDate(date); + if (o instanceof Date) { + addDate(result, (Date)o); + } else { + addPeriod(result, (DateRange)o); + } + } else { + // period + String[] period = date.split("/"); + + // + // Period like one of the following: + // yyyy-MM-ddTHH:mm:ssZ/yyyy-MM-ddTHH:mm:ssZ/P1D + // May be one of the following possible ISO 8601 Time Interval formats with trailing period for + // breaking the interval by given period: + // TIME/TIME/PERIOD + // DURATION/TIME/PERIOD + // TIME/DURATION/PERIOD + // + if (period.length == 3) { + Date[] range = parseTimeDuration(period); + + final long millisIncrement = parsePeriod(period[2]); + final long startTime = range[0].getTime(); + final long endTime = range[1].getTime(); + long time; + int j = 0; + while ((time = j * millisIncrement + startTime) <= endTime) { + final Calendar calendar = new GregorianCalendar(UTC_TZ); + calendar.setTimeInMillis(time); + addDate(result, calendar.getTime()); + j++; + + // limiting number of elements we can create + if(j>= MAX_ELEMENTS_TIMES_KVP){ + if(LOGGER.isLoggable(Level.INFO)) + LOGGER.info("Lmiting number of elements in this periodo to "+MAX_ELEMENTS_TIMES_KVP); + break; + } + } + } + // Period like : yyyy-MM-ddTHH:mm:ssZ/yyyy-MM-ddTHH:mm:ssZ, it is an extension + // of WMS that works with continuos period [Tb, Te]. + // May be one of the following possible ISO 8601 Time Interval formats, as in ECQL Time Period: + // TIME/DURATION + // DURATION/TIME + // TIME/TIME + else if (period.length == 2) { + Date[] range = parseTimeDuration(period); + addPeriod(result, new DateRange(range[0], range[1])); + } else { + throw new ParseException("Invalid time period: " + Arrays.toString(period), 0); + } + } + } + + return new ArrayList(result); + } + + private static Date[] parseTimeDuration(final String[] period) throws ParseException { + Date[] range = null; + + if (period.length == 2 || period.length == 3) { + Date begin = null; + Date end = null; + + // Check first to see if we have any duration value within TIME parameter + if (period[0].toUpperCase().startsWith("P") || period[1].toUpperCase().startsWith("P")) { + long durationOffset = Long.MIN_VALUE; + + // Attempt to parse a time or duration from the first portion of the + if (period[0].toUpperCase().startsWith("P")) { + durationOffset = parsePeriod(period[0]); + } else { + begin = beginning(getFuzzyDate(period[0])); + } + + if (period[1].toUpperCase().startsWith("P") + && !period[1].toUpperCase().startsWith("PRESENT")) { + // Invalid time period of the format: + // DURATION/DURATION[/PERIOD] + if (durationOffset != Long.MIN_VALUE) { + throw new ParseException( + "Invalid time period containing duration with no paired time value: " + + Arrays.toString(period), 0); + } + // Time period of the format: + // DURATION/TIME[/PERIOD] + else { + durationOffset = parsePeriod(period[1]); + final Calendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(begin.getTime() + durationOffset); + end = calendar.getTime(); + } + } + // Time period of the format: + // TIME/DURATION[/PERIOD] + else { + end = end(getFuzzyDate(period[1])); + final Calendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(end.getTime() - durationOffset); + begin = calendar.getTime(); + } + } + // Time period of the format: + // TIME/TIME[/PERIOD] + else { + begin = beginning(getFuzzyDate(period[0])); + end = end(getFuzzyDate(period[1])); + } + + range = new Date[2]; + range[0] = begin; + range[1] = end; + + } + + return range; + } + + private static Date beginning(Object dateOrDateRange) { + if (dateOrDateRange instanceof DateRange) { + return ((DateRange) dateOrDateRange).getMinValue(); + } else { + return (Date) dateOrDateRange; + } + } + + private static Date end(Object dateOrDateRange) { + if (dateOrDateRange instanceof DateRange) { + return ((DateRange) dateOrDateRange).getMaxValue(); + } else { + return (Date) dateOrDateRange; + } + } + + /** + * Tries to avoid insertion of multiple time values. + * + * @param result + * @param newRange + */ + private static void addPeriod(Collection result, DateRange newRange) { + for(Iterator it=result.iterator();it.hasNext();){ + final Object element=it.next(); + if(element instanceof Date){ + // convert + final Date local= (Date) element; + if(newRange.contains(local)){ + it.remove(); + } + } else { + // convert + final DateRange local= (DateRange) element; + if(local.contains(newRange)) + return; + if(newRange.contains(local)) + it.remove(); + } + } + result.add(newRange); + } + + private static void addDate(Collection result, Date newDate) { + for (Iterator it = result.iterator(); it.hasNext(); ) { + final Object element = it.next(); + if (element instanceof Date) { + if (newDate.equals(element)) return; + } else if (((DateRange) element).contains(newDate)) { + return; + } + } + result.add(newDate); + } + + /** + * Parses date given in parameter according the ISO-8601 standard. This parameter should follow + * a syntax defined in the {@link #PATTERNS} array to be validated. + * + * @param value The date to parse. + * @return A date found in the request. + * @throws ParseException if the string can not be parsed. + */ + static Object getFuzzyDate(final String value) throws ParseException { + String computedValue = value; + + // special handling for current keyword (we accept both wms and wcs ways) + if (computedValue.equalsIgnoreCase("current") || computedValue.equalsIgnoreCase("now")) { + return null; + } + + // Accept new "present" keyword, which actually fills in present time as now should have + if (computedValue.equalsIgnoreCase("present")) { + Calendar now = Calendar.getInstance(); + now.set(Calendar.MILLISECOND, 0); + computedValue = FormatAndPrecision.MILLISECOND.getFormat().format(now.getTime()); + } + + for (FormatAndPrecision f : FormatAndPrecision.values()) { + ParsePosition pos = new ParsePosition(0); + Date time = f.getFormat().parse(computedValue, pos); + if (pos.getIndex() == computedValue.length()) { + DateRange range = f.expand(time); + if (range.getMinValue().equals(range.getMaxValue())) { + return range.getMinValue(); + } else { + return range; + } + } + } + + throw new ParseException("Invalid date: " + value, 0); + } + + /** + * Parses the increment part of a period and returns it in milliseconds. + * + * @param period A string representation of the time increment according the ISO-8601:1988(E) + * standard. For example: {@code "P1D"} = one day. + * @return The increment value converted in milliseconds. + * @throws ParseException if the string can not be parsed. + */ + static long parsePeriod(final String period) throws ParseException { + final int length = period.length(); + if (length!=0 && Character.toUpperCase(period.charAt(0)) != 'P') { + throw new ParseException("Invalid period increment given: " + period, 0); + } + long millis = 0; + boolean time = false; + int lower = 0; + while (++lower < length) { + char letter = Character.toUpperCase(period.charAt(lower)); + if (letter == 'T') { + time = true; + if (++lower >= length) { + break; + } + } + int upper = lower; + letter = period.charAt(upper); + while (!Character.isLetter(letter) || letter == 'e' || letter == 'E') { + if (++upper >= length) { + throw new ParseException("Missing symbol in \"" + period + "\".", lower); + } + letter = period.charAt(upper); + } + letter = Character.toUpperCase(letter); + final double value = Double.parseDouble(period.substring(lower, upper)); + final double factor; + if (time) { + switch (letter) { + case 'S': factor = 1000; break; + case 'M': factor = 60*1000; break; + case 'H': factor = 60*60*1000; break; + default: throw new ParseException("Unknown time symbol: " + letter, upper); + } + } else { + switch (letter) { + case 'D': factor = MILLIS_IN_DAY; break; + case 'W': factor = 7 * MILLIS_IN_DAY; break; + // TODO: handle months in a better way than just taking the average length. + case 'M': factor = 30 * MILLIS_IN_DAY; break; + case 'Y': factor = 365.25 * MILLIS_IN_DAY; break; + default: throw new ParseException("Unknown period symbol: " + letter, upper); + } + } + millis += Math.round(value * factor); + lower = upper; + } + return millis; + } +} diff --git a/src/ows/src/test/java/org/geoserver/ows/kvp/TimeKvpParserTest.java b/src/ows/src/test/java/org/geoserver/ows/kvp/TimeKvpParserTest.java index ff98a9f6e91..f475bc30f62 100644 --- a/src/ows/src/test/java/org/geoserver/ows/kvp/TimeKvpParserTest.java +++ b/src/ows/src/test/java/org/geoserver/ows/kvp/TimeKvpParserTest.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -50,14 +50,14 @@ public class TimeKvpParserTest extends TestCase { private final static DateFormat format; static { format = new SimpleDateFormat("yyyy-MM-dd'T'HH'Z'"); - format.setTimeZone(TimeKvpParser.UTC_TZ); + format.setTimeZone(TimeParser.UTC_TZ); } public void testReducedAccuracyYear() throws Exception { Calendar c = new GregorianCalendar(); - c.setTimeZone(TimeKvpParser.UTC_TZ); + c.setTimeZone(TimeParser.UTC_TZ); - DateRange year = (DateRange) TimeKvpParser.getFuzzyDate("2000"); + DateRange year = (DateRange) TimeParser.getFuzzyDate("2000"); c.clear(); c.set(Calendar.YEAR, 2000); assertRangeStarts(year, c.getTime()); @@ -65,7 +65,7 @@ public void testReducedAccuracyYear() throws Exception { c.add(Calendar.MILLISECOND, -1); assertRangeEnds(year, c.getTime()); - year = (DateRange) TimeKvpParser.getFuzzyDate("2001"); + year = (DateRange) TimeParser.getFuzzyDate("2001"); c.clear(); c.set(Calendar.YEAR, 2001); assertRangeStarts(year, c.getTime()); @@ -73,7 +73,7 @@ public void testReducedAccuracyYear() throws Exception { c.add(Calendar.MILLISECOND, -1); assertRangeEnds(year, c.getTime()); - year = (DateRange) TimeKvpParser.getFuzzyDate("-6052"); + year = (DateRange) TimeParser.getFuzzyDate("-6052"); c.clear(); c.set(Calendar.ERA, GregorianCalendar.BC); c.set(Calendar.YEAR, 6053); @@ -85,10 +85,10 @@ public void testReducedAccuracyYear() throws Exception { public void testReducedAccuracyHour() throws Exception { Calendar c = new GregorianCalendar(); - c.setTimeZone(TimeKvpParser.UTC_TZ); + c.setTimeZone(TimeParser.UTC_TZ); c.clear(); - DateRange hour = (DateRange) TimeKvpParser.getFuzzyDate("2000-04-04T12Z"); + DateRange hour = (DateRange) TimeParser.getFuzzyDate("2000-04-04T12Z"); c.set(Calendar.YEAR, 2000); c.set(Calendar.MONTH, 3); // 0-indexed c.set(Calendar.DAY_OF_MONTH, 4); @@ -98,7 +98,7 @@ public void testReducedAccuracyHour() throws Exception { c.add(Calendar.MILLISECOND, -1); assertRangeEnds(hour, c.getTime()); - hour = (DateRange) TimeKvpParser.getFuzzyDate("2005-12-31T23Z"); // selected due to leapsecond at 23:59:60 UTC + hour = (DateRange) TimeParser.getFuzzyDate("2005-12-31T23Z"); // selected due to leapsecond at 23:59:60 UTC c.clear(); c.set(Calendar.YEAR, 2005); c.set(Calendar.MONTH, 11); @@ -109,7 +109,7 @@ public void testReducedAccuracyHour() throws Exception { c.add(Calendar.MILLISECOND, -1); assertRangeEnds(hour, c.getTime()); - hour = (DateRange) TimeKvpParser.getFuzzyDate("-25-06-08T17Z"); + hour = (DateRange) TimeParser.getFuzzyDate("-25-06-08T17Z"); c.clear(); c.set(Calendar.ERA, GregorianCalendar.BC); c.set(Calendar.YEAR, 26); @@ -124,17 +124,17 @@ public void testReducedAccuracyHour() throws Exception { public void testReducedAccuracyMilliseconds() throws Exception { Calendar c = new GregorianCalendar(); - c.setTimeZone(TimeKvpParser.UTC_TZ); + c.setTimeZone(TimeParser.UTC_TZ); c.clear(); - Date instant = (Date) TimeKvpParser.getFuzzyDate("2000-04-04T12:00:00.000Z"); + Date instant = (Date) TimeParser.getFuzzyDate("2000-04-04T12:00:00.000Z"); c.set(Calendar.YEAR, 2000); c.set(Calendar.MONTH, 3); // 0-indexed c.set(Calendar.DAY_OF_MONTH, 4); c.set(Calendar.HOUR_OF_DAY, 12); assertEquals(instant, c.getTime()); - instant = (Date) TimeKvpParser.getFuzzyDate("2005-12-31T23:59:60.000Z"); // selected due to leapsecond at 23:59:60 UTC + instant = (Date) TimeParser.getFuzzyDate("2005-12-31T23:59:60.000Z"); // selected due to leapsecond at 23:59:60 UTC c.clear(); c.set(Calendar.YEAR, 2005); c.set(Calendar.MONTH, 11); @@ -144,7 +144,7 @@ public void testReducedAccuracyMilliseconds() throws Exception { c.set(Calendar.SECOND, 60); assertEquals(instant, c.getTime()); - instant = (Date) TimeKvpParser.getFuzzyDate("-25-06-08T17:15:00.123Z"); + instant = (Date) TimeParser.getFuzzyDate("-25-06-08T17:15:00.123Z"); c.clear(); c.set(Calendar.ERA, GregorianCalendar.BC); c.set(Calendar.YEAR, 26); @@ -162,13 +162,13 @@ public void testReducedAccuracyMilliseconds() throws Exception { * @throws ParseException if the string can't be parsed. */ public void testPeriod() throws ParseException { - final long millisInDay = TimeKvpParser.MILLIS_IN_DAY; - assertEquals( millisInDay, TimeKvpParser.parsePeriod("P1D")); - assertEquals( 3*millisInDay, TimeKvpParser.parsePeriod("P3D")); - assertEquals( 14*millisInDay, TimeKvpParser.parsePeriod("P2W")); - assertEquals( 8*millisInDay, TimeKvpParser.parsePeriod("P1W1D")); - assertEquals( millisInDay, TimeKvpParser.parsePeriod("PT24H")); - assertEquals(Math.round(1.5*millisInDay), TimeKvpParser.parsePeriod("P1.5D")); + final long millisInDay = TimeParser.MILLIS_IN_DAY; + assertEquals( millisInDay, TimeParser.parsePeriod("P1D")); + assertEquals( 3*millisInDay, TimeParser.parsePeriod("P3D")); + assertEquals( 14*millisInDay, TimeParser.parsePeriod("P2W")); + assertEquals( 8*millisInDay, TimeParser.parsePeriod("P1W1D")); + assertEquals( millisInDay, TimeParser.parsePeriod("PT24H")); + assertEquals(Math.round(1.5*millisInDay), TimeParser.parsePeriod("P1.5D")); } /** @@ -237,7 +237,7 @@ public void testInvalidDualDuration() throws ParseException { } public void testContinuousRelativeInterval() throws ParseException { - final int millisInDay = (int) TimeKvpParser.MILLIS_IN_DAY; + final int millisInDay = (int) TimeParser.MILLIS_IN_DAY; TimeKvpParser timeKvpParser = new TimeKvpParser("TIME"); Calendar back = Calendar.getInstance(); Calendar now = (Calendar) back.clone(); diff --git a/src/web/core/src/main/java/org/geoserver/web/data/resource/DimensionEditor.java b/src/web/core/src/main/java/org/geoserver/web/data/resource/DimensionEditor.java index 69d5acaac13..7dd2fae30ba 100644 --- a/src/web/core/src/main/java/org/geoserver/web/data/resource/DimensionEditor.java +++ b/src/web/core/src/main/java/org/geoserver/web/data/resource/DimensionEditor.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -12,6 +12,8 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -35,9 +37,14 @@ import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.catalog.impl.DimensionInfoImpl; +import org.geoserver.ows.kvp.ElevationKvpParser; +import org.geoserver.ows.kvp.TimeKvpParser; +import org.geoserver.ows.kvp.TimeParser; +import org.geoserver.platform.GeoServerExtensions; import org.geoserver.web.wicket.ParamResourceModel; import org.geotools.coverage.grid.io.GridCoverage2DReader; -import org.geotools.feature.type.DateUtil; +import org.geotools.util.Range; +import org.geotools.util.logging.Logging; import org.opengis.coverage.grid.GridCoverageReader; import org.opengis.feature.type.PropertyDescriptor; @@ -48,6 +55,8 @@ */ @SuppressWarnings("serial") public class DimensionEditor extends FormComponentPanel { + + static final Logger LOGGER = Logging.getLogger(DimensionEditor.class); List presentationModes; @@ -452,32 +461,58 @@ public ReferenceValueValidator(String dimensionId, IModel value) { - if ( ((strategyModel.getObject() == Strategy.FIXED) || (strategyModel.getObject() == Strategy.NEAREST)) && value.getValue() == null){ + String stringValue = value.getValue(); + if ( ((strategyModel.getObject() == Strategy.FIXED) || (strategyModel.getObject() == Strategy.NEAREST)) && stringValue == null){ error(value, "emptyReferenceValue"); + } else if (dimension.equals("time")) { + if(!isValidTimeReference(stringValue, strategyModel.getObject())) { + String messageKey = strategyModel.getObject() == Strategy.NEAREST ? "invalidNearestTimeReferenceValue" : "invalidTimeReferenceValue"; + error(value, messageKey , Collections.singletonMap("value", (Object) stringValue)); + } + + } else if (dimension.equals("elevation")) { + if(!isValidElevationReference(stringValue)) { + error(value, "invalidElevationReferenceValue", Collections.singletonMap("value", + (Object) stringValue)); + } } - else if (dimension.equals("time")) { - try { - DateUtil.parseDateTime(value.getValue()); - } catch (IllegalArgumentException iae) { - if (strategyModel.getObject() == Strategy.NEAREST) { - if (!DimensionDefaultValueSetting.TIME_CURRENT.equalsIgnoreCase(value - .getValue())) { - error(value, "invalidNearestTimeReferenceValue", Collections - .singletonMap("value", (Object) value.getValue())); - } - } else { - error(value, "invalidTimeReferenceValue", Collections.singletonMap("value", - (Object) value.getValue())); - } + } + + private boolean isValidElevationReference(String stringValue) { + try { + ElevationKvpParser parser = GeoServerExtensions.bean(ElevationKvpParser.class); + List values = (List) parser.parse(stringValue); + // the KVP parser accepts also lists of values, we want a single one + return values.size() == 1; + } catch (Exception e) { + if(LOGGER.isLoggable(Level.FINER)) { + LOGGER.log(Level.FINER, "Invalid elevation value " + stringValue, e); } + return false; } - else if (dimension.equals("elevation")) { - try { - Double.parseDouble(value.getValue()); - } catch (NumberFormatException nfe){ - error(value, "invalidElevationReferenceValue", Collections.singletonMap("value", - (Object) value.getValue())); + } + + private boolean isValidTimeReference(String stringValue, Strategy strategy) { + try { + TimeParser parser = new TimeParser(); + List values = (List) parser.parse(stringValue); + // the KVP parser accepts also lists of values, we want a single one + if(strategy == Strategy.FIXED) { + // point or range, but just one + return values.size() == 1; + } else if(strategy == Strategy.NEAREST) { + // only point value, no ranges allowed + return values.size() == 1 && !(values.get(0) instanceof Range); + } else { + // nope, we cannot have a reference value if the strategy is + // not fixed or nearest + return false; + } + } catch (Exception e) { + if(LOGGER.isLoggable(Level.FINER)) { + LOGGER.log(Level.FINER, "Invalid time value " + stringValue, e); } + return false; } } diff --git a/src/wms/src/main/java/org/geoserver/wms/WMS.java b/src/wms/src/main/java/org/geoserver/wms/WMS.java index 75762e45954..b621e2a9efd 100644 --- a/src/wms/src/main/java/org/geoserver/wms/WMS.java +++ b/src/wms/src/main/java/org/geoserver/wms/WMS.java @@ -1,4 +1,4 @@ -/* (c) 2014 - 2015 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -1194,24 +1194,13 @@ public TreeSet getFeatureTypeElevations(FeatureTypeInfo typeInfo) throws return result; } - /** - * Returns the current time for the specified type info - * - * @param resourceInfo - * @return - * @deprecated this returns the default value for TIME dimension, which is not always "current" - */ - public Date getCurrentTime(ResourceInfo resourceInfo) { - return this.getDefaultTime(resourceInfo); - } - /** * Returns the default value for time dimension. * * @param resourceInfo * @return */ - public Date getDefaultTime(ResourceInfo resourceInfo) { + public Object getDefaultTime(ResourceInfo resourceInfo) { // check the time metadata DimensionInfo time = resourceInfo.getMetadata().get(ResourceInfo.TIME, DimensionInfo.class); if (time == null || !time.isEnabled()) { @@ -1230,15 +1219,26 @@ public Date getDefaultTime(ResourceInfo resourceInfo) { * @param resourceInfo * @return */ - public Double getDefaultElevation(ResourceInfo resourceInfo) { - DimensionInfo elevation = resourceInfo.getMetadata().get(ResourceInfo.ELEVATION, + public Object getDefaultElevation(ResourceInfo resourceInfo) { + DimensionInfo elevation = getDimensionInfo(resourceInfo, ResourceInfo.ELEVATION); + DimensionDefaultValueSelectionStrategy strategy = this.getDefaultValueStrategy(resourceInfo, ResourceInfo.ELEVATION, elevation); + return strategy.getDefaultValue(resourceInfo, ResourceInfo.ELEVATION, elevation, Double.class); + } + + /** + * Looks up the elevation configuration, throws an exception if not found + * @param resourceInfo + * @param dimensionName + * @return + */ + public DimensionInfo getDimensionInfo(ResourceInfo resourceInfo, String dimensionName) { + DimensionInfo info = resourceInfo.getMetadata().get(dimensionName, DimensionInfo.class); - if (elevation == null || !elevation.isEnabled()) { + if (info == null || !info.isEnabled()) { throw new ServiceException("Layer " + resourceInfo.prefixedName() - + " does not have elevation support enabled"); + + " does not have " + dimensionName + " support enabled"); } - DimensionDefaultValueSelectionStrategy strategy = this.getDefaultValueStrategy(resourceInfo, ResourceInfo.ELEVATION, elevation); - return strategy.getDefaultValue(resourceInfo, ResourceInfo.ELEVATION, elevation, Double.class); + return info; } /** @@ -1258,10 +1258,12 @@ public T getDefaultCustomDimensionValue(String dimensionName, ResourceInfo r + " does not have support enabled for dimension "+dimensionName); } DimensionDefaultValueSelectionStrategy strategy = this.getDefaultValueStrategy(resourceInfo, ResourceInfo.CUSTOM_DIMENSION_PREFIX+dimensionName, customDim); - return strategy.getDefaultValue(resourceInfo, ResourceInfo.CUSTOM_DIMENSION_PREFIX+dimensionName, customDim, clz); + // custom dimensions have no range support + return (T) strategy.getDefaultValue(resourceInfo, ResourceInfo.CUSTOM_DIMENSION_PREFIX+dimensionName, customDim, clz); } - DimensionDefaultValueSelectionStrategy getDefaultValueStrategy(ResourceInfo resource, + + public DimensionDefaultValueSelectionStrategy getDefaultValueStrategy(ResourceInfo resource, String dimensionName, DimensionInfo dimensionInfo){ if (defaultDimensionValueFactory != null) { return defaultDimensionValueFactory.getStrategy(resource, dimensionName, dimensionInfo); diff --git a/src/wms/src/main/java/org/geoserver/wms/capabilities/Capabilities_1_3_0_Transformer.java b/src/wms/src/main/java/org/geoserver/wms/capabilities/Capabilities_1_3_0_Transformer.java index 0b42bbe9a8b..78421093a42 100644 --- a/src/wms/src/main/java/org/geoserver/wms/capabilities/Capabilities_1_3_0_Transformer.java +++ b/src/wms/src/main/java/org/geoserver/wms/capabilities/Capabilities_1_3_0_Transformer.java @@ -881,7 +881,10 @@ private void doHandleLayer(LayerInfo layer) { commit(); } catch (Exception e) { // report what layer we failed on to help the admin locate and fix it + if (skipping) { + LOGGER.log(Level.WARNING, + "Error writing metadata; skipping layer: " + layer.getName(), e); reset(); } else { throw new ServiceException( diff --git a/src/wms/src/main/java/org/geoserver/wms/capabilities/DimensionHelper.java b/src/wms/src/main/java/org/geoserver/wms/capabilities/DimensionHelper.java index ca615373a49..c5e3318eb70 100644 --- a/src/wms/src/main/java/org/geoserver/wms/capabilities/DimensionHelper.java +++ b/src/wms/src/main/java/org/geoserver/wms/capabilities/DimensionHelper.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -25,13 +25,12 @@ import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.ResourceInfo; -import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.catalog.util.ReaderDimensionsAccessor; import org.geoserver.platform.ServiceException; import org.geoserver.util.ISO8601Formatter; import org.geoserver.wms.WMS; +import org.geoserver.wms.dimension.DimensionDefaultValueSelectionStrategy; import org.geotools.coverage.grid.io.GridCoverage2DReader; -import org.geotools.feature.type.DateUtil; import org.geotools.temporal.object.DefaultPeriodDuration; import org.geotools.util.Converters; import org.geotools.util.DateRange; @@ -221,22 +220,25 @@ void handleRasterLayerDimensions(final LayerInfo layer) throws RuntimeException, private void handleElevationDimensionRaster(CoverageInfo cvInfo, DimensionInfo elevInfo, ReaderDimensionsAccessor dimensions) throws IOException { TreeSet elevations = dimensions.getElevationDomain(); String elevationMetadata = getZDomainRepresentation(elevInfo, elevations); - Double defaultValue = wms.getDefaultElevation(cvInfo); - if (defaultValue == null){ - defaultValue = Double.valueOf(0d); - } + String defaultValue = getDefaultValueRepresentation(cvInfo, ResourceInfo.ELEVATION, "0"); writeElevationDimension(elevations, elevationMetadata, - elevInfo.getUnits(), elevInfo.getUnitSymbol(), defaultValue.doubleValue()); + elevInfo.getUnits(), elevInfo.getUnitSymbol(), defaultValue); + } + + private String getDefaultValueRepresentation(ResourceInfo resource, String dimensionName, String fallback) { + DimensionInfo dimensionInfo = wms.getDimensionInfo(resource, dimensionName); + DimensionDefaultValueSelectionStrategy strategy = wms.getDefaultValueStrategy(resource, dimensionName, dimensionInfo); + String defaultValue = strategy.getCapabilitiesRepresentation(resource, dimensionName, dimensionInfo); + if(defaultValue == null) { + defaultValue = fallback; + } + return defaultValue; } private void handleTimeDimensionRaster(CoverageInfo cvInfo, DimensionInfo timeInfo, ReaderDimensionsAccessor dimension) throws IOException { TreeSet temporalDomain = dimension.getTimeDomain(); String timeMetadata = getTemporalDomainRepresentation(timeInfo, temporalDomain); - Date defaultValue = null; - DimensionDefaultValueSetting defaultSetting = timeInfo.getDefaultValue(); - if ((defaultSetting != null) && !((defaultSetting.getStrategyType() == Strategy.NEAREST) && defaultSetting.getReferenceValue().equalsIgnoreCase(DimensionDefaultValueSetting.TIME_CURRENT))){ - defaultValue = wms.getDefaultTime(cvInfo); - } + String defaultValue = getDefaultValueRepresentation(cvInfo, ResourceInfo.TIME, DimensionDefaultValueSetting.TIME_CURRENT); writeTimeDimension(timeMetadata, defaultValue); } @@ -527,18 +529,14 @@ private void handleTimeDimensionVector(FeatureTypeInfo typeInfo) throws IOExcept // build the time dim representation TreeSet values = wms.getFeatureTypeTimes(typeInfo); String timeMetadata; - Date defaultValue = null; if (values != null && !values.isEmpty()) { DimensionInfo timeInfo = typeInfo.getMetadata().get(ResourceInfo.TIME, DimensionInfo.class); timeMetadata = getTemporalDomainRepresentation(timeInfo, values); - DimensionDefaultValueSetting defaultSetting = timeInfo.getDefaultValue(); - if ((defaultSetting != null) && !((defaultSetting.getStrategyType() == Strategy.NEAREST) && defaultSetting.getReferenceValue().equalsIgnoreCase(DimensionDefaultValueSetting.TIME_CURRENT))){ - defaultValue = wms.getDefaultTime(typeInfo); - } } else { timeMetadata = ""; } + String defaultValue = getDefaultValueRepresentation(typeInfo, ResourceInfo.TIME, DimensionDefaultValueSetting.TIME_CURRENT); writeTimeDimension(timeMetadata, defaultValue); } @@ -554,18 +552,14 @@ private void handleElevationDimensionVector(FeatureTypeInfo typeInfo) throws IOE } else { elevationMetadata = ""; } - Double defaultValue = wms.getDefaultElevation(typeInfo); - if (defaultValue == null){ - defaultValue = Double.valueOf(0d); - } - writeElevationDimension(elevations, elevationMetadata, units, unitSymbol, defaultValue.doubleValue()); + String defaultValue = getDefaultValueRepresentation(typeInfo, ResourceInfo.ELEVATION, "0"); + writeElevationDimension(elevations, elevationMetadata, units, unitSymbol, defaultValue); } - private void writeTimeDimension(String timeMetadata, Date defaultValue) { + private void writeTimeDimension(String timeMetadata, String defaultTimeStr) { AttributesImpl timeDim = new AttributesImpl(); - String defaultTimeStr = DimensionDefaultValueSetting.TIME_CURRENT; - if (defaultValue != null){ - defaultTimeStr = DateUtil.serializeDateTime(defaultValue.getTime(), true); + if(defaultTimeStr == null) { + defaultTimeStr = DimensionDefaultValueSetting.TIME_CURRENT; } if (mode == Mode.WMS11) { timeDim.addAttribute("", "name", "name", "", "time"); @@ -580,11 +574,11 @@ private void writeTimeDimension(String timeMetadata, Date defaultValue) { } private void writeElevationDimension(TreeSet elevations, final String elevationMetadata, - final String units, final String unitSymbol, double defaultValue) { + final String units, final String unitSymbol, String defaultValue) { if (mode == Mode.WMS11) { AttributesImpl elevDim = new AttributesImpl(); elevDim.addAttribute("", "name", "name", "", "elevation"); - elevDim.addAttribute("", "default", "default", "", Double.toString(defaultValue)); + elevDim.addAttribute("", "default", "default", "", defaultValue); element("Extent", elevationMetadata, elevDim); } else { writeElevationDimensionElement(elevationMetadata, defaultValue, @@ -592,7 +586,7 @@ private void writeElevationDimension(TreeSet elevations, final } } - private void writeElevationDimensionElement(final String elevationMetadata, final Double defaultValue, + private void writeElevationDimensionElement(final String elevationMetadata, final String defaultValue, final String units, final String unitSymbol) { AttributesImpl elevDim = new AttributesImpl(); String unitsNotNull = units; @@ -603,7 +597,7 @@ private void writeElevationDimensionElement(final String elevationMetadata, fina } elevDim.addAttribute("", "name", "name", "", "elevation"); if (defaultValue != null) { - elevDim.addAttribute("", "default", "default", "", Double.toString(defaultValue)); + elevDim.addAttribute("", "default", "default", "", defaultValue); } elevDim.addAttribute("", "units", "units", "", unitsNotNull); if (!"".equals(unitsNotNull) && !"".equals(unitSymNotNull)) { diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/AbstractDefaultValueSelectionStrategy.java b/src/wms/src/main/java/org/geoserver/wms/dimension/AbstractDefaultValueSelectionStrategy.java index ccf389043ba..9770470a14c 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/AbstractDefaultValueSelectionStrategy.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/AbstractDefaultValueSelectionStrategy.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -7,6 +7,7 @@ import java.util.Date; +import org.geoserver.catalog.DimensionDefaultValueSetting; import org.geoserver.catalog.DimensionInfo; import org.geoserver.catalog.ResourceInfo; import org.geotools.feature.type.DateUtil; @@ -24,18 +25,24 @@ public abstract class AbstractDefaultValueSelectionStrategy implements Dimension /** * Formats the dimension default value for the capabilities file * as ISO 8601 DateTime for TIME and as a number for ELEVATION. + * Assumes that getDefaultValue returns a single value, classes handling ranges have to override this method */ public String getCapabilitiesRepresentation(ResourceInfo resource, String dimensionName, DimensionInfo dimensionInfo) { String retval = null; if (dimensionName.equals(ResourceInfo.TIME)){ - Date dateValue = getDefaultValue(resource, dimensionName, dimensionInfo, Date.class); + Date dateValue = (Date) getDefaultValue(resource, dimensionName, dimensionInfo, Date.class); + if(dateValue == null) { + return DimensionDefaultValueSetting.TIME_CURRENT; + } retval = DateUtil.serializeDateTime(dateValue.getTime(), true); } else if (dimensionName.equals(ResourceInfo.ELEVATION)){ - Number numberValue = getDefaultValue(resource, dimensionName, dimensionInfo, Number.class); + Number numberValue = (Number) getDefaultValue(resource, dimensionName, dimensionInfo, Number.class); + if(numberValue == null) { + return "0"; + } retval = numberValue.toString(); - } - else { + } else { Object value = getDefaultValue(resource, dimensionName, dimensionInfo, Object.class); retval = value.toString(); } diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/DefaultFixedValueStrategyFactory.java b/src/wms/src/main/java/org/geoserver/wms/dimension/DefaultFixedValueStrategyFactory.java index 32465e84ca4..740ff595c90 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/DefaultFixedValueStrategyFactory.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/DefaultFixedValueStrategyFactory.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -27,4 +27,10 @@ public DimensionDefaultValueSelectionStrategy createFixedValueStrategy(Object va return new FixedValueStrategyImpl(value); } + @Override + public DimensionDefaultValueSelectionStrategy createFixedValueStrategy(Object value, + String fixedCapabilitiesValue) { + return new FixedValueStrategyImpl(value, fixedCapabilitiesValue); + } + } diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/DimensionDefaultValueSelectionStrategy.java b/src/wms/src/main/java/org/geoserver/wms/dimension/DimensionDefaultValueSelectionStrategy.java index 0e2e3f44deb..3debccd812b 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/DimensionDefaultValueSelectionStrategy.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/DimensionDefaultValueSelectionStrategy.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -17,9 +17,10 @@ public interface DimensionDefaultValueSelectionStrategy { /** - * Gets the actual value given the resource, the dimension, and the selected values for the already processed dimensions + * Gets the actual value given the resource, the dimension, and the selected values for the already processed dimensions. + * The default value returned will be either of the specified type clz, or if a range type, or type Range<clz> */ - public T getDefaultValue(ResourceInfo resource, String dimensionName, DimensionInfo dimension, Class clz); + public Object getDefaultValue(ResourceInfo resource, String dimensionName, DimensionInfo dimension, Class clz); /** * Returns the capabilities representation of the default value. For example, it could be "current" diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/FixedValueStrategyFactory.java b/src/wms/src/main/java/org/geoserver/wms/dimension/FixedValueStrategyFactory.java index 5aa721b5351..9212ec10660 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/FixedValueStrategyFactory.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/FixedValueStrategyFactory.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -21,5 +21,14 @@ public interface FixedValueStrategyFactory { * @return */ public DimensionDefaultValueSelectionStrategy createFixedValueStrategy(Object value); + + /** + * Returns a fixed default value strategy. + * + * @param value The value + * @param fixedCapabilitiesValue Its capabilities representation + * @return + */ + public DimensionDefaultValueSelectionStrategy createFixedValueStrategy(Object value, String fixedCapabilitiesValue); } diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageMaximumValueSelectionStrategyImpl.java b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageMaximumValueSelectionStrategyImpl.java index 659c1ffff7a..8dc3bcb80be 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageMaximumValueSelectionStrategyImpl.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageMaximumValueSelectionStrategyImpl.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -37,8 +37,8 @@ public CoverageMaximumValueSelectionStrategyImpl() { } @Override - public T getDefaultValue(ResourceInfo resource, String dimensionName, - DimensionInfo dimension, Class clz) { + public Object getDefaultValue(ResourceInfo resource, String dimensionName, + DimensionInfo dimension, Class clz) { Object retval = null; try { GridCoverage2DReader reader = (GridCoverage2DReader) ((CoverageInfo) resource) diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageMinimumValueSelectionStrategyImpl.java b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageMinimumValueSelectionStrategyImpl.java index 4a467aff78b..6fd5c5a1328 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageMinimumValueSelectionStrategyImpl.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageMinimumValueSelectionStrategyImpl.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -37,8 +37,8 @@ public CoverageMinimumValueSelectionStrategyImpl() { } @Override - public T getDefaultValue(ResourceInfo resource, String dimensionName, - DimensionInfo dimension, Class clz) { + public Object getDefaultValue(ResourceInfo resource, String dimensionName, + DimensionInfo dimension, Class clz) { Object retval = null; try { GridCoverage2DReader reader = (GridCoverage2DReader) ((CoverageInfo) resource) diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageNearestValueSelectionStrategyImpl.java b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageNearestValueSelectionStrategyImpl.java index 14431987518..5702c94cdfc 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageNearestValueSelectionStrategyImpl.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/CoverageNearestValueSelectionStrategyImpl.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -56,8 +56,8 @@ public CoverageNearestValueSelectionStrategyImpl(Object toMatch, String capabili @Override - public T getDefaultValue(ResourceInfo resource, String dimensionName, - DimensionInfo dimension, Class clz) { + public Object getDefaultValue(ResourceInfo resource, String dimensionName, + DimensionInfo dimension, Class clz) { Object retval = null; try { GridCoverage2DReader reader = (GridCoverage2DReader) ((CoverageInfo) resource) diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/DimensionDefaultValueSelectionStrategyFactoryImpl.java b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/DimensionDefaultValueSelectionStrategyFactoryImpl.java index 5c62e9e95e9..a3544b47dce 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/DimensionDefaultValueSelectionStrategyFactoryImpl.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/DimensionDefaultValueSelectionStrategyFactoryImpl.java @@ -1,10 +1,12 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms.dimension.impl; +import java.text.ParseException; +import java.util.Collection; import java.util.Date; import org.geoserver.catalog.CoverageInfo; @@ -13,6 +15,8 @@ import org.geoserver.catalog.DimensionInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.ResourceInfo; +import org.geoserver.ows.kvp.ElevationParser; +import org.geoserver.ows.kvp.TimeParser; import org.geoserver.platform.ServiceException; import org.geoserver.wms.dimension.DimensionDefaultValueSelectionStrategy; import org.geoserver.wms.dimension.DimensionDefaultValueSelectionStrategyFactory; @@ -67,6 +71,10 @@ public class DimensionDefaultValueSelectionStrategyFactoryImpl implements private NearestValueStrategyFactory coverageNearestValueStrategyFactory; private FixedValueStrategyFactory fixedValueStrategyFactory; + + private TimeParser timeParser = new TimeParser(); + + private ElevationParser elevationParser = new ElevationParser(); /** @@ -329,7 +337,7 @@ public FixedValueStrategyFactory getFixedValueStrategyFactory() { public void setFixedValueStrategyFactory(FixedValueStrategyFactory fixedValueStrategyFactory) { this.fixedValueStrategyFactory = fixedValueStrategyFactory; } - + private DimensionDefaultValueSelectionStrategy getStrategyFromSetting(ResourceInfo resource, String dimensionName, DimensionInfo dimensionInfo) { DimensionDefaultValueSelectionStrategy retval = null; @@ -349,13 +357,12 @@ private DimensionDefaultValueSelectionStrategy getStrategyFromSetting(ResourceIn private DimensionDefaultValueSelectionStrategy getDefaultTimeStrategy(ResourceInfo resource, DimensionDefaultValueSetting setting) { DimensionDefaultValueSelectionStrategy retval = null; - String referenceValue = null; Strategy getStrategyType = setting.getStrategyType(); switch (getStrategyType) { case NEAREST: { Date refDate; String capabilitiesValue = null; - referenceValue = setting.getReferenceValue(); + String referenceValue = setting.getReferenceValue(); if (referenceValue != null) { if (referenceValue.equalsIgnoreCase(DimensionDefaultValueSetting.TIME_CURRENT)) { refDate = new Date(); @@ -400,18 +407,18 @@ private DimensionDefaultValueSelectionStrategy getDefaultTimeStrategy(ResourceIn break; } case FIXED: { - Date refDate; - referenceValue = setting.getReferenceValue(); + Object refDate; + String referenceValue = setting.getReferenceValue(); if (referenceValue != null) { try { - refDate = new Date(DateUtil.parseDateTime(referenceValue)); - } catch (IllegalArgumentException e) { + refDate = singleValue(timeParser.parse(referenceValue), new Date()); + } catch (ParseException e) { throw new ServiceException( "Unable to parse time dimension default value reference '" + referenceValue - + "' as date, an ISO 8601 datetime format is expected", e); + + "' as date or a date range, an ISO 8601 datetime format is expected", e); } - retval = fixedValueStrategyFactory.createFixedValueStrategy(refDate); + retval = fixedValueStrategyFactory.createFixedValueStrategy(refDate, referenceValue); } else { throw new ServiceException( "No reference value given for time dimension default value 'fixed' strategy"); @@ -425,11 +432,10 @@ private DimensionDefaultValueSelectionStrategy getDefaultTimeStrategy(ResourceIn private DimensionDefaultValueSelectionStrategy getDefaultElevationStrategy(ResourceInfo resource, DimensionDefaultValueSetting setting) { DimensionDefaultValueSelectionStrategy retval = null; - String referenceValue = null; switch (setting.getStrategyType()) { case NEAREST: { Number refNumber; - referenceValue = setting.getReferenceValue(); + String referenceValue = setting.getReferenceValue(); if (referenceValue != null) { try { refNumber = Long.parseLong(referenceValue); @@ -472,31 +478,41 @@ private DimensionDefaultValueSelectionStrategy getDefaultElevationStrategy(Resou break; } case FIXED: { - Number refNumber; - referenceValue = setting.getReferenceValue(); + Object refNumber; + String referenceValue = setting.getReferenceValue(); if (referenceValue != null) { try { - refNumber = Long.parseLong(referenceValue); - } catch (NumberFormatException fne) { - try { - refNumber = Double.parseDouble(referenceValue); - } catch (NumberFormatException e) { - throw new ServiceException( - "Unable to parse elevation dimension default value reference '" - + referenceValue + "' as long or double", e); - } + refNumber = singleValue(elevationParser.parse(referenceValue), new Date()); + } catch (ParseException e) { + throw new ServiceException( + "Unable to parse elevation dimension default value reference '" + + referenceValue + "' as long or double", e); } } else { throw new ServiceException( "No reference value given for elevation dimension default value 'fixed' strategy"); } - retval = fixedValueStrategyFactory.createFixedValueStrategy(refNumber); + retval = fixedValueStrategyFactory.createFixedValueStrategy(refNumber, referenceValue); break; } } return retval; } + private Object singleValue(Collection parsed, Object defaultValue) { + Object result = null; + if(parsed.size() == 1) { + result = parsed.iterator().next(); + } else if(parsed.size() > 1) { + throw new IllegalArgumentException("Dimension reference value must be a single value or range"); + } + if(result == null) { + return defaultValue; + } else { + return result; + } + } + private DimensionDefaultValueSelectionStrategy getDefaultCustomDimensionStrategy(ResourceInfo resource, DimensionDefaultValueSetting setting) { DimensionDefaultValueSelectionStrategy retval = null; diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureMaximumValueSelectionStrategyImpl.java b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureMaximumValueSelectionStrategyImpl.java index 8322beaaef8..7438b820fa8 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureMaximumValueSelectionStrategyImpl.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureMaximumValueSelectionStrategyImpl.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -30,8 +30,8 @@ public FeatureMaximumValueSelectionStrategyImpl() { } @Override - public T getDefaultValue(ResourceInfo resource, String dimensionName, - DimensionInfo dimension, Class clz) { + public Object getDefaultValue(ResourceInfo resource, String dimensionName, + DimensionInfo dimension, Class clz) { final MaxVisitor max = new MaxVisitor(dimension.getAttribute()); CalcResult res = getCalculatedResult((FeatureTypeInfo) resource, dimension, max); if (res.equals(CalcResult.NULL_RESULT)) { diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureMinimumValueSelectionStrategyImpl.java b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureMinimumValueSelectionStrategyImpl.java index 4803646bea9..0fd233af21f 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureMinimumValueSelectionStrategyImpl.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureMinimumValueSelectionStrategyImpl.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -30,8 +30,8 @@ public FeatureMinimumValueSelectionStrategyImpl() { } @Override - public T getDefaultValue(ResourceInfo resource, String dimensionName, - DimensionInfo dimension, Class clz) { + public Object getDefaultValue(ResourceInfo resource, String dimensionName, + DimensionInfo dimension, Class clz) { final MinVisitor min = new MinVisitor(dimension.getAttribute()); CalcResult res = getCalculatedResult((FeatureTypeInfo) resource, dimension, min); if (res.equals(CalcResult.NULL_RESULT)) { diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureNearestValueSelectionStrategyImpl.java b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureNearestValueSelectionStrategyImpl.java index 4ef248119df..f55d250aab8 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureNearestValueSelectionStrategyImpl.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FeatureNearestValueSelectionStrategyImpl.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -49,8 +49,8 @@ public FeatureNearestValueSelectionStrategyImpl(Object toMatch, String capabilit } @Override - public T getDefaultValue(ResourceInfo resource, String dimensionName, - DimensionInfo dimension, Class clz) { + public Object getDefaultValue(ResourceInfo resource, String dimensionName, + DimensionInfo dimension, Class clz) { String attrName = dimension.getAttribute(); Class attrType = String.class; if (resource instanceof FeatureTypeInfo){ @@ -82,8 +82,7 @@ public T getDefaultValue(ResourceInfo resource, String dimensionName, public String getCapabilitiesRepresentation(ResourceInfo resource, String dimensionName, DimensionInfo dimensionInfo) { if (fixedCapabilitiesValue != null){ return this.fixedCapabilitiesValue; - } - else { + } else { return super.getCapabilitiesRepresentation(resource, dimensionName, dimensionInfo); } } diff --git a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FixedValueStrategyImpl.java b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FixedValueStrategyImpl.java index 1cc8098d889..237e689d9f3 100644 --- a/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FixedValueStrategyImpl.java +++ b/src/wms/src/main/java/org/geoserver/wms/dimension/impl/FixedValueStrategyImpl.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -9,6 +9,7 @@ import org.geoserver.catalog.ResourceInfo; import org.geoserver.wms.dimension.AbstractDefaultValueSelectionStrategy; import org.geotools.util.Converters; +import org.geotools.util.Range; /** * A default value strategy which always return the same fixed value. @@ -18,19 +19,46 @@ public class FixedValueStrategyImpl extends AbstractDefaultValueSelectionStrategy { private Object value; + private String fixedCapabilitiesValue; /** * Constructs a * @param value + * @param fixedCapabilitiesValue */ - public FixedValueStrategyImpl(Object value){ + public FixedValueStrategyImpl(Object value) { this.value = value; } + public FixedValueStrategyImpl(Object value, String fixedCapabilitiesValue) { + this.value = value; + this.fixedCapabilitiesValue = fixedCapabilitiesValue; + } + @Override - public T getDefaultValue(ResourceInfo resource, String dimensionName, - DimensionInfo dimension, Class clz) { - return Converters.convert(this.value, clz); + public Object getDefaultValue(ResourceInfo resource, String dimensionName, + DimensionInfo dimension, Class clz) { + if(value instanceof Range) { + Range r = (Range) value; + if(clz.isAssignableFrom(r.getElementClass())) { + return r; + } else { + Comparable min = (Comparable) Converters.convert(r.getMinValue(), clz); + Comparable max = (Comparable) Converters.convert(r.getMaxValue(), clz); + return new Range(clz, min, max); + } + } else { + return Converters.convert(this.value, clz); + } } - + + @Override + public String getCapabilitiesRepresentation(ResourceInfo resource, String dimensionName, DimensionInfo dimensionInfo) { + if (fixedCapabilitiesValue != null){ + return this.fixedCapabilitiesValue; + } else { + return super.getCapabilitiesRepresentation(resource, dimensionName, dimensionInfo); + } + } + } \ No newline at end of file diff --git a/src/wms/src/test/java/org/geoserver/wms/WMSDimensionsTestSupport.java b/src/wms/src/test/java/org/geoserver/wms/WMSDimensionsTestSupport.java index a804734ce2d..25039ee2644 100644 --- a/src/wms/src/test/java/org/geoserver/wms/WMSDimensionsTestSupport.java +++ b/src/wms/src/test/java/org/geoserver/wms/WMSDimensionsTestSupport.java @@ -1,10 +1,12 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms; +import static org.junit.Assert.assertEquals; + import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; @@ -15,9 +17,11 @@ import org.custommonkey.xmlunit.XMLUnit; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CoverageInfo; +import org.geoserver.catalog.DimensionDefaultValueSetting; import org.geoserver.catalog.DimensionInfo; import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.FeatureTypeInfo; +import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.impl.DimensionInfoImpl; import org.geoserver.config.GeoServerInfo; import org.geoserver.data.test.MockData; @@ -27,6 +31,9 @@ import org.junit.Before; public abstract class WMSDimensionsTestSupport extends WMSTestSupport { + + protected static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000; + protected static final long MILLIS_IN_MINUTE = 60000; protected QName V_TIME_ELEVATION = new QName(MockData.SF_URI, "TimeElevation", MockData.SF_PREFIX); protected QName V_TIME_ELEVATION_EMPTY = new QName(MockData.SF_URI, "TimeElevationEmpty", MockData.SF_PREFIX); @@ -145,5 +152,48 @@ protected void setupRasterDimension(QName layer, String metadata, info.getMetadata().put(metadata, di); getCatalog().save(info); } + + protected void setupResourceDimensionDefaultValue(QName name, String dimensionName, DimensionDefaultValueSetting defaultValue) { + ResourceInfo info = getCatalog().getResourceByName(name.getLocalPart(), ResourceInfo.class); + if (info == null){ + throw new RuntimeException("Unable to get resource by name "+name.getLocalPart()); + } + DimensionInfo di = new DimensionInfoImpl(); + di.setEnabled(true); + di.setPresentation(DimensionPresentation.LIST); + di.setDefaultValue(defaultValue); + info.getMetadata().put(dimensionName, di); + getCatalog().save(info); + } + + protected void setupResourceDimensionDefaultValue(QName name, String dimensionName, DimensionDefaultValueSetting defaultValue, String... startEndAttribute) { + ResourceInfo info = getCatalog().getResourceByName(name.getLocalPart(), ResourceInfo.class); + if (info == null){ + throw new RuntimeException("Unable to get resource by name "+name.getLocalPart()); + } + DimensionInfo di = new DimensionInfoImpl(); + di.setEnabled(true); + di.setPresentation(DimensionPresentation.LIST); + di.setDefaultValue(defaultValue); + if(startEndAttribute != null && startEndAttribute.length > 0) { + di.setAttribute(startEndAttribute[0]); + if(startEndAttribute.length > 1) { + di.setEndAttribute(startEndAttribute[1]); + } + } + info.getMetadata().put(dimensionName, di); + getCatalog().save(info); + } + + /** + * Checks two dates are the same, within a given tolerance. + * @param d1 + * @param d2 + * @param tolerance + */ + protected static void assertDateEquals(java.util.Date d1, java.util.Date d2, long tolerance) { + assertEquals(d1.getTime(), d2.getTime(), tolerance); + } + } diff --git a/src/wms/src/test/java/org/geoserver/wms/dimension/RasterElevationDimensionDefaultValueTest.java b/src/wms/src/test/java/org/geoserver/wms/dimension/RasterElevationDimensionDefaultValueTest.java index b8d7e30dfed..eca0c9e596e 100644 --- a/src/wms/src/test/java/org/geoserver/wms/dimension/RasterElevationDimensionDefaultValueTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/dimension/RasterElevationDimensionDefaultValueTest.java @@ -1,10 +1,11 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms.dimension; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Collections; @@ -15,6 +16,7 @@ import org.geoserver.catalog.DimensionDefaultValueSetting; import org.geoserver.catalog.DimensionInfo; import org.geoserver.catalog.DimensionPresentation; +import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.catalog.impl.DimensionInfoImpl; @@ -22,6 +24,7 @@ import org.geoserver.data.test.SystemTestData; import org.geoserver.wms.WMS; import org.geoserver.wms.WMSTestSupport; +import org.geotools.util.Range; import org.junit.Before; import org.junit.Test; @@ -59,7 +62,7 @@ public void testDefaultElevationCoverageSelector() throws Exception { CoverageInfo elevatedCoverage = getCatalog().getCoverageByName(WATTEMP.getLocalPart()); Double expected = Double.valueOf(0d); - Double e = wms.getDefaultElevation(elevatedCoverage); + Double e = (Double) wms.getDefaultElevation(elevatedCoverage); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the smallest one", Math.abs(e.doubleValue()-expected.doubleValue()) < 0.00001); @@ -77,7 +80,7 @@ public void testExplicitMinElevationCoverageSelector() throws Exception { CoverageInfo elevatedCoverage = getCatalog().getCoverageByName(WATTEMP.getLocalPart()); Double expected = Double.valueOf(0d); - Double e = wms.getDefaultElevation(elevatedCoverage); + Double e = (Double) wms.getDefaultElevation(elevatedCoverage); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the smallest one", Math.abs(e.doubleValue()-expected.doubleValue()) < 0.00001); } @@ -94,7 +97,7 @@ public void testExplicitMaxElevationCoverageSelector() throws Exception { CoverageInfo elevatedCoverage = getCatalog().getCoverageByName(WATTEMP.getLocalPart()); Double expected = Double.valueOf(100d); - Double e = wms.getDefaultElevation(elevatedCoverage); + Double e = (Double) wms.getDefaultElevation(elevatedCoverage); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the biggest one", Math.abs(e.doubleValue()-expected.doubleValue()) < 0.00001); } @@ -114,7 +117,7 @@ public void testExplicitFixedElevationCoverageSelector() throws Exception { CoverageInfo elevatedCoverage = getCatalog().getCoverageByName(WATTEMP.getLocalPart()); - Double e = wms.getDefaultElevation(elevatedCoverage); + Double e = (Double) wms.getDefaultElevation(elevatedCoverage); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the fixed one", Math.abs(e.doubleValue()-fixedElevation.doubleValue()) < 0.00001); } @@ -135,7 +138,7 @@ public void testExplicitNearestToGivenTimeCoverageSelector() throws Exception { CoverageInfo elevatedCoverage = getCatalog().getCoverageByName(WATTEMP.getLocalPart()); - Double e = wms.getDefaultElevation(elevatedCoverage); + Double e = (Double) wms.getDefaultElevation(elevatedCoverage); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the fixed one", Math.abs(e.doubleValue()-expected.doubleValue()) < 0.00001); @@ -157,10 +160,24 @@ public void testExplicitNearestToGivenTimeCoverageSelector2() throws Exception { CoverageInfo elevatedCoverage = getCatalog().getCoverageByName(WATTEMP.getLocalPart()); - Double e = wms.getDefaultElevation(elevatedCoverage); + Double e = (Double) wms.getDefaultElevation(elevatedCoverage); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the fixed one", Math.abs(e.doubleValue()-expected.doubleValue()) < 0.00001); } + + @Test + public void testFixedRangeElevation() throws Exception { + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("-100/0"); + setupCoverageElevationDimension(WATTEMP, defaultValueSetting); + + CoverageInfo elevatedCoverage = getCatalog().getCoverageByName(WATTEMP.getLocalPart()); + Range defaultRange = (Range) wms.getDefaultElevation(elevatedCoverage); + assertTrue("Default elevation is null", defaultRange != null); + assertEquals(-100, defaultRange.getMinValue(), 0d); + assertEquals(0, defaultRange.getMaxValue(), 0d); + } protected void setupCoverageElevationDimension(QName name, DimensionDefaultValueSetting defaultValue) { diff --git a/src/wms/src/test/java/org/geoserver/wms/dimension/RasterTimeDimensionDefaultValueTest.java b/src/wms/src/test/java/org/geoserver/wms/dimension/RasterTimeDimensionDefaultValueTest.java index b546b812575..3f6a363211f 100644 --- a/src/wms/src/test/java/org/geoserver/wms/dimension/RasterTimeDimensionDefaultValueTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/dimension/RasterTimeDimensionDefaultValueTest.java @@ -1,10 +1,11 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms.dimension; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; @@ -23,19 +24,16 @@ import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.CoverageStoreInfo; import org.geoserver.catalog.DimensionDefaultValueSetting; -import org.geoserver.catalog.DimensionInfo; -import org.geoserver.catalog.DimensionPresentation; -import org.geoserver.catalog.LayerInfo; -import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; +import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.PublishedType; -import org.geoserver.catalog.impl.DimensionInfoImpl; +import org.geoserver.catalog.ResourceInfo; import org.geoserver.data.test.MockData; import org.geoserver.data.test.SystemTestData; import org.geoserver.data.util.IOUtils; import org.geoserver.platform.GeoServerResourceLoader; import org.geoserver.wms.WMS; -import org.geoserver.wms.WMSTestSupport; +import org.geoserver.wms.WMSDimensionsTestSupport; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.GridCoverage2DReader; import org.geotools.coverage.grid.io.GridFormatFinder; @@ -44,6 +42,7 @@ import org.geotools.feature.type.DateUtil; import org.geotools.gce.imagemosaic.ImageMosaicFormat; import org.geotools.io.DefaultFileFilter; +import org.geotools.util.Range; import org.junit.Before; import org.junit.Test; @@ -52,7 +51,7 @@ * * @author Ilkka Rinne */ -public class RasterTimeDimensionDefaultValueTest extends WMSTestSupport { +public class RasterTimeDimensionDefaultValueTest extends WMSDimensionsTestSupport { static final QName WATTEMP_FUTURE = new QName(MockData.SF_URI, "watertemp_future_generated", MockData.SF_PREFIX); @@ -75,7 +74,7 @@ public void setup() throws Exception { @Test public void testDefaultTimeCoverageSelector() throws Exception { // Use default default value strategy: - setupCoverageTimeDimension(WATTEMP_FUTURE,null); + setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, null); Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); @@ -86,7 +85,7 @@ public void testDefaultTimeCoverageSelector() throws Exception { CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart()); - java.util.Date d = wms.getDefaultTime(coverage); + java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage); assertTrue("Returns a valid Default time", d != null); assertTrue("Default time should be the closest one", d.getTime() == todayMidnight); } @@ -97,7 +96,7 @@ public void testExplicitCurrentTimeCoverageSelector() throws Exception { DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.NEAREST); defaultValueSetting.setReferenceValue(DimensionDefaultValueSetting.TIME_CURRENT); - setupCoverageTimeDimension(WATTEMP_FUTURE,defaultValueSetting); + setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting); Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); @@ -108,24 +107,44 @@ public void testExplicitCurrentTimeCoverageSelector() throws Exception { CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart()); - java.util.Date d = wms.getDefaultTime(coverage); + java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage); assertTrue("Returns a valid Default time", d != null); assertTrue("Default time should be the closest one", d.getTime() == todayMidnight); } + @Test + public void testFixedTimeRange() throws Exception { + // Use explicit default value strategy: + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("P1M/PRESENT"); + setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting); + + CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart()); + + // the default is a single value, as we get the nearest to the range + java.util.Date curr = new java.util.Date(); + Range d = (Range) wms.getDefaultTime(coverage); + assertTrue("Returns a valid Default range", d != null); + // check "now" it's in the same minute... should work for even the slowest build server + assertDateEquals(curr, (java.util.Date) d.getMaxValue(), MILLIS_IN_MINUTE); + // the beginning + assertDateEquals(new Date(curr.getTime() - 30l * MILLIS_IN_DAY), (java.util.Date) d.getMinValue(), 60000); + } + @Test public void testExplicitMinTimeCoverageSelector() throws Exception { // Use explicit default value strategy: DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.MINIMUM); - setupCoverageTimeDimension(WATTEMP_FUTURE,defaultValueSetting); + setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting); //From src/test/resources/org/geoserver/wms/watertemp.zip: Date expected = Date.valueOf("2008-10-31"); CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart()); - java.util.Date d = wms.getDefaultTime(coverage); + java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage); assertTrue("Returns a valid Default time", d != null); assertTrue("Default time should be the smallest one", d.getTime() == expected.getTime()); } @@ -135,7 +154,7 @@ public void testExplicitMaxTimeCoverageSelector() throws Exception { // Use explicit default value strategy: DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.MAXIMUM); - setupCoverageTimeDimension(WATTEMP_FUTURE,defaultValueSetting); + setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting); Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); @@ -151,7 +170,7 @@ public void testExplicitMaxTimeCoverageSelector() throws Exception { CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart()); - java.util.Date d = wms.getDefaultTime(coverage); + java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage); assertTrue("Returns a valid Default time", d != null); assertTrue("Default time should be the biggest one", d.getTime() == oneYearInFuture); } @@ -163,13 +182,13 @@ public void testExplicitFixedTimeCoverageSelector() throws Exception { DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.FIXED); defaultValueSetting.setReferenceValue(fixedTimeStr); - setupCoverageTimeDimension(WATTEMP_FUTURE,defaultValueSetting); + setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting); long fixedTime = DateUtil.parseDateTime(fixedTimeStr); CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart()); - java.util.Date d = wms.getDefaultTime(coverage); + java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage); assertTrue("Returns a valid Default time", d != null); assertTrue("Default time should be the fixed one", d.getTime() == fixedTime); } @@ -181,32 +200,19 @@ public void testExplicitNearestToGivenTimeCoverageSelector() throws Exception { DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.NEAREST); defaultValueSetting.setReferenceValue(preferredTimeStr); - setupCoverageTimeDimension(WATTEMP_FUTURE,defaultValueSetting); + setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting); //From src/test/resources/org/geoserver/wms/watertemp.zip: Date expected = Date.valueOf("2008-11-01"); CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart()); - java.util.Date d = wms.getDefaultTime(coverage); + java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage); assertTrue("Returns a valid Default time", d != null); assertTrue("Default time should be the closest one", d.getTime() == expected.getTime()); } - protected void setupCoverageTimeDimension(QName name, DimensionDefaultValueSetting defaultValue) { - CoverageInfo info = getCatalog().getCoverageByName(name.getLocalPart()); - if (info == null){ - throw new RuntimeException("Unable to get coverage by name "+name.getLocalPart()); - } - DimensionInfo di = new DimensionInfoImpl(); - di.setEnabled(true); - di.setPresentation(DimensionPresentation.LIST); - di.setDefaultValue(defaultValue); - info.getMetadata().put(ResourceInfo.TIME, di); - getCatalog().save(info); - } - private void prepareFutureCoverageData(QName coverageName) throws IOException { SimpleDateFormat tsFormatter = new SimpleDateFormat("yyyyMMdd"); @@ -378,4 +384,5 @@ private void addRasterLayerFromDataDir(QName qName, Catalog catalog) throws IOEx } } } + } diff --git a/src/wms/src/test/java/org/geoserver/wms/dimension/VectorElevationDimensionDefaultValueTest.java b/src/wms/src/test/java/org/geoserver/wms/dimension/VectorElevationDimensionDefaultValueTest.java index 2ae9da56cc0..186dbc5f12b 100644 --- a/src/wms/src/test/java/org/geoserver/wms/dimension/VectorElevationDimensionDefaultValueTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/dimension/VectorElevationDimensionDefaultValueTest.java @@ -1,11 +1,11 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms.dimension; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.IOException; import java.sql.Date; @@ -14,11 +14,11 @@ import javax.xml.namespace.QName; import org.geoserver.catalog.DimensionDefaultValueSetting; +import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.catalog.DimensionInfo; import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.ResourceInfo; -import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.catalog.impl.DimensionInfoImpl; import org.geoserver.data.test.MockData; import org.geoserver.data.test.SystemTestData; @@ -27,6 +27,7 @@ import org.geotools.data.DataUtilities; import org.geotools.data.FeatureStore; import org.geotools.data.memory.MemoryFeatureCollection; +import org.geotools.util.Range; import org.junit.Before; import org.junit.Test; import org.opengis.feature.simple.SimpleFeature; @@ -72,13 +73,13 @@ public void testExplicitMinElevationVectorSelector() throws Exception { ELEVATION_WITH_START_END.getLocalPart()); Double originallySmallest = Double.valueOf(1d); - Double e = wms.getDefaultElevation(elevationWithStartEnd); + Double e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the smallest one", Math.abs(e.doubleValue()-originallySmallest.doubleValue()) < 0.00001); addFeatureWithElevation(fid++, 10d); - e = wms.getDefaultElevation(elevationWithStartEnd); + e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the smallest one", Math.abs(e.doubleValue()-originallySmallest.doubleValue()) < 0.00001); @@ -86,7 +87,7 @@ public void testExplicitMinElevationVectorSelector() throws Exception { addFeatureWithElevation(fid++, smaller.doubleValue()); - e = wms.getDefaultElevation(elevationWithStartEnd); + e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the smallest one", Math.abs(e.doubleValue()-smaller.doubleValue()) < 0.00001); @@ -107,7 +108,7 @@ public void testExplicitMaxElevationVectorSelector() throws Exception { ELEVATION_WITH_START_END.getLocalPart()); Double originallyBiggest = Double.valueOf(2d); - Double e = wms.getDefaultElevation(elevationWithStartEnd); + Double e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the biggest one", Math.abs(e.doubleValue()-originallyBiggest.doubleValue()) < 0.00001); @@ -115,7 +116,7 @@ public void testExplicitMaxElevationVectorSelector() throws Exception { addFeatureWithElevation(fid++, smaller.doubleValue()); - e = wms.getDefaultElevation(elevationWithStartEnd); + e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the biggest one", Math.abs(e.doubleValue()-originallyBiggest.doubleValue()) < 0.00001); @@ -123,7 +124,7 @@ public void testExplicitMaxElevationVectorSelector() throws Exception { addFeatureWithElevation(fid++, bigger.doubleValue()); - e = wms.getDefaultElevation(elevationWithStartEnd); + e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the biggest one", Math.abs(e.doubleValue()-bigger.doubleValue()) < 0.00001); @@ -149,7 +150,7 @@ public void testExplicitFixedElevationVectorSelector() throws Exception { ELEVATION_WITH_START_END.getLocalPart()); Double originallyBiggest = Double.valueOf(3d); - Double e = wms.getDefaultElevation(elevationWithStartEnd); + Double e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the fixed one", Math.abs(e.doubleValue()-fixedElevation.doubleValue()) < 0.00001); @@ -157,7 +158,7 @@ public void testExplicitFixedElevationVectorSelector() throws Exception { addFeatureWithElevation(fid++, smaller.doubleValue()); - e = wms.getDefaultElevation(elevationWithStartEnd); + e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the fixed one", Math.abs(e.doubleValue()-fixedElevation.doubleValue()) < 0.00001); @@ -165,7 +166,7 @@ public void testExplicitFixedElevationVectorSelector() throws Exception { addFeatureWithElevation(fid++, bigger.doubleValue()); - e = wms.getDefaultElevation(elevationWithStartEnd); + e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the fixed one", Math.abs(e.doubleValue()-fixedElevation.doubleValue()) < 0.00001); } @@ -187,22 +188,37 @@ public void testExplicitNearestToGivenElevationVectorSelector() throws Exception ELEVATION_WITH_START_END.getLocalPart()); Double expected = Double.valueOf(2d); - Double e = wms.getDefaultElevation(elevationWithStartEnd); + Double e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the nearest one to "+referenceElevation.doubleValue(), Math.abs(e.doubleValue()-expected.doubleValue()) < 0.00001); expected = Double.valueOf(1.8d); addFeatureWithElevation(fid++, expected); - e = wms.getDefaultElevation(elevationWithStartEnd); + e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the nearest one to "+referenceElevation.doubleValue(), Math.abs(e.doubleValue()-expected.doubleValue()) < 0.00001); addFeatureWithElevation(fid++, 1.3d); - e = wms.getDefaultElevation(elevationWithStartEnd); + e = (Double) wms.getDefaultElevation(elevationWithStartEnd); assertTrue("Default elevation is null", e != null); assertTrue("Default elevation should be the nearest one to "+referenceElevation.doubleValue(), Math.abs(e.doubleValue()-expected.doubleValue()) < 0.00001); } + + @Test + public void testFixedRangeElevation() throws Exception { + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("-100/0"); + setupFeatureElevationDimension(defaultValueSetting); + + FeatureTypeInfo elevationWithStartEnd = getCatalog().getFeatureTypeByName( + ELEVATION_WITH_START_END.getLocalPart()); + Range defaultRange = (Range) wms.getDefaultElevation(elevationWithStartEnd); + assertTrue("Default elevation is null", defaultRange != null); + assertEquals(-100, defaultRange.getMinValue(), 0d); + assertEquals(0, defaultRange.getMaxValue(), 0d); + } protected void setupFeatureElevationDimension(DimensionDefaultValueSetting defaultValue) { FeatureTypeInfo info = getCatalog() diff --git a/src/wms/src/test/java/org/geoserver/wms/dimension/VectorTimeDimensionDefaultValueTest.java b/src/wms/src/test/java/org/geoserver/wms/dimension/VectorTimeDimensionDefaultValueTest.java index 2e82417854a..7bf857b92bd 100644 --- a/src/wms/src/test/java/org/geoserver/wms/dimension/VectorTimeDimensionDefaultValueTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/dimension/VectorTimeDimensionDefaultValueTest.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -15,6 +15,7 @@ import javax.xml.namespace.QName; +import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.DimensionDefaultValueSetting; import org.geoserver.catalog.DimensionInfo; import org.geoserver.catalog.DimensionPresentation; @@ -25,11 +26,13 @@ import org.geoserver.data.test.MockData; import org.geoserver.data.test.SystemTestData; import org.geoserver.wms.WMS; +import org.geoserver.wms.WMSDimensionsTestSupport; import org.geoserver.wms.WMSTestSupport; import org.geotools.data.DataUtilities; import org.geotools.data.FeatureStore; import org.geotools.data.memory.MemoryFeatureCollection; import org.geotools.feature.type.DateUtil; +import org.geotools.util.Range; import org.junit.Before; import org.junit.Test; import org.opengis.feature.simple.SimpleFeature; @@ -41,7 +44,7 @@ * * @author Ilkka Rinne */ -public class VectorTimeDimensionDefaultValueTest extends WMSTestSupport { +public class VectorTimeDimensionDefaultValueTest extends WMSDimensionsTestSupport { static final QName TIME_WITH_START_END = new QName(MockData.SF_URI, "TimeWithStartEnd", MockData.SF_PREFIX); @@ -74,7 +77,7 @@ public void testDefaultTimeVectorSelector() throws Exception { Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo,Double.valueOf(0d)); - java.util.Date d = wms.getDefaultTime(timeWithStartEnd); + java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == twoDaysAgo.getTime()); @@ -82,13 +85,13 @@ public void testDefaultTimeVectorSelector() throws Exception { Date dayAfterTomorrow = addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == dayAfterTomorrow.getTime()); Date todayMidnight = addFeatureWithTimeTodayMidnight(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == todayMidnight .getTime()); @@ -111,7 +114,7 @@ public void testExplicitCurrentTimeVectorSelector() throws Exception { Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo,Double.valueOf(0d)); - java.util.Date d = wms.getDefaultTime(timeWithStartEnd); + java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == twoDaysAgo.getTime()); @@ -119,13 +122,13 @@ public void testExplicitCurrentTimeVectorSelector() throws Exception { Date dayAfterTomorrow = addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == dayAfterTomorrow.getTime()); Date todayMidnight = addFeatureWithTimeTodayMidnight(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == todayMidnight .getTime()); @@ -148,7 +151,7 @@ public void testExplicitMinTimeVectorSelector() throws Exception { Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo,Double.valueOf(0d)); - java.util.Date d = wms.getDefaultTime(timeWithStartEnd); + java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the smallest one", d.getTime() == smallest.getTime()); @@ -156,13 +159,13 @@ public void testExplicitMinTimeVectorSelector() throws Exception { addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the smallest one", d.getTime() == smallest.getTime()); addFeatureWithTimeTodayMidnight(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the smallest one", d.getTime() == smallest .getTime()); @@ -184,7 +187,7 @@ public void testExplicitMaxTimeVectorSelector() throws Exception { Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo, Double.valueOf(0d)); - java.util.Date d = wms.getDefaultTime(timeWithStartEnd); + java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the biggest one", d.getTime() == twoDaysAgo.getTime()); @@ -192,13 +195,13 @@ public void testExplicitMaxTimeVectorSelector() throws Exception { addFeatureWithTimeDayAfterTomorrow(fid++); Date oneYearFromNow = addFeatureWithTimeOneYearFromNow(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the biggest one", d.getTime() == oneYearFromNow.getTime()); addFeatureWithTimeTodayMidnight(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the biggest one", d.getTime() == oneYearFromNow .getTime()); @@ -224,7 +227,7 @@ public void testExplicitFixedTimeVectorSelector() throws Exception { Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo,Double.valueOf(0d)); - java.util.Date d = wms.getDefaultTime(timeWithStartEnd); + java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the fixed one", d.getTime() == fixedTime); @@ -232,17 +235,39 @@ public void testExplicitFixedTimeVectorSelector() throws Exception { addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the fixed one", d.getTime() == fixedTime); addFeatureWithTimeTodayMidnight(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the fixed one", d.getTime() == fixedTime); } + @Test + public void testFixedRange() throws Exception { + // Use explicit default value DimensionInfo setup: + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("P1M/PRESENT"); + setupFeatureTimeDimension(defaultValueSetting); + + FeatureTypeInfo timeWithStartEnd = getCatalog().getFeatureTypeByName( + TIME_WITH_START_END.getLocalPart()); + + // the default should be the range we requested + java.util.Date curr = new java.util.Date(); + Range d = (Range) wms.getDefaultTime(timeWithStartEnd); + assertTrue("Returns a valid Default range", d != null); + // check "now" it's in the same minute... should work for even the slowest build server + assertDateEquals(curr, (java.util.Date) d.getMaxValue(), MILLIS_IN_MINUTE); + // the beginning + assertDateEquals(new Date(curr.getTime() - 30l * MILLIS_IN_DAY), (java.util.Date) d.getMinValue(), 60000); + } + + @Test public void testExplicitNearestToGivenTimeVectorSelector() throws Exception { int fid = 1000; @@ -264,7 +289,7 @@ public void testExplicitNearestToGivenTimeVectorSelector() throws Exception { Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo, Double.valueOf(0d)); - java.util.Date d = wms.getDefaultTime(timeWithStartEnd); + java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == expected.getTime()); @@ -272,13 +297,13 @@ public void testExplicitNearestToGivenTimeVectorSelector() throws Exception { addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == expected.getTime()); addFeatureWithTimeTodayMidnight(fid++); - d = wms.getDefaultTime(timeWithStartEnd); + d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == expected.getTime()); } diff --git a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterCapabilitiesTest.java b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterCapabilitiesTest.java index 3007da2df7a..f2d9039a91c 100644 --- a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterCapabilitiesTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterCapabilitiesTest.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -15,6 +15,7 @@ import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.ResourceInfo; +import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.catalog.impl.LayerGroupInfoImpl; import org.geoserver.wms.WMSDimensionsTestSupport; import org.junit.Test; @@ -122,6 +123,21 @@ public void testElevationDiscreteManualResolution() throws Exception { assertXpathEvaluatesTo("0.0/100.0/10.0", "//Layer/Extent", dom); } + @Test + public void testDefaultTimeRangeFixed() throws Exception { + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("P1M/PRESENT"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.TIME, defaultValueSetting); + + Document dom = dom(get("wms?request=getCapabilities&version=1.1.1"), false); + print(dom); + + assertXpathEvaluatesTo("1", "count(//Layer/Dimension)", dom); + assertXpathEvaluatesTo("time", "//Layer/Dimension/@name", dom); + assertXpathEvaluatesTo("P1M/PRESENT", "//Layer/Extent/@default", dom); + } + @Test public void testTimeList() throws Exception { setupRasterDimension(WATTEMP, ResourceInfo.TIME, DimensionPresentation.LIST, null, null, null); @@ -216,7 +232,7 @@ public void testTimeRangeList() throws Exception { setupRasterDimension(TIMERANGES, ResourceInfo.TIME, DimensionPresentation.LIST, null, null, null); Document dom = dom(get("wms?request=getCapabilities&version=1.1.1"), false); - print(dom); + // print(dom); // check dimension has been declared assertXpathEvaluatesTo("1", "count(//Layer/Dimension)", dom); diff --git a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterGetFeatureInfoTest.java b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterGetFeatureInfoTest.java index 30808e109b8..f58407f0b84 100644 --- a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterGetFeatureInfoTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterGetFeatureInfoTest.java @@ -1,11 +1,13 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms.wms_1_1_1; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import java.awt.Color; import java.awt.image.BufferedImage; @@ -13,8 +15,10 @@ import org.custommonkey.xmlunit.XMLUnit; import org.custommonkey.xmlunit.XpathEngine; +import org.geoserver.catalog.DimensionDefaultValueSetting; import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.ResourceInfo; +import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.wms.WMSDimensionsTestSupport; import org.junit.Before; import org.junit.Test; @@ -138,5 +142,58 @@ public void testTimeRange() throws Exception { assertEquals(20.027, getFeatureAt(url, 68, 72, layer), EPS); } + @Test + public void testTimeDefaultAsRange() throws Exception { + setupRasterDimension(WATTEMP, ResourceInfo.ELEVATION, DimensionPresentation.LIST, null, UNITS, UNIT_SYMBOL); + // setup a default + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("2008-10-30T23:00:00.000Z/2008-10-31T01:00:00.000Z"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.TIME, defaultValueSetting); + + // use the default time range, specify elevation + String url = BASE_URL + "&elevation=100"; + // this one should be the no-data + assertEquals(-30000, getFeatureAt(url, 36, 31, "sf:watertemp"), EPS); + // and this one should be medium + assertEquals(14.134, getFeatureAt(url, 68, 72, "sf:watertemp"), EPS); + } + + @Test + public void testElevationDefaultAsRange() throws Exception { + setupRasterDimension(WATTEMP, ResourceInfo.TIME, DimensionPresentation.LIST, null, null, null); + // setup a default + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("99/101"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.ELEVATION, defaultValueSetting); + + // default elevation, specific time + String url = BASE_URL + "&time=2008-10-31T00:00:00.000Z"; + // this one should be the no-data + assertEquals(-30000, getFeatureAt(url, 36, 31, "sf:watertemp"), EPS); + // and this one should be medium + assertEquals(14.134, getFeatureAt(url, 68, 72, "sf:watertemp"), EPS); + } + @Test + public void testTimeElevationDefaultAsRange() throws Exception { + // setup a range default for time + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("2008-10-30T23:00:00.000Z/2008-10-31T01:00:00.000Z"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.TIME, defaultValueSetting); + // setup a range default for elevation + defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("99/101"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.ELEVATION, defaultValueSetting); + + // default elevation, default time + String url = BASE_URL; + // this one should be the no-data + assertEquals(-30000, getFeatureAt(url, 36, 31, "sf:watertemp"), EPS); + // and this one should be medium + assertEquals(14.134, getFeatureAt(url, 68, 72, "sf:watertemp"), EPS); + } } diff --git a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterGetMapTest.java b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterGetMapTest.java index c26dda71e1d..c75f16cef00 100644 --- a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterGetMapTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsRasterGetMapTest.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -8,12 +8,10 @@ import java.awt.Color; import java.awt.image.BufferedImage; -import javax.swing.ImageIcon; -import javax.swing.JFrame; -import javax.swing.JLabel; - +import org.geoserver.catalog.DimensionDefaultValueSetting; import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.ResourceInfo; +import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.wms.WMSDimensionsTestSupport; import org.junit.Test; @@ -125,4 +123,63 @@ public void testTimeRange() throws Exception { assertPixel(image, 68, 72, new Color(255, 170, 170)); } + @Test + public void testTimeDefaultAsRange() throws Exception { + setupRasterDimension(WATTEMP, ResourceInfo.ELEVATION, DimensionPresentation.LIST, null, UNITS, UNIT_SYMBOL); + // setup a default + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("2008-10-30T23:00:00.000Z/2008-10-31T01:00:00.000Z"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.TIME, defaultValueSetting); + + // default time, specific elevation + // BufferedImage image = getAsImage(BASE_URL + "&time=2008-10-31T00:00:00.000Z&elevation=100", "image/png"); + BufferedImage image = getAsImage(BASE_URL + "&elevation=100", "image/png"); + + // at this elevation the pixel is black + assertPixel(image, 36, 31, new Color(0,0,0)); + // and this one a light blue, but slightly darker than before + assertPixel(image, 68, 72, new Color(240, 240, 255)); + } + + @Test + public void testElevationDefaultAsRange() throws Exception { + setupRasterDimension(WATTEMP, ResourceInfo.TIME, DimensionPresentation.LIST, null, null, null); + // setup a default + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("99/101"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.ELEVATION, defaultValueSetting); + + // default elevation, specific time + BufferedImage image = getAsImage(BASE_URL + "&time=2008-10-31T00:00:00.000Z", "image/png"); + + // at this elevation the pixel is black + assertPixel(image, 36, 31, new Color(0,0,0)); + // and this one a light blue, but slightly darker than before + assertPixel(image, 68, 72, new Color(240, 240, 255)); + } + + @Test + public void testTimeElevationDefaultAsRange() throws Exception { + // setup a range default for time + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("2008-10-30T23:00:00.000Z/2008-10-31T01:00:00.000Z"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.TIME, defaultValueSetting); + // setup a range default for elevation + defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("99/101"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.ELEVATION, defaultValueSetting); + + // use defaults for both time and elevation + BufferedImage image = getAsImage(BASE_URL, "image/png"); + + // at this elevation the pixel is black + assertPixel(image, 36, 31, new Color(0,0,0)); + // and this one a light blue, but slightly darker than before + assertPixel(image, 68, 72, new Color(240, 240, 255)); + } + } diff --git a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorCapabilitiesTest.java b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorCapabilitiesTest.java index 1a86ec1ad84..1bd3442ebb0 100644 --- a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorCapabilitiesTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorCapabilitiesTest.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -16,6 +16,7 @@ import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.ResourceInfo; +import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.catalog.impl.LayerGroupInfoImpl; import org.geoserver.wms.WMSDimensionsTestSupport; import org.junit.Test; @@ -77,7 +78,7 @@ void checkEmptyElevationDimensionAndExtent() throws Exception { // check we have the extent assertXpathEvaluatesTo("1", "count(//Layer/Extent)", dom); assertXpathEvaluatesTo("elevation", "//Layer/Extent/@name", dom); - assertXpathEvaluatesTo("0.0", "//Layer/Extent/@default", dom); + assertXpathEvaluatesTo("0", "//Layer/Extent/@default", dom); // and that it is empty assertXpathEvaluatesTo("", "//Layer/Extent", dom); } @@ -269,4 +270,35 @@ public void testTimeContinuousInEarthObservationRootLayer() throws Exception { getCatalog().remove(eoProduct); } } + + @Test + public void testDefaultTimeRangeFixed() throws Exception { + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("P1M/PRESENT"); + setupResourceDimensionDefaultValue(V_TIME_ELEVATION, ResourceInfo.TIME, defaultValueSetting); + + Document dom = dom(get("wms?request=getCapabilities&version=1.1.1"), false); + // print(dom); + + assertXpathEvaluatesTo("1", "count(//Layer/Dimension)", dom); + assertXpathEvaluatesTo("time", "//Layer/Dimension/@name", dom); + assertXpathEvaluatesTo("P1M/PRESENT", "//Layer/Extent/@default", dom); + } + + @Test + public void testDefaultElevationRangeFixed() throws Exception { + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("-100/0"); + setupResourceDimensionDefaultValue(V_TIME_ELEVATION, ResourceInfo.ELEVATION, defaultValueSetting); + + Document dom = dom(get("wms?request=getCapabilities&version=1.1.1"), false); + // print(dom); + + assertXpathEvaluatesTo("1", "count(//Layer/Dimension)", dom); + assertXpathEvaluatesTo("elevation", "//Layer/Dimension/@name", dom); + assertXpathEvaluatesTo("-100/0", "//Layer/Extent/@default", dom); + } + } diff --git a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorGetFeatureInfoTest.java b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorGetFeatureInfoTest.java index 6eab88d2b1a..f6332b16656 100644 --- a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorGetFeatureInfoTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorGetFeatureInfoTest.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -6,12 +6,17 @@ package org.geoserver.wms.wms_1_1_1; import static org.junit.Assert.*; + +import java.awt.Color; +import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import org.custommonkey.xmlunit.XMLUnit; import org.custommonkey.xmlunit.XpathEngine; +import org.geoserver.catalog.DimensionDefaultValueSetting; import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.ResourceInfo; +import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.wms.WMSDimensionsTestSupport; import org.junit.Before; import org.junit.Ignore; @@ -240,5 +245,36 @@ public void testTimeIntervalResolution() throws Exception { assertEquals("TimeElevation.2", getFeatureAt(base, 20, 30)); assertNull(getFeatureAt(base, 60, 30)); } + + @Test + public void testElevationDefaultAsRange() throws Exception { + // setup a default + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("1/3"); + setupResourceDimensionDefaultValue(V_TIME_ELEVATION, ResourceInfo.ELEVATION, defaultValueSetting, "elevation"); + + // the last three show up, the first does not + assertNull(getFeatureAt(baseFeatureInfo, 20, 10)); + assertEquals("TimeElevation.1", getFeatureAt(baseFeatureInfo, 60, 10)); + assertEquals("TimeElevation.2", getFeatureAt(baseFeatureInfo, 20, 30)); + assertEquals("TimeElevation.3", getFeatureAt(baseFeatureInfo, 60, 30)); + } + + + @Test + public void testTimeDefaultAsRange() throws Exception { + // setup a default + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("2011-05-02/2011-05-03"); + setupResourceDimensionDefaultValue(V_TIME_ELEVATION, ResourceInfo.TIME, defaultValueSetting, "time"); + + // the last three show up, the first does not + assertNull(getFeatureAt(baseFeatureInfo, 20, 10)); + assertEquals("TimeElevation.1", getFeatureAt(baseFeatureInfo, 60, 10)); + assertEquals("TimeElevation.2", getFeatureAt(baseFeatureInfo, 20, 30)); + assertNull(getFeatureAt(baseFeatureInfo, 60, 30)); + } } diff --git a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorGetMapTest.java b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorGetMapTest.java index ad5071668d9..84eb5ff9ea5 100644 --- a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorGetMapTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/DimensionsVectorGetMapTest.java @@ -1,25 +1,25 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms.wms_1_1_1; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.awt.Color; import java.awt.Graphics2D; -import java.awt.Transparency; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; -import java.awt.image.IndexColorModel; import java.io.ByteArrayInputStream; -import java.io.File; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; +import org.geoserver.catalog.DimensionDefaultValueSetting; +import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.ResourceInfo; import org.geoserver.wms.WMSDimensionsTestSupport; @@ -368,5 +368,49 @@ public void testTimeIntervalResolution() throws Exception { assertPixel(image, 20, 30, Color.BLACK); assertPixel(image, 60, 30, Color.WHITE); } + + @Test + public void testElevationDefaultAsRange() throws Exception { + // setup a default + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("1/3"); + setupResourceDimensionDefaultValue(V_TIME_ELEVATION, ResourceInfo.ELEVATION, defaultValueSetting, "elevation"); + + // request with default values + BufferedImage image = getAsImage("wms?service=WMS&version=1.1.1&request=GetMap" + + "&bbox=-180,-90,180,90&styles=&Format=image/png&width=80&height=40&srs=EPSG:4326" + + "&layers=" + getLayerId(V_TIME_ELEVATION), "image/png"); + + // RenderedImageBrowser.showChain(image); + + // the last three show up, the first does not + assertPixel(image, 20, 10, Color.WHITE); + assertPixel(image, 60, 10, Color.BLACK); + assertPixel(image, 20, 30, Color.BLACK); + assertPixel(image, 60, 30, Color.BLACK); + } + + @Test + public void testTimeDefaultAsRange() throws Exception { + // setup a default + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("2011-05-02/2011-05-03"); + setupResourceDimensionDefaultValue(V_TIME_ELEVATION, ResourceInfo.TIME, defaultValueSetting, "time"); + + // request with default values + BufferedImage image = getAsImage("wms?service=WMS&version=1.1.1&request=GetMap" + + "&bbox=-180,-90,180,90&styles=&Format=image/png&width=80&height=40&srs=EPSG:4326" + + "&layers=" + getLayerId(V_TIME_ELEVATION), "image/png"); + + // RenderedImageBrowser.showChain(image); + + // the last three show up, the first does not + assertPixel(image, 20, 10, Color.WHITE); + assertPixel(image, 60, 10, Color.BLACK); + assertPixel(image, 20, 30, Color.BLACK); + assertPixel(image, 60, 30, Color.WHITE); + } } diff --git a/src/wms/src/test/java/org/geoserver/wms/wms_1_3/DimensionsRasterCapabilitiesTest.java b/src/wms/src/test/java/org/geoserver/wms/wms_1_3/DimensionsRasterCapabilitiesTest.java index cacd2bfa769..64f2ddb3f9c 100644 --- a/src/wms/src/test/java/org/geoserver/wms/wms_1_3/DimensionsRasterCapabilitiesTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/wms_1_3/DimensionsRasterCapabilitiesTest.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -11,6 +11,7 @@ import org.geoserver.catalog.DimensionInfo; import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.ResourceInfo; +import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.wms.WMSDimensionsTestSupport; import org.junit.Test; import org.w3c.dom.Document; @@ -161,5 +162,20 @@ public void testTimeResolution() throws Exception { assertXpathEvaluatesTo("2008-10-31T00:00:00.000Z/2008-11-01T00:00:00.000Z/PT12H", "//wms:Layer/wms:Dimension", dom); } + @Test + public void testDefaultTimeRangeFixed() throws Exception { + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("P1M/PRESENT"); + setupResourceDimensionDefaultValue(WATTEMP, ResourceInfo.TIME, defaultValueSetting); + + Document dom = dom(get("wms?request=getCapabilities&version=1.3.0"), false); + print(dom); + + assertXpathEvaluatesTo("1", "count(//wms:Layer/wms:Dimension)", dom); + assertXpathEvaluatesTo("time", "//wms:Layer/wms:Dimension/@name", dom); + assertXpathEvaluatesTo("P1M/PRESENT", "//wms:Layer/wms:Dimension/@default", dom); + } + } diff --git a/src/wms/src/test/java/org/geoserver/wms/wms_1_3/DimensionsVectorCapabilitiesTest.java b/src/wms/src/test/java/org/geoserver/wms/wms_1_3/DimensionsVectorCapabilitiesTest.java index 92d42fd43e0..ba2732ec0d9 100644 --- a/src/wms/src/test/java/org/geoserver/wms/wms_1_3/DimensionsVectorCapabilitiesTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/wms_1_3/DimensionsVectorCapabilitiesTest.java @@ -1,4 +1,4 @@ -/* (c) 2014 Open Source Geospatial Foundation - all rights reserved +/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. @@ -12,6 +12,7 @@ import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.ResourceInfo; +import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.wms.WMSDimensionsTestSupport; import org.junit.Test; import org.w3c.dom.Document; @@ -65,7 +66,7 @@ void checkEmptyElevationDimensionAndExtent() throws Exception { assertXpathEvaluatesTo(UNIT_SYMBOL, "//wms:Layer/wms:Dimension/@unitSymbol", dom); // check we have the extent assertXpathEvaluatesTo("elevation", "//wms:Layer/wms:Dimension/@name", dom); - assertXpathEvaluatesTo("0.0", "//wms:Layer/wms:Dimension/@default", dom); + assertXpathEvaluatesTo("0", "//wms:Layer/wms:Dimension/@default", dom); // and that it is empty assertXpathEvaluatesTo("", "//wms:Layer/wms:Dimension", dom); } @@ -235,5 +236,35 @@ public void testTimeElevation() throws Exception { assertXpathEvaluatesTo("2011-05-01T00:00:00.000Z,2011-05-02T00:00:00.000Z,2011-05-03T00:00:00.000Z,2011-05-04T00:00:00.000Z", "//wms:Layer/wms:Dimension[@name='time']", dom); } + + @Test + public void testDefaultTimeRangeFixed() throws Exception { + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("P1M/PRESENT"); + setupResourceDimensionDefaultValue(V_TIME_ELEVATION, ResourceInfo.TIME, defaultValueSetting); + + Document dom = dom(get("wms?request=getCapabilities&version=1.3.0"), false); + // print(dom); + + assertXpathEvaluatesTo("1", "count(//wms:Layer/wms:Dimension)", dom); + assertXpathEvaluatesTo("time", "//wms:Layer/wms:Dimension/@name", dom); + assertXpathEvaluatesTo("P1M/PRESENT", "//wms:Layer/wms:Dimension/@default", dom); + } + + @Test + public void testDefaultElevationRangeFixed() throws Exception { + DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); + defaultValueSetting.setStrategyType(Strategy.FIXED); + defaultValueSetting.setReferenceValue("-100/0"); + setupResourceDimensionDefaultValue(V_TIME_ELEVATION, ResourceInfo.ELEVATION, defaultValueSetting); + + Document dom = dom(get("wms?request=getCapabilities&version=1.3.0"), false); + // print(dom); + + assertXpathEvaluatesTo("1", "count(//wms:Layer/wms:Dimension)", dom); + assertXpathEvaluatesTo("elevation", "//wms:Layer/wms:Dimension/@name", dom); + assertXpathEvaluatesTo("-100/0", "//wms:Layer/wms:Dimension/@default", dom); + } }