sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Add a `CoverageCanvasApp` for manual & visual testing of `CoverageCanvas`.
Date Tue, 02 Feb 2021 23:11:59 GMT
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 e05a30a6daae3e833f9f157dd10568dbef613a35
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Feb 3 00:10:48 2021 +0100

    Add a `CoverageCanvasApp` for manual & visual testing of `CoverageCanvas`.
---
 .../org/apache/sis/gui/coverage/RenderingData.java |   3 +-
 .../java/org/apache/sis/gui/map/MapCanvas.java     |   7 +-
 .../apache/sis/gui/coverage/CoverageCanvasApp.java | 147 +++++++++++++++++++++
 .../org/apache/sis/gui/coverage/GridViewApp.java   |  17 ++-
 .../apache/sis/coverage/grid/SliceGeometry.java    |   6 +-
 .../java/org/apache/sis/image/TiledImageMock.java  |  30 ++++-
 6 files changed, 201 insertions(+), 9 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
index 6e08f03..c6f768f 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
@@ -530,10 +530,11 @@ final class RenderingData implements Cloneable {
      * Converts the given bounds from objective coordinates to pixel coordinates in the source
coverage.
      *
      * @param  bounds  objective coordinates.
-     * @return data coverage cell coordinates (in pixels).
+     * @return data coverage cell coordinates (in pixels), or {@code null} if unknown.
      * @throws TransformException if the bounds can not be transformed.
      */
     final Rectangle objectiveToData(final Rectangle2D bounds) throws TransformException {
+        if (objectiveToCenter == null) return null;
         return (Rectangle) Shapes2D.transform(MathTransforms.bidimensional(objectiveToCenter),
bounds, new Rectangle());
     }
 
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
index 8d53ffc..a5f403b 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
@@ -945,7 +945,12 @@ public abstract class MapCanvas extends PlanarCanvas {
                     crsToDisplay = MathTransforms.linear(m);
                     if (objectiveCRS == null) {
                         objectiveCRS = extent.toEnvelope(crsToDisplay.inverse()).getCoordinateReferenceSystem();
-                        // CRS computed above should not be null.
+                        /*
+                         * Above code tried to provide a non-null CRS on a "best effort"
basis. The objective CRS
+                         * may still be null, there is no obvious answer against that. It
is not the display CRS
+                         * if the "display to objective" transform is not identity. A grid
CRS is not appropriate
+                         * neither, otherwise `extent.toEnvelope(…)` would have found it.
+                         */
                     }
                 } else {
                     objectiveCRS = getDisplayCRS();
diff --git a/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/CoverageCanvasApp.java
b/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/CoverageCanvasApp.java
new file mode 100644
index 0000000..ab7964a
--- /dev/null
+++ b/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/CoverageCanvasApp.java
@@ -0,0 +1,147 @@
+/*
+ * 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.gui.coverage;
+
+import java.util.Random;
+import java.awt.Point;
+import java.awt.image.DataBuffer;
+import javafx.application.Application;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+import org.opengis.referencing.datum.PixelInCell;
+import org.apache.sis.coverage.grid.GridCoverage2D;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.gui.map.StatusBar;
+import org.apache.sis.image.TiledImageMock;
+import org.apache.sis.image.WritablePixelIterator;
+import org.apache.sis.internal.gui.BackgroundThreads;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+
+
+/**
+ * Shows {@link CoverageCanvas} with random data. The image will have small tiles of size
+ * {@value #TILE_WIDTH}×{@value #TILE_HEIGHT}. The image will artificially fails to provide
+ * some tiles in order to test error controls.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public class CoverageCanvasApp extends Application implements ChangeListener<Throwable>
{
+    /**
+     * Size of the artificial tiles. Should be small enough so we can have many of them.
+     * Width and height should be different in order to increase the chance to see bugs
+     * if some code confuse them.
+     */
+    private static final int TILE_WIDTH = 60, TILE_HEIGHT = 50;
+
+
+    /**
+     * Starts the test application.
+     *
+     * @param args  ignored.
+     */
+    public static void main(final String[] args) {
+        launch(args);
+    }
+
+    /**
+     * Creates and starts the test application.
+     *
+     * @param  window  where to show the application.
+     */
+    @Override
+    public void start(final Stage window) {
+        final CoverageCanvas canvas = new CoverageCanvas();
+        canvas.errorProperty().addListener(this);
+        final StatusBar statusBar = new StatusBar(null, canvas);
+        canvas.statusBar = statusBar;
+        canvas.setCoverage(createImage());
+        final BorderPane pane = new BorderPane(canvas.getView());
+        pane.setBottom(statusBar.getView());
+        window.setTitle("CoverageCanvas Test");
+        window.setScene(new Scene(pane));
+        window.setWidth (800);
+        window.setHeight(600);
+        window.show();
+    }
+
+    /**
+     * Stops background threads for allowing JVM to exit.
+     *
+     * @throws Exception if an error occurred while stopping the threads.
+     */
+    @Override
+    public void stop() throws Exception {
+        BackgroundThreads.stop();
+        super.stop();
+    }
+
+    /**
+     * Creates a dummy image for testing purpose. Some tiles will
+     * have artificial errors in order to see the error controls.
+     */
+    private static GridCoverage2D createImage() {
+        final Random random = new Random();
+        final int width  = TILE_WIDTH  * 15;
+        final int height = TILE_HEIGHT * 12;
+        final TiledImageMock image = new TiledImageMock(
+                DataBuffer.TYPE_BYTE, 1,
+                random.nextInt(50) - 25,            // minX
+                random.nextInt(50) - 25,            // minY
+                width, height,
+                TILE_WIDTH, TILE_HEIGHT,
+                random.nextInt(10) - 5,             // minTileX
+                random.nextInt(10) - 5,             // minTileY
+                false);
+        image.validate();
+        final double sc = 500d / Math.max(width, height);
+        final WritablePixelIterator it = WritablePixelIterator.create(image);
+        while (it.next()) {
+            final Point p = it.getPosition();
+            final double d = Math.hypot(p.x - width/2, p.y - height/2);
+            int value = 0;
+            if ((Math.round(d) & 16) == 0) {
+                value = Math.max(0, 255 - (int) (d * sc));
+            }
+            it.setSample(0, value);
+        }
+//      image.failRandomly(random);
+        return new GridCoverage2D(new GridGeometry(null, PixelInCell.CELL_CORNER,
+                MathTransforms.identity(2), CommonCRS.Engineering.DISPLAY.crs()), null, image);
+    }
+
+    /**
+     * Invoked when an exception occurred during rendering.
+     *
+     * @param  property  the {@link CoverageCanvas#errorProperty()}.
+     * @param  oldValue  the previous error, or {@code null} if none.
+     * @param  newValue  the new error, or {@code null} if cleared.
+     */
+    @Override
+    @SuppressWarnings("CallToPrintStackTrace")
+    public void changed(ObservableValue<? extends Throwable> property, Throwable oldValue,
Throwable newValue) {
+        if (newValue != null) {
+            newValue.printStackTrace();
+        }
+    }
+}
diff --git a/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/GridViewApp.java
b/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/GridViewApp.java
index c416872..bc457ce 100644
--- a/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/GridViewApp.java
+++ b/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/GridViewApp.java
@@ -23,6 +23,7 @@ import javafx.scene.layout.BorderPane;
 import javafx.scene.Scene;
 import javafx.stage.Stage;
 import org.apache.sis.image.TiledImageMock;
+import org.apache.sis.internal.gui.BackgroundThreads;
 
 
 /**
@@ -59,9 +60,8 @@ public final strictfp class GridViewApp extends Application {
      */
     @Override
     public void start(final Stage window) {
-        final GridView  view  = new GridView();
-        final BorderPane pane = new BorderPane();
-        pane.setCenter(view);
+        final GridView   view = new GridView();
+        final BorderPane pane = new BorderPane(view);
         window.setTitle("GridView Test");
         window.setScene(new Scene(pane));
         window.setWidth (400);
@@ -71,6 +71,17 @@ public final strictfp class GridViewApp extends Application {
     }
 
     /**
+     * Stops background threads for allowing JVM to exit.
+     *
+     * @throws Exception if an error occurred while stopping the threads.
+     */
+    @Override
+    public void stop() throws Exception {
+        BackgroundThreads.stop();
+        super.stop();
+    }
+
+    /**
      * Creates a dummy image for testing purpose. Some tiles will
      * have artificial errors in order to see the error controls.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/SliceGeometry.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/SliceGeometry.java
index 4c03da2..f222077 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/SliceGeometry.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/SliceGeometry.java
@@ -241,8 +241,10 @@ final class SliceGeometry implements Function<RenderedImage, GridGeometry>
{
                 }
                 final LinearTransform translation = MathTransforms.translation(offset);
                 final MathTransformsOrFactory f = MathTransformsOrFactory.wrap(factory);
-                gridToCRS   = f.concatenate(translation, gridToCRS);
-                cornerToCRS = f.concatenate(translation, cornerToCRS);
+                if (gridToCRS != null) {
+                    gridToCRS   = f.concatenate(translation, gridToCRS);
+                    cornerToCRS = f.concatenate(translation, cornerToCRS);
+                }
             }
             extent = relativeExtent;
         }
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/TiledImageMock.java b/core/sis-feature/src/test/java/org/apache/sis/image/TiledImageMock.java
index 6dbc034..8e93266 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/TiledImageMock.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/TiledImageMock.java
@@ -17,8 +17,13 @@
 package org.apache.sis.image;
 
 import java.awt.Point;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
 import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
 import java.awt.image.BandedSampleModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.ComponentSampleModel;
 import java.awt.image.ImagingOpException;
 import java.awt.image.PixelInterleavedSampleModel;
 import java.awt.image.Raster;
@@ -116,6 +121,12 @@ public final strictfp class TiledImageMock extends PlanarImage implements
Writab
     private AtomicInteger errorSequence;
 
     /**
+     * The color model, created only if requested.
+     * This is needed only for visualizing the image on screen; most tests do not need it.
+     */
+    private ColorModel colorModel;
+
+    /**
      * Creates a new tiled image. Testers should invoke {@link #validate()} after construction.
      *
      * @param dataType     sample data type as one of the {@link java.awt.image.DataBuffer}
constants.
@@ -154,9 +165,24 @@ public final strictfp class TiledImageMock extends PlanarImage implements
Writab
     }
 
     /**
-     * No color model since this test images is not for rendering on screen.
+     * Returns a gray scale color model if the data type is byte, or {@code null} otherwise.
+     * More color models may be supported in future versions if there is a need for them.
      */
-    @Override public ColorModel  getColorModel()  {return null;}
+    @Override
+    public ColorModel getColorModel() {
+        if (colorModel == null && sampleModel instanceof ComponentSampleModel &&
sampleModel.getNumBands() == 1) {
+            final int dataType = sampleModel.getDataType();
+            if (dataType <= DataBuffer.TYPE_USHORT) {
+                colorModel = new ComponentColorModel(
+                        ColorSpace.getInstance(ColorSpace.CS_GRAY),
+                        new int[] {DataBuffer.getDataTypeSize(dataType)},
+                        false, true, Transparency.OPAQUE, dataType);
+            }
+        }
+        return colorModel;
+    }
+
+    /** Returns a sample model for data type given to the constructor. */
     @Override public SampleModel getSampleModel() {return sampleModel;}
 
     /*


Mime
View raw message