Search in sources :

Example 1 with Range

use of org.apache.sis.measure.Range in project sis by apache.

the class Parameters method getValueDomain.

/**
 * Returns the domain of valid values defined by the given descriptor, or {@code null} if none.
 * This method performs the following operations:
 *
 * <ul>
 *   <li>If the given parameter is an instance of {@code DefaultParameterDescriptor},
 *       delegate to {@link DefaultParameterDescriptor#getValueDomain()}.</li>
 *   <li>Otherwise builds the range from the {@linkplain DefaultParameterDescriptor#getMinimumValue() minimum value},
 *       {@linkplain DefaultParameterDescriptor#getMaximumValue() maximum value} and, if the values are numeric, from
 *       the {@linkplain DefaultParameterDescriptor#getUnit() unit}.</li>
 * </ul>
 *
 * @param  descriptor  the parameter descriptor, or {@code null}.
 * @return the domain of valid values, or {@code null} if none.
 *
 * @see DefaultParameterDescriptor#getValueDomain()
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Range<?> getValueDomain(final ParameterDescriptor<?> descriptor) {
    if (descriptor != null) {
        if (descriptor instanceof DefaultParameterDescriptor<?>) {
            return ((DefaultParameterDescriptor<?>) descriptor).getValueDomain();
        }
        final Class<?> valueClass = descriptor.getValueClass();
        final Comparable<?> minimumValue = descriptor.getMinimumValue();
        final Comparable<?> maximumValue = descriptor.getMaximumValue();
        if ((minimumValue == null || valueClass.isInstance(minimumValue)) && (maximumValue == null || valueClass.isInstance(maximumValue))) {
            if (Number.class.isAssignableFrom(valueClass)) {
                final Unit<?> unit = descriptor.getUnit();
                if (unit != null) {
                    return new MeasurementRange((Class) valueClass, (Number) minimumValue, true, (Number) maximumValue, true, unit);
                } else if (minimumValue != null || maximumValue != null) {
                    return new NumberRange((Class) valueClass, (Number) minimumValue, true, (Number) maximumValue, true);
                }
            } else if (minimumValue != null || maximumValue != null) {
                return new Range(valueClass, minimumValue, true, maximumValue, true);
            }
        }
    }
    return null;
}
Also used : NumberRange(org.apache.sis.measure.NumberRange) MeasurementRange(org.apache.sis.measure.MeasurementRange) NumberRange(org.apache.sis.measure.NumberRange) Range(org.apache.sis.measure.Range) MeasurementRange(org.apache.sis.measure.MeasurementRange)

Example 2 with Range

use of org.apache.sis.measure.Range in project sis by apache.

the class ParameterFormat method format.

/**
 * Implementation of public {@code format(…)} methods for all content levels except {@code NAME_SUMMARY}.
 *
 * @param  name    the group name, usually {@code descriptor.getName().getCode()}.
 * @param  group   the parameter descriptor, usually {@code values.getDescriptor()}.
 * @param  values  the parameter values, or {@code null} if none.
 * @throws IOException if an error occurred while writing to the given appendable.
 */
