This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 10215933bdc7bd6d5c919cd624485ebc3dad933d Author: Martin Desruisseaux <[email protected]> AuthorDate: Sat May 18 15:12:48 2019 +0200 Better formatting of GeodeticCalculator, using the default accuracy (currently 1 cm). --- .../internal/referencing/ReferencingUtilities.java | 37 +++++++ .../apache/sis/referencing/GeodeticCalculator.java | 106 ++++++++++++++------- 2 files changed, 110 insertions(+), 33 deletions(-) diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java index 1d47591..664920d 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java @@ -44,6 +44,7 @@ import org.apache.sis.util.Static; import org.apache.sis.util.Utilities; import org.apache.sis.util.CharSequences; import org.apache.sis.util.resources.Errors; +import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.referencing.CommonCRS; import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.referencing.AbstractIdentifiedObject; @@ -52,6 +53,7 @@ import org.apache.sis.referencing.crs.DefaultGeographicCRS; import org.apache.sis.referencing.cs.AxesConvention; import org.apache.sis.referencing.cs.DefaultEllipsoidalCS; import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory.Context; +import org.apache.sis.internal.metadata.AxisDirections; import static java.util.Collections.singletonMap; @@ -559,4 +561,39 @@ public final class ReferencingUtilities extends Static { } return mapping; } + + /** + * Returns short names for all axes of the given CRS. This method uses short names like "Latitude" or "Height", + * even if the full ISO 19111 names are "Geodetic latitude" or "Ellipsoidal height". This is suitable as header + * for columns in a table. This method does not include abbreviation or units in the returned names. + * + * @param resources the resources from which to get "latitude" and "longitude" localized labels. + * @param crs the coordinate reference system from which to get axis names. + * @return axis names, localized if possible. + */ + public static String[] getShortAxisNames(final Vocabulary resources, final CoordinateReferenceSystem crs) { + final boolean isGeographic = (crs instanceof GeographicCRS); + final boolean isProjected = (crs instanceof ProjectedCRS); + final CoordinateSystem cs = crs.getCoordinateSystem(); + final String[] names = new String[cs.getDimension()]; + for (int i=0; i<names.length; i++) { + short key = 0; + final CoordinateSystemAxis axis = cs.getAxis(i); + final AxisDirection direction = axis.getDirection(); + if (AxisDirections.isCardinal(direction)) { + final boolean isMeridional = AxisDirection.NORTH.equals(direction) || AxisDirection.SOUTH.equals(direction); + if (isGeographic) { + key = isMeridional ? Vocabulary.Keys.Latitude : Vocabulary.Keys.Longitude; + } else if (isProjected) { + // We could add "Easting" / "Northing" here for ProjectedCRS in a future version. + } + } else if (AxisDirection.UP.equals(direction)) { + if (isGeographic | isProjected) { + key = Vocabulary.Keys.Height; + } + } + names[i] = (key != 0) ? resources.getString(key) : axis.getName().getCode(); + } + return names; + } } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticCalculator.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticCalculator.java index d474b26..2138605 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticCalculator.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticCalculator.java @@ -20,6 +20,7 @@ import java.awt.Shape; import java.util.Locale; import java.io.IOException; import java.io.UncheckedIOException; +import java.text.NumberFormat; import javax.measure.Unit; import javax.measure.quantity.Length; @@ -32,14 +33,16 @@ import org.opengis.geometry.coordinate.Position; import org.opengis.geometry.DirectPosition; import org.apache.sis.io.TableAppender; -import org.apache.sis.measure.Angle; +import org.apache.sis.measure.AngleFormat; import org.apache.sis.measure.Latitude; +import org.apache.sis.measure.Units; import org.apache.sis.geometry.CoordinateFormat; import org.apache.sis.internal.referencing.PositionTransformer; import org.apache.sis.internal.referencing.ReferencingUtilities; import org.apache.sis.internal.referencing.j2d.ShapeUtilities; import org.apache.sis.internal.referencing.Resources; import org.apache.sis.internal.referencing.Formulas; +import org.apache.sis.internal.util.Numerics; import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.ArgumentChecks; @@ -693,51 +696,88 @@ public class GeodeticCalculator { /** * Returns a string representation of start point, end point, azimuths and distance. + * The text representation is implementation-specific and may change in any future version. + * Current implementation is like below: + * + * {@preformat text + * Coordinate reference system: Unspecified datum based upon the GRS 1980 Authalic Sphere + * ┌─────────────┬─────────────────┬──────────────────┬─────────────┐ + * │ │ Latitude │ Longitude │ Azimuth │ + * │ Start point │ 9°39′06.1120″N │ 132°37′37.1248″W │ -17°10′37″ │ + * │ End point │ 70°32′45.0206″N │ 109°50′05.0533″E │ -119°03′12″ │ + * └─────────────┴─────────────────┴──────────────────┴─────────────┘ + * Geodesic distance: 9,967,530.74 m + * } * * @return a string representation of this calculator state. */ @Override public String toString() { - final Locale locale = Locale.getDefault(); - final Vocabulary resources = Vocabulary.getResources(locale); - final StringBuilder buffer = new StringBuilder(); - final String lineSeparator = System.lineSeparator(); - final CoordinateReferenceSystem crs = userToGeodetic.getCoordinateReferenceSystem(); - final boolean isGeographic = crs.equals(userToGeodetic.defaultCRS); + final StringBuilder buffer = new StringBuilder(); + final Locale locale = Locale.getDefault(); + final Vocabulary resources = Vocabulary.getResources(locale); + final String lineSeparator = System.lineSeparator(); + final CoordinateReferenceSystem crs = getPositionCRS(); try { + /* + * Header: name of the Coordinate Reference System. + */ resources.appendLabel(Vocabulary.Keys.CoordinateRefSys, buffer); buffer.append(' ').append(crs.getName().getCode()).append(lineSeparator); - final TableAppender table = new TableAppender(buffer, " │ "); - table.appendHorizontalSeparator(); - table.nextColumn(); if (isGeographic) table.append(resources.getString(Vocabulary.Keys.Latitude)); - table.nextColumn(); if (isGeographic) table.append(resources.getString(Vocabulary.Keys.Longitude)); - for (int i=crs.getCoordinateSystem().getDimension(); --i >= 2;) { - table.nextColumn(); // Insert space for additional coordinates, e.g. ellipsoidal height. - } - table.nextColumn(); - table.append(resources.getString(Vocabulary.Keys.Azimuth)).nextLine(); - final CoordinateFormat cf = new CoordinateFormat(locale, null); - cf.setSeparator("\t"); // For distributing coordinate values on different columns. - boolean endPoint = false; - do { - table.append(resources.getString(endPoint ? Vocabulary.Keys.EndPoint : Vocabulary.Keys.StartPoint)) - .nextColumn(); - try { - cf.format(endPoint ? getEndPoint() : getStartPoint(), table); + /* + * Start point and end point together with their azimuth, formatted as a table. + */ + if ((validity & (START_POINT | STARTING_AZIMUTH | END_POINT | ENDING_AZIMUTH)) != 0) { + final String[] axes = ReferencingUtilities.getShortAxisNames(resources, crs); + final AngleFormat azimuthFormat = new AngleFormat("DD°MM′SS″", locale); + final CoordinateFormat pointFormat = new CoordinateFormat(locale, null); + pointFormat.setSeparator("\t"); // For distributing coordinate values on different columns. + pointFormat.setDefaultCRS(crs); + pointFormat.setPrecision(Formulas.LINEAR_TOLERANCE, Units.METRE); + final TableAppender table = new TableAppender(buffer, " │ "); + table.setCellAlignment(TableAppender.ALIGN_CENTER); + table.appendHorizontalSeparator(); + for (final String axis : axes) { table.nextColumn(); - table.append(new Angle(endPoint ? getEndingAzimuth() : getStartingAzimuth()).toString()); - } catch (IllegalStateException | TransformException e) { - // Ignore. + table.append(axis); } - table.nextLine(); - } while ((endPoint = !endPoint) == true); - table.appendHorizontalSeparator(); - table.flush(); - resources.appendLabel(Vocabulary.Keys.GeodesicDistance, buffer); + table.nextColumn(); + table.append(resources.getString(Vocabulary.Keys.Azimuth)).nextLine(); + boolean endPoint = false; + do { + table.setCellAlignment(TableAppender.ALIGN_LEFT); + table.append(resources.getString(endPoint ? Vocabulary.Keys.EndPoint : Vocabulary.Keys.StartPoint)).nextColumn(); + table.setCellAlignment(TableAppender.ALIGN_RIGHT); + try { + pointFormat.format(endPoint ? getEndPoint() : getStartPoint(), table); + table.nextColumn(); + table.append(azimuthFormat.format(endPoint ? getEndingAzimuth() : getStartingAzimuth())); + } catch (IllegalStateException | TransformException e) { + // Ignore. + } + table.nextLine(); + } while ((endPoint = !endPoint) == true); + table.appendHorizontalSeparator(); + table.flush(); + } + /* + * Distances, formatted with a number of decimal fraction digits suitable for at least 1 centimetre precision. + */ + try { + final Unit<Length> unit = getDistanceUnit(); + final double distance = getGeodesicDistance(); + final double precision = Units.METRE.getConverterTo(unit).convert(Formulas.LINEAR_TOLERANCE); + final NumberFormat nf = NumberFormat.getNumberInstance(locale); + nf.setMaximumFractionDigits(max(Numerics.suggestFractionDigits(precision), 0)); + resources.appendLabel(Vocabulary.Keys.GeodesicDistance, buffer); + buffer.append(' ').append(nf.format(distance)) + .append(' ').append(unit).append(lineSeparator); + } catch (IllegalStateException e) { + // Ignore. + } } catch (IOException e) { throw new UncheckedIOException(e); // Should never happen since we are writting in a StringBuilder. } - buffer.append(String.format(locale, " %f %s", geodesicDistance, getDistanceUnit())); return buffer.toString(); } }
