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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 88b18df First draft of Canvas.getGeographicArea() and
getSpatialResolution(), and use those information when searching for a
CoordinateOperation.
88b18df is described below
commit 88b18df1c73f9a6275131ddc2bf8e09e1ca968d2
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Feb 11 19:58:25 2020 +0100
First draft of Canvas.getGeographicArea() and getSpatialResolution(), and
use those information when searching for a CoordinateOperation.
---
.../java/org/apache/sis/internal/map/Canvas.java | 147 ++++++++++----
.../org/apache/sis/internal/map/CanvasContext.java | 219 +++++++++++++++++++++
.../org/apache/sis/internal/map/PlanarCanvas.java | 2 +-
.../internal/referencing/ReferencingUtilities.java | 4 +-
4 files changed, 334 insertions(+), 38 deletions(-)
diff --git
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
index b049d2d..59b501c 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
@@ -16,10 +16,11 @@
*/
package org.apache.sis.internal.map;
+import java.util.ArrayList;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
-import java.util.ArrayList;
+import java.util.OptionalDouble;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.MismatchedDimensionException;
@@ -28,8 +29,10 @@ import org.opengis.metadata.extent.GeographicBoundingBox;
import org.opengis.metadata.spatial.DimensionNameType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.EngineeringCRS;
+import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.coverage.CannotEvaluateException;
import org.opengis.util.FactoryException;
@@ -46,7 +49,6 @@ import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.referencing.operation.transform.LinearTransform;
import org.apache.sis.referencing.operation.transform.TransformSeparator;
-import org.apache.sis.referencing.operation.CoordinateOperationContext;
import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
import org.apache.sis.internal.referencing.CoordinateOperations;
import org.apache.sis.internal.referencing.ReferencingUtilities;
@@ -104,7 +106,7 @@ import org.apache.sis.coverage.grid.GridExtent;
* <h2>Location of data to display</h2>
* In addition of above-cited Coordinate Reference Systems, a {@code Canvas}
contains also a point of interest.
* The point of interest is often, but not necessarily, at the center of
display area.
- * It defines the position where {@linkplain #getResolution() resolutions}
will be computed, and where
+ * It defines the position where {@linkplain #getSpatialResolution()
resolutions} will be computed, and where
* {@linkplain PlanarCanvas#scale(double, double) scales},
* {@linkplain PlanarCanvas#translate(double, double) translations} and
* {@linkplain PlanarCanvas#rotate(double) rotations} will be applied.
@@ -144,14 +146,6 @@ import org.apache.sis.coverage.grid.GridExtent;
*/
public class Canvas extends Observable implements Localized {
/**
- * Desired resolution in display units (usually pixels). This is used for
avoiding
- * the cost of transformations having too much accuracy for the current
zoom level.
- *
- * @see #findTransform(CoordinateReferenceSystem,
CoordinateReferenceSystem)
- */
- private static final double DISPLAY_RESOLUTION = 1;
-
- /**
* The {@value} property name, used for notifications about changes in
objective CRS.
* The objective CRS is the Coordinate Reference System in which all data
are transformed before displaying.
* Its number of dimension is the determined by the display device (two
for flat screens).
@@ -216,6 +210,24 @@ public class Canvas extends Observable implements
Localized {
private static final String GRID_GEOMETRY_PROPERTY = "gridGeometry";
/**
+ * The {@value} property name. The geographic area is a synthetic property
computed
+ * from {@value #DISPLAY_BOUNDS_PROPERTY}, {@value
#OBJECTIVE_TO_DISPLAY_PROPERTY}
+ * and {@value #OBJECTIVE_CRS_PROPERTY}. There is no event fired for this
property.
+ *
+ * @see #getGeographicArea()
+ */
+ private static final String GEOGRAPHIC_AREA_PROPERTY = "geographicArea";
+
+ /**
+ * The {@value} property name. The resolution is a synthetic property
computed from
+ * {@value #POINT_OF_INTEREST_PROPERTY}, {@value
#OBJECTIVE_TO_DISPLAY_PROPERTY} and
+ * {@value #OBJECTIVE_CRS_PROPERTY}. There is no event fired for this
property.
+ *
+ * @see #getSpatialResolution()
+ */
+ private static final String SPATIAL_RESOLUTION_PROPERTY =
"spatialResolution";
+
+ /**
* The coordinate reference system in which to transform all data before
displaying.
* If {@code null}, then no transformation is applied and data coordinates
are used directly
* as display coordinates, regardless the data CRS (even if different data
use different CRS).
@@ -270,6 +282,7 @@ public class Canvas extends Observable implements Localized
{
* and {@link #objectiveCRS} (indirectly, through {@link
#multidimToObjective}) and should be
* recomputed when any of those properties changed.</p>
*
+ * @see #getObjectivePOI()
* @see #getGridGeometry()
*/
private DirectPosition objectivePOI;
@@ -343,6 +356,17 @@ public class Canvas extends Observable implements
Localized {
private GridGeometry gridGeometry;
/**
+ * The context (geographic area and desired resolution) for selecting a
coordinate operation.
+ * The information contained in this object can opportunistically be used
for providing the
+ * geographic area and spatial resolution of this canvas.
+ *
+ * @see #getGeographicArea()
+ * @see #getSpatialResolution()
+ * @see #findTransform(CoordinateReferenceSystem,
CoordinateReferenceSystem)
+ */
+ private final CanvasContext operationContext;
+
+ /**
* The factory to use for creating coordinate operations. This factory
allow us to specify the area
* of interest (the geographic region shown by this {@code Canvas}) and
the desired resolution.
*
@@ -374,6 +398,7 @@ public class Canvas extends Observable implements Localized
{
displayBounds = new GeneralEnvelope(displayCRS);
displayBounds.setToNaN();
coordinateOperationFactory = CoordinateOperations.factory();
+ operationContext = new CanvasContext();
}
/**
@@ -479,10 +504,11 @@ public class Canvas extends Observable implements
Localized {
ArgumentChecks.ensureNonNull(OBJECTIVE_CRS_PROPERTY, newValue);
ArgumentChecks.ensureDimensionMatches(OBJECTIVE_CRS_PROPERTY,
getDisplayDimensions(), newValue);
final CoordinateReferenceSystem oldValue = objectiveCRS;
- LinearTransform oldObjectiveToDisplay = null;
- LinearTransform newObjectiveToDisplay = null;
- if (!newValue.equals(oldValue)) {
- if (oldValue != null) try {
+ if (!newValue.equals(oldValue)) try {
+ final CoordinateOperation newToGeo =
objectiveToGeographic(newValue);
+ LinearTransform oldObjectiveToDisplay = null;
+ LinearTransform newObjectiveToDisplay = null;
+ if (oldValue != null) {
/*
* Compute the change unconditionally as a way to verify that
the new CRS is compatible with
* data currently shown. Another reason is that checking
identity transform is more reliable
@@ -517,14 +543,15 @@ public class Canvas extends Observable implements
Localized {
axisTypes = null;
gridGeometry = null;
}
- } catch (FactoryException | TransformException e) {
- throw new
RenderException(errors().getString(Errors.Keys.CanNotSetPropertyValue_1,
OBJECTIVE_CRS_PROPERTY), e);
}
- objectiveCRS = newValue; // Set only after everything
else succeeded.
+ objectiveCRS = newValue; // Set
only after everything else succeeded.
+ operationContext.setObjectiveToGeographic(newToGeo);
firePropertyChange(OBJECTIVE_CRS_PROPERTY, oldValue, newValue);
if (!Objects.equals(oldObjectiveToDisplay, newObjectiveToDisplay))
{
firePropertyChange(OBJECTIVE_TO_DISPLAY_PROPERTY,
oldObjectiveToDisplay, newObjectiveToDisplay);
}
+ } catch (FactoryException | TransformException e) {
+ throw new
RenderException(errors().getString(Errors.Keys.CanNotSetPropertyValue_1,
OBJECTIVE_CRS_PROPERTY), e);
}
}
@@ -610,6 +637,8 @@ public class Canvas extends Observable implements Localized
{
*/
void updateObjectiveToDisplay(final LinearTransform newValue) {
objectiveToDisplay = newValue;
+ gridGeometry = null;
+ operationContext.clear();
}
/**
@@ -624,6 +653,7 @@ public class Canvas extends Observable implements Localized
{
* @return size and location of the display device.
*
* @see #DISPLAY_BOUNDS_PROPERTY
+ * @see #getGeographicArea()
*/
public Envelope getDisplayBounds() {
return displayBounds.isAllNaN() ? null : new
GeneralEnvelope(displayBounds);
@@ -656,6 +686,7 @@ public class Canvas extends Observable implements Localized
{
}
if (!oldValue.equals(displayBounds)) {
gridGeometry = null;
+ operationContext.partialClear(false);
// Resolution is still valid.
firePropertyChange(DISPLAY_BOUNDS_PROPERTY, oldValue, newValue);
// Do not publish reference to `displayBounds`.
}
}
@@ -703,11 +734,13 @@ public class Canvas extends Observable implements
Localized {
* (only the number of dimensions that the display device can
show).
*/
if (objectiveCRS == null) {
- objectiveCRS = CRS.getComponentAt(crs, 0,
getDisplayDimensions());
- if (objectiveCRS == null) {
+ final CoordinateReferenceSystem newObjectiveCRS =
CRS.getComponentAt(crs, 0, getDisplayDimensions());
+ if (newObjectiveCRS == null) {
throw new IllegalArgumentException("Can not infer
objective CRS.");
// Message not localized yet because we should probably
try harder.
}
+
operationContext.setObjectiveToGeographic(objectiveToGeographic(newObjectiveCRS));
+ objectiveCRS = newObjectiveCRS; //
Set only on success.
}
/*
* Transform the Point Of Interest to the objective CRS as a way
to test its validity.
@@ -724,6 +757,7 @@ public class Canvas extends Observable implements Localized
{
augmentedObjectiveCRS = null;
// Will be recomputed when first needed.
axisTypes = null;
gridGeometry = null;
+ operationContext.partialClear(true);
// Geographic area is still valid.
firePropertyChange(POINT_OF_INTEREST_PROPERTY, oldValue,
newValue); // Do not publish reference to `copy`.
} catch (FactoryException | TransformException e) {
throw new
RenderException(errors().getString(Errors.Keys.CanNotSetPropertyValue_1,
POINT_OF_INTEREST_PROPERTY), e);
@@ -731,6 +765,14 @@ public class Canvas extends Observable implements
Localized {
}
/**
+ * Returns the coordinate values of the Point Of Interest (POI) in
objective CRS.
+ * The array length should be equal to {@link #getDisplayDimensions()}.
+ */
+ final double[] getObjectivePOI() {
+ return objectivePOI.getCoordinate();
+ }
+
+ /**
* Returns canvas properties (CRS, display bounds, conversion)
encapsulated in a grid geometry.
* This is a convenience method for interoperability with grid coverage
API.
* If {@link #setGridGeometry(GridGeometry)} has been invoked with a
non-null value and no other
@@ -914,8 +956,8 @@ public class Canvas extends Observable implements Localized
{
* "all or nothing" behavior.
*/
displayBounds.setEnvelope(newBounds);
+ updateObjectiveToDisplay(newObjectiveToDisplay);
pointOfInterest = newPOI;
- objectiveToDisplay = newObjectiveToDisplay;
objectiveCRS = newObjectiveCRS;
multidimToObjective = dimensionSelect;
augmentedObjectiveCRS = null; // Will be recomputed
when first needed.
@@ -949,21 +991,47 @@ public class Canvas extends Observable implements
Localized {
}
}
- public Optional<GeographicBoundingBox> getGeographicArea() {
- return Optional.empty(); // TODO
+ /**
+ * Returns the geographic bounding box encompassing the area shown on the
display device.
+ * If the {@linkplain #getObjectiveCRS() objective CRS} is not convertible
to a geographic CRS,
+ * then this method returns an empty value.
+ *
+ * @return geographic bounding box encompassing the viewed area.
+ * @throws RenderException in an error occurred while computing the
geographic area.
+ *
+ * @see #getDisplayBounds()
+ */
+ public Optional<GeographicBoundingBox> getGeographicArea() throws
RenderException {
+ try {
+ return operationContext.getGeographicArea(this);
+ } catch (TransformException e) {
+ throw new
RenderException(errors().getString(Errors.Keys.CanNotCompute_1,
GEOGRAPHIC_AREA_PROPERTY), e);
+ }
}
- public double[] getResolution() {
- return null;
+ /**
+ * Returns an estimation of the resolution (in metres) at the point of
interest.
+ * If the {@linkplain #getObjectiveCRS() objective CRS} is not convertible
to a
+ * geographic CRS, then this method returns an empty value.
+ *
+ * @return estimation of the resolution in metres at current point of
interest.
+ * @throws RenderException in an error occurred while computing the
resolution.
+ */
+ public OptionalDouble getSpatialResolution() throws RenderException {
+ try {
+ return operationContext.getSpatialResolution(this);
+ } catch (TransformException e) {
+ throw new
RenderException(errors().getString(Errors.Keys.CanNotCompute_1,
SPATIAL_RESOLUTION_PROPERTY), e);
+ }
}
/**
- * Allocates a position which can hold a coordinates in objective or
display CRS, or
- * returns {@code null} for letting {@link MathTransform} do the
allocation themselves.
- * May be overridden by subclasses for a little bit more efficiency.
+ * Computes the value for {@link #objectiveToGeographic}. The value is not
stored by this method for
+ * giving caller a chance to validate other properties before to write
them in a "all or nothing" way.
*/
- DirectPosition allocatePosition() {
- return null;
+ private CoordinateOperation objectiveToGeographic(final
CoordinateReferenceSystem crs) throws FactoryException {
+ final GeographicCRS geoCRS =
ReferencingUtilities.toNormalizedGeographicCRS(crs, false, false);
+ return (geoCRS != null) ?
coordinateOperationFactory.createOperation(crs, geoCRS) : null;
}
/**
@@ -972,14 +1040,23 @@ public class Canvas extends Observable implements
Localized {
* CRS may differ depending on which area is currently visible in the
canvas. All requests for a coordinate
* operation should invoke this method instead than {@link
CRS#findOperation(CoordinateReferenceSystem,
* CoordinateReferenceSystem, GeographicBoundingBox)}.
+ *
+ * @todo verify if bounding box/resolution are up-to-date.
*/
private MathTransform findTransform(final CoordinateReferenceSystem source,
- final CoordinateReferenceSystem
target) throws FactoryException
+ final CoordinateReferenceSystem target)
+ throws FactoryException, RenderException
{
- final CoordinateOperationContext context = new
CoordinateOperationContext();
- final Optional<GeographicBoundingBox> geographicArea =
getGeographicArea();
- geographicArea.ifPresent(context::setAreaOfInterest);
- return coordinateOperationFactory.createOperation(source, target,
context).getMathTransform();
+ operationContext.refresh();
+ return coordinateOperationFactory.createOperation(source, target,
operationContext).getMathTransform();
+ }
+
+ /**
+ * Allocates a position which can hold a coordinates in objective CRS.
+ * May be overridden by subclasses for a little bit more efficiency.
+ */
+ DirectPosition allocatePosition() {
+ return new GeneralDirectPosition(objectiveCRS);
}
/**
diff --git
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasContext.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasContext.java
new file mode 100644
index 0000000..5e673bd
--- /dev/null
+++
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasContext.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.map;
+
+import java.util.Optional;
+import java.util.OptionalDouble;
+import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.opengis.referencing.crs.GeographicCRS;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.datum.Ellipsoid;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.apache.sis.referencing.operation.CoordinateOperationContext;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.math.MathFunctions;
+import org.apache.sis.measure.Units;
+import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.referencing.operation.transform.LinearTransform;
+
+
+/**
+ * Contextual information for allowing {@link Canvas} to select the most
appropriate
+ * coordinate operation for the viewed area and resolution.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+@SuppressWarnings("serial") // Not intended to be serialized.
+final class CanvasContext extends CoordinateOperationContext {
+ /**
+ * Desired resolution in display units (usually pixels). This is used for
avoiding
+ * the cost of transformations having too much accuracy for the current
zoom level.
+ *
+ * @see Canvas#findTransform(CoordinateReferenceSystem,
CoordinateReferenceSystem)
+ */
+ private static final double DISPLAY_RESOLUTION = 1;
+
+ /**
+ * Transformation from {@linkplain Canvas#getObjectiveCRS() objective CRS}
to a geographic CRS, or {@code null} if
+ * none can be found. The geographic CRS (operation target) has
(longitude, latitude) axes in degrees but the same
+ * geodetic datum than the objective CRS, so the prime meridian is not
necessarily Greenwich. This is recomputed
+ * immediately after a change of {@link Canvas#objectiveCRS} or {@link
Canvas#pointOfInterest} because it will
+ * be needed anyway for {@link
Canvas#findTransform(CoordinateReferenceSystem, CoordinateReferenceSystem)}.
+ *
+ * @see Canvas#getGeographicArea()
+ * @see Canvas#objectiveToGeographic(CoordinateReferenceSystem)
+ */
+ private CoordinateOperation objectiveToGeographic;
+
+ /**
+ * Authalic radius of the geographic CRS which is the target of {@link
#objectiveToGeographic}, in metres.
+ * This is computed in same time than {@link #objectiveToGeographic}.
+ */
+ private double radius;
+
+ /**
+ * The geographic area, computed when first requested and saved for reuse.
This is reset to {@code null} every time
+ * that {@link Canvas#objectiveCRS}, {@link Canvas#objectiveToDisplay} or
{@link Canvas#displayBounds} is modified.
+ *
+ * @see Canvas#GEOGRAPHIC_AREA_PROPERTY
+ * @see #getGeographicArea(Canvas)
+ */
+ private GeographicBoundingBox geographicArea;
+
+ /**
+ * The resolution in metres, computed when first requested and saved for
reuse. This is reset to {@code null}
+ * every time {@link Canvas#objectiveCRS}, {@link
Canvas#objectiveToDisplay} or {@link Canvas#pointOfInterest}
+ * is modified. Value 0 means that the value has not yet been computed.
+ *
+ * @see Canvas#SPATIAL_RESOLUTION_PROPERTY
+ * @see #getSpatialResolution(Canvas)
+ */
+ private double resolution;
+
+ /**
+ * Creates a new context.
+ */
+ CanvasContext() {
+ }
+
+ /**
+ * Sets the operation from {@link Canvas#objectiveCRS} to geographic CRS.
+ */
+ final void setObjectiveToGeographic(final CoordinateOperation op) {
+ final Ellipsoid ellipsoid = ((GeographicCRS)
op.getTargetCRS()).getDatum().getEllipsoid();
+ radius =
ellipsoid.getAxisUnit().getConverterTo(Units.METRE).convert(Formulas.getAuthalicRadius(ellipsoid));
+ objectiveToGeographic = op; // Set only if above line
succeeded.
+ clear();
+ }
+
+ /**
+ * Clears information that depends on {@link Canvas#objectiveToDisplay}.
+ * This method assumes that {@link Canvas#objectiveCRS} is still valid.
+ */
+ final void clear() {
+ geographicArea = null;
+ resolution = 0;
+ }
+
+ /**
+ * Clears only some information, depending on whether the modified
property is point of interest
+ * or the display bounds.
+ *
+ * @param poi {@code true} if the modified property is the point of
interest,
+ * {@code false} if the modified property is the display
bounds.
+ */
+ final void partialClear(final boolean poi) {
+ if (poi) resolution = 0;
+ else geographicArea = null;
+ }
+
+ /**
+ * Returns the geographic area, or an empty value if none.
+ *
+ * @see Canvas#getGeographicArea()
+ */
+ final Optional<GeographicBoundingBox> getGeographicArea(final Canvas
canvas) throws TransformException {
+ if (geographicArea != null) {
+ return Optional.of(geographicArea);
+ }
+ recompute(canvas);
+ return Optional.ofNullable(geographicArea);
+ }
+
+ /**
+ * Returns the spatial resolution, or an empty value if none.
+ *
+ * @see Canvas#getSpatialResolution()
+ */
+ final OptionalDouble getSpatialResolution(final Canvas canvas) throws
TransformException {
+ if (!(resolution > 0)) {
+ recompute(canvas);
+ if (!(resolution > 0)) {
+ return OptionalDouble.empty();
+ }
+ }
+ return OptionalDouble.of(resolution);
+ }
+
+ /**
+ * Recomputes {@link #geographicArea} and {@link #resolution} fields that
are not valid.
+ * This method assumes that {@link #objectiveToGeographic} is valid.
+ */
+ @SuppressWarnings("fallthrough")
+ private void recompute(final Canvas canvas) throws TransformException {
+ final LinearTransform objectiveToDisplay =
canvas.getObjectiveToDisplay();
+ final MathTransform displayToGeographic = MathTransforms.concatenate(
+ objectiveToDisplay.inverse(),
+ objectiveToGeographic.getMathTransform());
+ /*
+ * Compute geographic area using an operation going directly from
display CRS
+ * to geographic CRS (do not go to objective CRS as an intermediate
step,
+ * because doing 2 envelope transformations increases the errors).
+ */
+ if (geographicArea == null && !canvas.displayBounds.isAllNaN()) {
+ final GeneralEnvelope bounds =
Envelopes.transform(displayToGeographic, canvas.displayBounds);
+
bounds.setCoordinateReferenceSystem(objectiveToGeographic.getTargetCRS());
+ final DefaultGeographicBoundingBox bbox = new
DefaultGeographicBoundingBox();
+ bbox.setBounds(bounds); // Will perform longitude rotation to
Greenwich if needed.
+ bbox.transitionTo(DefaultGeographicBoundingBox.State.FINAL);
+ geographicArea = bbox;
+ }
+ /*
+ * Estimate spatial resolution at the point of interest. The
calculation is done in
+ * (longitude, latitude, height) space where the height is optional.
The angles are
+ * converted to meters using the authalic radius.
+ */
+ if (!(resolution > 0)) {
+ final double[] poi = canvas.getObjectivePOI();
+ objectiveToDisplay.transform(poi, 0, poi, 0, 1);
+ final Matrix derivative =
MathTransforms.derivativeAndTransform(displayToGeographic, poi, 0, poi, 1);
+ final double[] vector = new double[derivative.getNumCol()];
+ final double[] combined = new double[derivative.getNumRow()];
+ for (int j=0; j<combined.length; j++) {
+ for (int i=0; i<vector.length; i++) {
+ vector[i] = derivative.getElement(j,i);
+ }
+ double m = MathFunctions.magnitude(vector);
+ switch (j) {
+ case 0: m *= Math.cos(Math.toRadians(poi[1])); //
Adjust longitude, then fall through.
+ case 1: m = Math.toRadians(m) * radius; break; //
Latitude (this case) or Longitude (case 0).
+ // Other cases: assume value already in metres.
+ }
+ combined[j] = m;
+ }
+ resolution = MathFunctions.magnitude(combined) * radius;
+ }
+ }
+
+ /**
+ * Sets the {@link CoordinateOperationContext} object to the desired area
and accuracy
+ * of the coordinate operation to obtain.
+ */
+ final void refresh() {
+ setAreaOfInterest(geographicArea); // null
for default behavior.
+ setDesiredAccuracy(resolution * DISPLAY_RESOLUTION); // 0 for
default behavior.
+ }
+}
diff --git
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/PlanarCanvas.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/PlanarCanvas.java
index e7e8ca2..ec0eed8 100644
---
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/PlanarCanvas.java
+++
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/PlanarCanvas.java
@@ -116,7 +116,7 @@ public abstract class PlanarCanvas extends Canvas {
*/
@Override
final DirectPosition allocatePosition() {
- return new DirectPosition2D();
+ return new DirectPosition2D(super.getObjectiveCRS());
}
/**
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 7e6489a..35e6745 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
@@ -283,8 +283,8 @@ public final class ReferencingUtilities extends Static {
}
/**
- * Derives a geographic CRS with (<var>longitude</var>,
<var>latitude</var>) axis in the specified order and in decimal degrees.
- * If no such CRS can be obtained or created, returns {@code null}.
+ * Derives a geographic CRS with (<var>longitude</var>,
<var>latitude</var>) axis in the specified
+ * order and in decimal degrees. If no such CRS can be obtained or
created, returns {@code null}.
*
* <p>This method does not set the prime meridian to Greenwich.
* Meridian rotation, if needed, shall be performed by the caller.</p>