private void format(final String name, final ParameterDescriptorGroup group, final ParameterValueGroup values, final Appendable out) throws IOException {
    final boolean isBrief = (contentLevel == ContentLevel.BRIEF);
    final boolean showObligation = !isBrief || (values == null);
    final boolean hasColors = (colors != null);
    final String lineSeparator = this.lineSeparator;
    final Map<String, Integer> remarks = new LinkedHashMap<>();
    final ParameterTableRow header = new ParameterTableRow(group, displayLocale, preferredCodespaces, remarks, isBrief);
    final String groupCodespace = header.getCodeSpace();
    /*
         * Prepares the informations to be printed later as table rows. We scan all rows before to print them
         * in order to compute the width of codespaces. During this process, we split the objects to be printed
         * later in two collections: simple parameters are stored as (descriptor,value) pairs, while groups are
         * stored in an other collection for deferred formatting after the simple parameters.
         */
    int codespaceWidth = 0;
    final Collection<?> elements = (values != null) ? values.values() : group.descriptors();
    final Map<GeneralParameterDescriptor, ParameterTableRow> descriptorValues = new LinkedHashMap<>(hashMapCapacity(elements.size()));
    // To be created only if needed (it is usually not).
    List<Object> deferredGroups = null;
    for (final Object element : elements) {
        final GeneralParameterValue parameter;
        final GeneralParameterDescriptor descriptor;
        if (values != null) {
            parameter = (GeneralParameterValue) element;
            descriptor = parameter.getDescriptor();
        } else {
            parameter = null;
            descriptor = (GeneralParameterDescriptor) element;
        }
        if (descriptor instanceof ParameterDescriptorGroup) {
            if (deferredGroups == null) {
                deferredGroups = new ArrayList<>(4);
            }
            deferredGroups.add(element);
            continue;
        }
        /*
             * In the vast majority of cases, there is only one value for each parameter. However
             * if we find more than one value, we will append all extra occurrences in a "multiple
             * values" list to be formatted in the same row.
             */
        Object value = null;
        Unit<?> unit = null;
        if (parameter instanceof ParameterValue<?>) {
            final ParameterValue<?> p = (ParameterValue<?>) parameter;
            value = p.getValue();
            unit = p.getUnit();
        } else if (descriptor instanceof ParameterDescriptor<?>) {
            final ParameterDescriptor<?> p = (ParameterDescriptor<?>) descriptor;
            value = p.getDefaultValue();
            unit = p.getUnit();
        }
        ParameterTableRow row = descriptorValues.get(descriptor);
        if (row == null) {
            row = new ParameterTableRow(descriptor, displayLocale, preferredCodespaces, remarks, isBrief);
            descriptorValues.put(descriptor, row);
            if (row.codespaceWidth > codespaceWidth) {
                codespaceWidth = row.codespaceWidth;
            }
        }
        row.addValue(value, unit);
    }
    /*
         * Finished to collect the values. Now transform the values:
         *
         *   - Singleton value of array types (either primitive or not) are wrapped into a list.
         *   - Values are formatted.
         *   - Value domains are formatted.
         *   - Position of the character on which to do the alignment are remembered.
         */
    int unitWidth = 0;
    int valueDomainAlignment = 0;
    boolean writeCodespaces = (groupCodespace == null);
    final StringBuffer buffer = new StringBuffer();
    final FieldPosition dummyFP = new FieldPosition(-1);
    for (final Map.Entry<GeneralParameterDescriptor, ParameterTableRow> entry : descriptorValues.entrySet()) {
        final GeneralParameterDescriptor descriptor = entry.getKey();
        if (descriptor instanceof ParameterDescriptor<?>) {
            final ParameterTableRow row = entry.getValue();
            /*
                 * Verify if all rows use the same codespace than the header, in which case we can omit
                 * row codespace formatting.
                 */
            if (!writeCodespaces && !groupCodespace.equals(entry.getValue().getCodeSpace())) {
                writeCodespaces = true;
            }
            /*
                 * Format the value domain, so we can compute the character position on which to perform alignment.
                 */
            final Range<?> valueDomain = Parameters.getValueDomain((ParameterDescriptor<?>) descriptor);
            if (valueDomain != null) {
                final int p = row.setValueDomain(valueDomain, getFormat(Range.class), buffer);
                if (p > valueDomainAlignment) {
                    valueDomainAlignment = p;
                }
            }
            /*
                 * Singleton array conversion. Because it may be an array of primitive types, we can not just
                 * cast to Object[]. Then formats the units, with a space before the unit if the symbol is a
                 * letter or digit (i.e. we do not put a space in front of ° symbol for instance).
                 */
            row.expandSingleton();
            final int length = row.units.size();
            for (int i = 0; i < length; i++) {
                final Object unit = row.units.get(i);
                if (unit != null) {
                    if (getFormat(Unit.class).format(unit, buffer, dummyFP).length() != 0) {
                        if (Character.isLetterOrDigit(buffer.codePointAt(0))) {
                            buffer.insert(0, ' ');
                        }
                    }
                    final String symbol = buffer.toString();
                    row.units.set(i, symbol);
                    buffer.setLength(0);
                    final int p = symbol.length();
                    if (p > unitWidth) {
                        unitWidth = p;
                    }
                }
            }
        }
    }
    /*
         * Finished to prepare information. Now begin the actual writing.
         * First, formats the table header (i.e. the column names).
         */
    final Vocabulary resources = Vocabulary.getResources(displayLocale);
    header.writeIdentifiers(out, true, colors, false, lineSeparator);
    out.append(lineSeparator);
    final char horizontalBorder = isBrief ? '─' : '═';
    final TableAppender table = (isBrief || !columnSeparator.equals(SEPARATOR)) ? new TableAppender(out, columnSeparator) : new TableAppender(out);
    table.setMultiLinesCells(true);
    table.nextLine(horizontalBorder);
    int numColumnsBeforeValue = 0;
    for (int i = 0; ; i++) {
        boolean end = false;
        final short key;
        switch(i) {
            case 0:
                {
                    key = Vocabulary.Keys.Name;
                    break;
                }
            case 1:
                {
                    key = Vocabulary.Keys.Type;
                    break;
                }
            case 2:
                {
                    if (!showObligation) {
                        continue;
                    }
                    key = Vocabulary.Keys.Obligation;
                    break;
                }
            case 3:
                {
                    key = Vocabulary.Keys.ValueDomain;
                    break;
                }
            case 4:
                {
                    key = (values == null) ? Vocabulary.Keys.DefaultValue : Vocabulary.Keys.Value;
                    end = true;
                    break;
                }
            default:
                throw new AssertionError(i);
        }
        if (hasColors)
            table.append(X364.BOLD.sequence());
        table.append(resources.getString(key));
        if (hasColors)
            table.append(X364.NORMAL.sequence());
        if (!writeCodespaces && i == 0) {
            table.append(" (").append(groupCodespace).append(')');
        }
        if (end)
            break;
        nextColumn(table);
        numColumnsBeforeValue++;
    }
    table.nextLine();
    /*
         * Now process to the formatting of (descriptor,value) pairs. Each descriptor's alias
         * will be formatted on its own line in a table row. If there is more than one value,
         * then each value will be formatted on its own line as well. Note that the values may
         * be null if there is none.
         */
    char horizontalLine = horizontalBorder;
    for (final Map.Entry<GeneralParameterDescriptor, ParameterTableRow> entry : descriptorValues.entrySet()) {
        if (horizontalLine != 0) {
            table.nextLine('─');
        }
        horizontalLine = isBrief ? 0 : '─';
        final ParameterTableRow row = entry.getValue();
        row.codespaceWidth = codespaceWidth;
        row.writeIdentifiers(table, writeCodespaces, null, hasColors, lineSeparator);
        nextColumn(table);
        final GeneralParameterDescriptor generalDescriptor = entry.getKey();
        if (generalDescriptor instanceof ParameterDescriptor<?>) {
            /*
                 * Writes value type.
                 */
            final ParameterDescriptor<?> descriptor = (ParameterDescriptor<?>) generalDescriptor;
            final Class<?> valueClass = descriptor.getValueClass();
            if (valueClass != null) {
                // Should never be null, but let be safe.
                table.append(getFormat(Class.class).format(valueClass, buffer, dummyFP).toString());
            }
            nextColumn(table);
            buffer.setLength(0);
            /*
                 * Writes the obligation (mandatory or optional).
                 */
            if (showObligation) {
                final int minimumOccurs = descriptor.getMinimumOccurs();
                final int maximumOccurs = descriptor.getMaximumOccurs();
                if (maximumOccurs == 1) {
                    table.append(resources.getString(minimumOccurs == 0 ? Vocabulary.Keys.Optional : Vocabulary.Keys.Mandatory));
                } else {
                    final Format f = getFormat(Integer.class);
                    table.append(f.format(minimumOccurs, buffer, dummyFP).toString()).append(" … ");
                    buffer.setLength(0);
                    if (maximumOccurs == Integer.MAX_VALUE) {
                        table.append('∞');
                    } else {
                        table.append(f.format(maximumOccurs, buffer, dummyFP).toString());
                        buffer.setLength(0);
                    }
                }
                nextColumn(table);
            }
            /*
                 * Writes minimum and maximum values, together with the unit of measurement (if any).
                 */
            final String valueDomain = row.valueDomain;
            if (valueDomain != null) {
                table.append(CharSequences.spaces(valueDomainAlignment - row.valueDomainAlignment)).append(valueDomain);
            }
            nextColumn(table);
            /*
                 * Writes the values, each on its own line, together with their unit of measurement.
                 */
            final byte alignment = Number.class.isAssignableFrom(valueClass) ? TableAppender.ALIGN_RIGHT : TableAppender.ALIGN_LEFT;
            table.setCellAlignment(alignment);
            final int length = row.values.size();
            for (int i = 0; i < length; i++) {
                Object value = row.values.get(i);
                if (value != null) {
                    if (i != 0) {
                        /*
                             * If the same parameter is repeated more than once (not allowed by ISO 19111,
                             * but this extra flexibility is allowed by Apache SIS), write the ditto mark
                             * in all previous columns (name, type, etc.) on a new row.
                             */
                        final String ditto = resources.getString(Vocabulary.Keys.DittoMark);
                        table.nextLine();
                        table.setCellAlignment(TableAppender.ALIGN_CENTER);
                        for (int j = 0; j < numColumnsBeforeValue; j++) {
                            table.append(ditto);
                            nextColumn(table);
                        }
                        table.setCellAlignment(alignment);
                    }
                    /*
                         * Format the value followed by the unit of measure, or followed by spaces if there is no unit
                         * for this value. The intent is the right align the numerical value rather than the numerical
                         * + unit tupple.
                         */
                    final Format format = getFormat(value.getClass());
                    if (format != null) {
                        if (format instanceof NumberFormat && value instanceof Number) {
                            configure((NumberFormat) format, Math.abs(((Number) value).doubleValue()));
                        }
                        value = format.format(value, buffer, dummyFP);
                    }
                    table.append(value.toString());
                    buffer.setLength(0);
                    int pad = unitWidth;
                    final String unit = (String) row.units.get(i);
                    if (unit != null) {
                        table.append(unit);
                        pad -= unit.length();
                    }
                    table.append(CharSequences.spaces(pad));
                }
            }
        }
        table.nextLine();
        table.setCellAlignment(TableAppender.ALIGN_LEFT);
    }
    table.nextLine(horizontalBorder);
    table.flush();
    /*
         * Write remarks, if any.
         */
    for (final Map.Entry<String, Integer> remark : remarks.entrySet()) {
        ParameterTableRow.writeFootnoteNumber(out, remark.getValue());
        out.append(' ').append(remark.getKey()).append(lineSeparator);
    }
    /*
         * Now formats all groups deferred to the end of this table, with recursive calls to
         * this method (recursive calls use their own TableWriter instance, so they may result
         * in a different cell layout). Most of the time, there is no such additional group.
         */
    if (deferredGroups != null) {
        for (final Object element : deferredGroups) {
            final ParameterValueGroup value;
            final ParameterDescriptorGroup descriptor;
            if (element instanceof ParameterValueGroup) {
                value = (ParameterValueGroup) element;
                descriptor = value.getDescriptor();
            } else {
                value = null;
                descriptor = (ParameterDescriptorGroup) element;
            }
            out.append(lineSeparator);
            format(name + '/' + descriptor.getName().getCode(), descriptor, value, out);
        }
    }
}
Also used : Vocabulary(org.apache.sis.util.resources.Vocabulary) TableAppender(org.apache.sis.io.TableAppender) Unit(javax.measure.Unit) LinkedHashMap(java.util.LinkedHashMap) Format(java.text.Format) NumberFormat(java.text.NumberFormat) TabularFormat(org.apache.sis.io.TabularFormat) FieldPosition(java.text.FieldPosition) Range(org.apache.sis.measure.Range) IdentifiedObject(org.opengis.referencing.IdentifiedObject) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) NumberFormat(java.text.NumberFormat)

