use of javax.measure.Unit in project sis by apache.
the class QuantitiesTest method testCastOrCopy.
/**
* Tests {@link Quantities#castOrCopy(Quantity)}.
*/
@Test
public void testCastOrCopy() {
Quantity<Length> q = Quantities.create(5, Units.KILOMETRE);
assertSame(q, Quantities.castOrCopy(q));
q = new Quantity<Length>() {
@Override
public Number getValue() {
return 8;
}
@Override
public Unit<Length> getUnit() {
return Units.CENTIMETRE;
}
@Override
public Quantity<Length> add(Quantity<Length> ignored) {
return null;
}
@Override
public Quantity<Length> subtract(Quantity<Length> ignored) {
return null;
}
@Override
public Quantity<?> multiply(Quantity<?> ignored) {
return null;
}
@Override
public Quantity<?> divide(Quantity<?> ignored) {
return null;
}
@Override
public Quantity<Length> multiply(Number ignored) {
return null;
}
@Override
public Quantity<Length> divide(Number ignored) {
return null;
}
@Override
public Quantity<?> inverse() {
return null;
}
@Override
public Quantity<Length> to(Unit<Length> ignored) {
return null;
}
@Override
public <T extends Quantity<T>> Quantity<T> asType(Class<T> ignored) {
return null;
}
};
final Length c = Quantities.castOrCopy(q);
assertNotSame(q, c);
assertEquals("value", 8, c.getValue().doubleValue(), STRICT);
assertSame("unit", Units.CENTIMETRE, c.getUnit());
}
use of javax.measure.Unit 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);
}
}
}
use of javax.measure.Unit in project sis by apache.
the class UnitFormat method fromName.
/**
* Returns the unit instance for the given long (un)localized or name.
* This method is somewhat the converse of {@link #symbolToName()}, but recognizes also
* international and American spelling of unit names in addition of localized names.
* The intent is to recognize "meter" as well as "metre".
*
* <p>While we said that {@code UnitFormat} is not thread safe, we make an exception for this method
* for allowing the singleton {@link #INSTANCE} to parse symbols in a multi-threads environment.</p>
*
* @param uom the unit symbol, without leading or trailing spaces.
*/
private Unit<?> fromName(String uom) {
/*
* Before to search in resource bundles, check for degrees units. The "deg" unit can be both angular
* and Celsius degrees. We try to resolve this ambiguity by looking for the "C" suffix. We perform a
* special case for the degrees units because SI symbols are case-sentive and unit names in resource
* bundles are case-insensitive, but the "deg" case is a mix of both.
*/
final int length = uom.length();
for (int i = 0; ; i++) {
if (i != DEGREES.length()) {
if (i != length && (uom.charAt(i) | ('a' - 'A')) == DEGREES.charAt(i)) {
// Loop as long as the characters are the same, ignoring case.
continue;
}
if (i != 3 && i != 6) {
// Exit if not "deg" (3) or "degree" (6 characters).
break;
}
}
if (length == i) {
// Exactly "deg", "degree" or "degrees" (ignoring case).
return Units.DEGREE;
}
final int c = uom.codePointAt(i);
if (c == '_' || Character.isSpaceChar(c)) {
// Ignore space in "degree C", "deg C", "deg K", etc.
i += Character.charCount(c);
}
if (length - i == 1) {
switch(uom.charAt(i)) {
// Unicode U+212A
case 'K':
// "degK" (ignoring case except for 'K')
case 'K':
return Units.KELVIN;
case 'C':
return Units.CELSIUS;
// degree_N, degrees_N, degreeN, degreesN.
case 'N':
// degree_E, degrees_E, degreeE, degreesE.
case 'E':
return Units.DEGREE;
}
}
break;
}
/*
* At this point, we determined that the given unit symbol is not degrees (of angle or of temperature).
* Remaining code is generic to all other kinds of units: a check in a HashMap loaded when first needed.
*/
Map<String, Unit<?>> map = nameToUnit;
if (map == null) {
map = SHARED.get(locale);
if (map == null) {
map = new HashMap<>(128);
copy(locale, symbolToName(), map);
if (!locale.equals(Locale.US))
copy(Locale.US, getBundle(Locale.US), map);
if (!locale.equals(Locale.ROOT))
copy(Locale.ROOT, getBundle(Locale.ROOT), map);
/*
* The UnitAliases file contains names that are not unit symbols and are not included in the UnitNames
* property files neither. It contains longer names sometime used (for example "decimal degree" instead
* of "degree"), some plural forms (for example "feet" instead of "foot") and a few common misspellings
* (for exemple "Celcius" instead of "Celsius").
*/
final ResourceBundle r = ResourceBundle.getBundle("org.apache.sis.measure.UnitAliases", locale, UnitFormat.class.getClassLoader());
for (final String name : r.keySet()) {
map.put(name.intern(), Units.get(r.getString(name)));
}
map = Collections.unmodifiableMap(map);
/*
* Cache the map so we can share it with other UnitFormat instances.
* Sharing is safe if the map is unmodifiable.
*/
synchronized (SHARED) {
for (final Map<String, Unit<?>> existing : SHARED.values()) {
if (map.equals(existing)) {
map = existing;
break;
}
}
SHARED.put(locale, map);
}
}
nameToUnit = map;
}
/*
* The 'nameToUnit' map contains plural forms (declared in UnitAliases.properties),
* but we make a special case for "degrees", "metres" and "meters" because they
* appear in numerous places.
*/
uom = uom.replace('_', ' ').toLowerCase(locale);
uom = CharSequences.replace(CharSequences.replace(CharSequences.replace(CharSequences.toASCII(uom), "meters", "meter"), "metres", "metre"), DEGREES, "degree").toString();
return map.get(uom);
}
use of javax.measure.Unit in project sis by apache.
the class EPSGDataAccess method createParameterDescriptor.
/**
* Creates a definition of a single parameter used by an operation method.
*
* <div class="note"><b>Example:</b>
* some EPSG codes for parameters are:
*
* <table class="sis" summary="EPSG codes examples">
* <tr><th>Code</th> <th>Description</th></tr>
* <tr><td>8801</td> <td>Latitude of natural origin</td></tr>
* <tr><td>8802</td> <td>Longitude of natural origin</td></tr>
* <tr><td>8805</td> <td>Scale factor at natural origin</td></tr>
* <tr><td>8806</td> <td>False easting</td></tr>
* <tr><td>8807</td> <td>False northing</td></tr>
* </table></div>
*
* @param code value allocated by EPSG.
* @return the parameter descriptor for the given code.
* @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
* @throws FactoryException if the object creation failed for some other reason.
*
* @see org.apache.sis.parameter.DefaultParameterDescriptor
*/
@Override
public synchronized ParameterDescriptor<?> createParameterDescriptor(final String code) throws NoSuchAuthorityCodeException, FactoryException {
ArgumentChecks.ensureNonNull("code", code);
ParameterDescriptor<?> returnValue = null;
try (ResultSet result = executeQuery("Coordinate_Operation Parameter", "PARAMETER_CODE", "PARAMETER_NAME", "SELECT PARAMETER_CODE," + " PARAMETER_NAME," + " DESCRIPTION," + " DEPRECATED" + " FROM [Coordinate_Operation Parameter]" + " WHERE PARAMETER_CODE = ?", code)) {
while (result.next()) {
final Integer epsg = getInteger(code, result, 1);
final String name = getString(code, result, 2);
final String description = getOptionalString(result, 3);
final boolean deprecated = getOptionalBoolean(result, 4);
Class<?> type = Double.class;
/*
* If the parameter appears to have at least one non-null value in the "Parameter File Name" column,
* then the type is assumed to be URI as a string. Otherwise, the type is a floating point number.
*/
try (ResultSet r = executeQuery("ParameterType", "SELECT PARAM_VALUE_FILE_REF FROM [Coordinate_Operation Parameter Value]" + " WHERE (PARAMETER_CODE = ?) AND PARAM_VALUE_FILE_REF IS NOT NULL", epsg)) {
while (r.next()) {
String element = getOptionalString(r, 1);
if (element != null && !element.isEmpty()) {
type = String.class;
break;
}
}
}
/*
* Search for units. We typically have many different units but all of the same dimension
* (for example metres, kilometres, feet, etc.). In such case, the units Set will have only
* one element and that element will be the most frequently used unit. But some parameters
* accept units of different dimensions. For example the "Ordinate 1 of evaluation point"
* (EPSG:8617) parameter value may be in metres or in degrees. In such case the units Set
* will have two elements.
*/
final Set<Unit<?>> units = new LinkedHashSet<>();
try (ResultSet r = executeQuery("ParameterUnit", "SELECT UOM_CODE FROM [Coordinate_Operation Parameter Value]" + " WHERE (PARAMETER_CODE = ?)" + " GROUP BY UOM_CODE" + " ORDER BY COUNT(UOM_CODE) DESC", epsg)) {
next: while (r.next()) {
final String c = getOptionalString(r, 1);
if (c != null) {
final Unit<?> candidate = owner.createUnit(c);
for (final Unit<?> e : units) {
if (candidate.isCompatible(e)) {
continue next;
}
}
units.add(candidate);
}
}
}
/*
* Determines if the inverse operation can be performed by reversing the parameter sign.
* The EPSG dataset uses "Yes" or "No" value, but SIS scripts use boolean type. We have
* to accept both. Note that if we do not recognize the string as a boolean value, then
* we need a SQLException, not a null value. If the value is wrongly null, this method
* will succeed anyway and EPSGDataAccess will finish its work without apparent problem,
* but Apache SIS will fail later when it will try to compute the inverse operation, for
* example in a call to CRS.findOperation(…). The exception thrown at such later time is
* much more difficult to relate to the root cause than if we throw the exception here.
*/
InternationalString isReversible = null;
try (ResultSet r = executeQuery("ParameterSign", "SELECT DISTINCT PARAM_SIGN_REVERSAL FROM [Coordinate_Operation Parameter Usage]" + " WHERE (PARAMETER_CODE = ?)", epsg)) {
if (r.next()) {
Boolean b;
if (translator.useBoolean()) {
b = r.getBoolean(1);
if (r.wasNull())
b = null;
} else {
// May throw SQLException - see above comment.
b = SQLUtilities.toBoolean(r.getString(1));
}
if (b != null) {
isReversible = b ? SignReversalComment.OPPOSITE : SignReversalComment.SAME;
}
}
}
/*
* Now creates the parameter descriptor.
*/
final NumberRange<?> valueDomain;
switch(units.size()) {
case 0:
valueDomain = null;
break;
default:
valueDomain = new EPSGParameterDomain(units);
break;
case 1:
valueDomain = MeasurementRange.create(Double.NEGATIVE_INFINITY, false, Double.POSITIVE_INFINITY, false, CollectionsExt.first(units));
break;
}
final Map<String, Object> properties = createProperties("Coordinate_Operation Parameter", name, epsg, isReversible, deprecated);
properties.put(ImmutableIdentifier.DESCRIPTION_KEY, description);
final ParameterDescriptor<?> descriptor = new DefaultParameterDescriptor<>(properties, 1, 1, type, valueDomain, null, null);
returnValue = ensureSingleton(descriptor, returnValue, code);
}
} catch (SQLException exception) {
throw databaseFailure(OperationMethod.class, code, exception);
}
if (returnValue == null) {
throw noSuchAuthorityCode(OperationMethod.class, code);
}
return returnValue;
}
use of javax.measure.Unit in project sis by apache.
the class CoordinateSystemsTest method testReplaceAxes.
/**
* Tests {@link CoordinateSystems#replaceAxes(CoordinateSystem, AxisFilter)}
* without change of coordinate system type.
*/
@Test
public void testReplaceAxes() {
final EllipsoidalCS sourceCS = HardCodedCS.GEODETIC_3D;
// What we want to get.
final EllipsoidalCS targetCS = HardCodedCS.ELLIPSOIDAL_gon;
final CoordinateSystem actualCS = CoordinateSystems.replaceAxes(sourceCS, new AxisFilter() {
@Override
public boolean accept(final CoordinateSystemAxis axis) {
return Units.isAngular(axis.getUnit());
}
@Override
public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> unit) {
if (Units.isAngular(unit)) {
unit = Units.GRAD;
}
return unit;
}
});
assertEqualsIgnoreMetadata(targetCS, actualCS);
}
Aggregations