Example 3 with Range

use of org.apache.sis.measure.Range in project sis by apache.

the class Extents method getTimeRange.

/**
 * Returns the union of all time ranges found in the given extent, or {@code null} if none.
 *
 * @param  extent  the extent to convert to a time range, or {@code null}.
 * @return a time range created from the given extent, or {@code null} if none.
 *
 * @since 0.4
 */
public static Range<Date> getTimeRange(final Extent extent) {
    Date min = null;
    Date max = null;
    if (extent != null) {
        for (final TemporalExtent t : extent.getTemporalElements()) {
            final Date startTime, endTime;
            if (t instanceof DefaultTemporalExtent) {
                final DefaultTemporalExtent dt = (DefaultTemporalExtent) t;
                // Maybe user has overridden those methods.
                startTime = dt.getStartTime();
                endTime = dt.getEndTime();
            } else {
                final TemporalPrimitive p = t.getExtent();
                startTime = DefaultTemporalExtent.getTime(p, true);
                endTime = DefaultTemporalExtent.getTime(p, false);
            }
            if (startTime != null && (min == null || startTime.before(min)))
                min = startTime;
            if (endTime != null && (max == null || endTime.after(max)))
                max = endTime;
        }
    }
    if (min == null && max == null) {
        return null;
    }
    return new Range<>(Date.class, min, true, max, true);
}
Also used : TemporalExtent(org.opengis.metadata.extent.TemporalExtent) TemporalPrimitive(org.opengis.temporal.TemporalPrimitive) Range(org.apache.sis.measure.Range) MeasurementRange(org.apache.sis.measure.MeasurementRange) Date(java.util.Date)

Example 4 with Range

use of org.apache.sis.measure.Range in project sis by apache.

the class RangeSetTest method testRangeOfDates.

/**
 * Tests {@link RangeSet#add(Range)} using date values.
 */
@Test
public void testRangeOfDates() {
    final RangeSet<Date> ranges = RangeSet.create(Date.class, true, false);
    assertTrue(ranges.isEmpty());
    /*
         * Add a singleton range.
         */
    final Date now = new Date();
    final Date yesterday = new Date(now.getTime() - MILLISECONDS_PER_DAY);
    assertTrue(ranges.add(yesterday, now));
    assertEquals(1, ranges.size());
    checkContains(ranges, new Range<>(Date.class, yesterday, true, now, false), true, true);
    /*
         * Add a disjoint range.
         */
    final Date lastWeek = new Date(now.getTime() - 7 * MILLISECONDS_PER_DAY);
    final Date other = new Date(lastWeek.getTime() + 2 * MILLISECONDS_PER_DAY);
    assertTrue(ranges.add(new Range<>(Date.class, lastWeek, true, other, false)));
    assertEquals(2, ranges.size());
    /*
         * Verify the RangeSet content.
         */
    final Iterator<Range<Date>> it = ranges.iterator();
    assertEqual(new Range<>(Date.class, lastWeek, true, other, false), it.next(), ranges.first());
    assertEqual(new Range<>(Date.class, yesterday, true, now, false), it.next(), ranges.last());
    assertFalse(it.hasNext());
}
Also used : NumberRange(org.apache.sis.measure.NumberRange) Range(org.apache.sis.measure.Range) Date(java.util.Date) Test(org.junit.Test)

Aggregations

Range (org.apache.sis.measure.Range)4 Date (java.util.Date)2 MeasurementRange (org.apache.sis.measure.MeasurementRange)2 NumberRange (org.apache.sis.measure.NumberRange)2 FieldPosition (java.text.FieldPosition)1 Format (java.text.Format)1 NumberFormat (java.text.NumberFormat)1 LinkedHashMap (java.util.LinkedHashMap)1 Map (java.util.Map)1 Unit (javax.measure.Unit)1 TableAppender (org.apache.sis.io.TableAppender)1 TabularFormat (org.apache.sis.io.TabularFormat)1 Vocabulary (org.apache.sis.util.resources.Vocabulary)1 Test (org.junit.Test)1 TemporalExtent (org.opengis.metadata.extent.TemporalExtent)1 IdentifiedObject (org.opengis.referencing.IdentifiedObject)1 TemporalPrimitive (org.opengis.temporal.TemporalPrimitive)1