sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jso...@apache.org
Subject [sis] branch geoapi-4.0 updated: Portrayal : add query property on MapLayer, add grid geometry 2d on GridCanvas
Date Fri, 20 Dec 2019 13:23:07 GMT
This is an automated email from the ASF dual-hosted git repository.

jsorel 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 3c22971  Portrayal : add query property on MapLayer, add grid geometry 2d on GridCanvas
3c22971 is described below

commit 3c22971ed994d3394838137122f5e02bacb4e52a
Author: jsorel <johann.sorel@geomatys.com>
AuthorDate: Fri Dec 20 14:22:49 2019 +0100

    Portrayal : add query property on MapLayer, add grid geometry 2d on GridCanvas
---
 .../org/apache/sis/internal/map/GridCanvas.java    | 315 ++++++++++++++++++++-
 .../java/org/apache/sis/internal/map/MapItem.java  |   2 -
 .../java/org/apache/sis/internal/map/MapLayer.java |  30 ++
 3 files changed, 343 insertions(+), 4 deletions(-)

diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/GridCanvas.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/GridCanvas.java
index 1d9a38e..e655ae2 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/GridCanvas.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/GridCanvas.java
@@ -16,14 +16,34 @@
  */
 package org.apache.sis.internal.map;
 
+import java.awt.geom.Rectangle2D;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
+import org.apache.sis.internal.referencing.provider.Affine;
 import org.apache.sis.referencing.CRS;
+import static org.apache.sis.referencing.CRS.getSingleComponents;
 import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.referencing.crs.DefaultDerivedCRS;
+import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.transform.TransformSeparator;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.Utilities;
+import org.apache.sis.util.collection.BackingStoreException;
+import org.opengis.geometry.Envelope;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.SingleCRS;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
 
 /**
- *
  * <p>
  * NOTE: this class is a first draft subject to modifications.
  * </p>
@@ -34,16 +54,307 @@ import org.apache.sis.util.ArgumentChecks;
  * @module
  */
 public abstract class GridCanvas {
+    /**
+     * The operation method used by {@link #getDisplayCRS()}.
+     * This is a temporary constant, as we will probably need to replace the creation
+     * of a {@link DefaultDerivedCRS} by something else. After that replacement, this
+     * constant will be removed.
+     */
+    private static final Affine DISPLAY_TO_OBJECTIVE_OPERATION = new Affine();
 
     private GridGeometry gridGeometry = new GridGeometry(new GridExtent(360, 180), CRS.getDomainOfValidity(CommonCRS.WGS84.normalizedGeographic()));
+    private GridGeometry gridGeometry2d = gridGeometry;
+    private boolean proportion = true;
 
     public GridGeometry getGridGeometry() {
         return gridGeometry;
     }
 
-    public void setGridGeometry(GridGeometry gridGeometry) {
+    /**
+     * Set global N dimension grid geometry.
+     *
+     * @param gridGeometry new grid geometry
+     */
+    public final void setGridGeometry(GridGeometry gridGeometry) throws FactoryException
{
         ArgumentChecks.ensureNonNull("gridGeometry", gridGeometry);
+        if (this.gridGeometry.equals(gridGeometry)) return;
+        final GridGeometry old = this.gridGeometry;
         this.gridGeometry = gridGeometry;
+
+        {
+            final CoordinateReferenceSystem crs = gridGeometry.getCoordinateReferenceSystem();
+            if (crs.getCoordinateSystem().getDimension() == 2) {
+                gridGeometry2d = gridGeometry;
+            } else {
+                final CoordinateReferenceSystem crs2d = getHorizontalComponent(crs);
+                final int idx = getHorizontalIndex(crs);
+                final MathTransform gridToCRS = gridGeometry.getGridToCRS(PixelInCell.CELL_CENTER);
+                final TransformSeparator sep = new TransformSeparator(gridToCRS);
+                sep.addTargetDimensions(idx, idx+1);
+                final MathTransform gridToCRS2D = sep.separate();
+
+                //we are expecting axis index to be preserved from grid to crs
+                final GridExtent extent = gridGeometry.getExtent().reduce(idx, idx+1);
+                gridGeometry2d = new GridGeometry(extent, PixelInCell.CELL_CENTER, gridToCRS2D,
crs2d);
+            }
+        }
+    }
+
+    /**
+     * Get 2 dimension grid geometry.
+     * This grid geometry only has the 2D CRS part of the global grid geometry.
+     *
+     * @return GridGeometry 2D, never null
+     */
+    public final GridGeometry getGridGeometry2D() {
+        return gridGeometry2d;
+    }
+
+    public final CoordinateReferenceSystem getObjectiveCRS() {
+        return gridGeometry.getCoordinateReferenceSystem();
+    }
+
+    public final CoordinateReferenceSystem getObjectiveCRS2D() {
+        return gridGeometry2d.getCoordinateReferenceSystem();
+    }
+
+    public final CoordinateReferenceSystem getDisplayCRS() {
+        /*
+         * TODO: will need a way to avoid the cast below. In my understanding, DerivedCRS
may not be the appropriate
+         *       CRS to create after all, because in ISO 19111 a DerivedCRS is more than
just a base CRS with a math
+         *       transform. A DerivedCRS may also "inherit" some characteritics of the base
CRS. For example if the
+         *       base CRS is a VerticalCRS, then the DerivedCRS may also implement VerticalCRS.
+         *
+         *       I'm not yet sure what should be the appropriate kind of CRS to create here.
ImageCRS? EngineeringCRS?
+         *       How to express the relationship to the base CRS is also not yet determined.
+         */
+        final SingleCRS objCRS2D = (SingleCRS) getObjectiveCRS2D();
+        final Map<String,?> name = Collections.singletonMap(DefaultDerivedCRS.NAME_KEY,
"Derived - "+objCRS2D.getName().toString());
+        final CoordinateReferenceSystem displayCRS = DefaultDerivedCRS.create(name, objCRS2D,
+                new DefaultConversion(name, DISPLAY_TO_OBJECTIVE_OPERATION, getObjectiveToDisplay2D(),
null),
+                objCRS2D.getCoordinateSystem());
+        return displayCRS;
+    }
+
+    public final void setObjectiveCRS(final CoordinateReferenceSystem crs) throws TransformException,
FactoryException {
+        ArgumentChecks.ensureNonNull("Objective CRS", crs);
+        if (Utilities.equalsIgnoreMetadata(gridGeometry.getCoordinateReferenceSystem(), crs))
{
+            return;
+        }
+
+        //store the visible area to restore it later
+        final GeneralEnvelope preserve = new GeneralEnvelope(gridGeometry.getEnvelope());
+
+        final int newDim = crs.getCoordinateSystem().getDimension();
+        final Envelope env = transform(preserve, crs);
+        final int oldidx = getHorizontalIndex(gridGeometry.getCoordinateReferenceSystem());
+        final int idx = getHorizontalIndex(crs);
+        final GridExtent oldExtent = gridGeometry.getExtent();
+        final long[] oldlow = oldExtent.getLow().getCoordinateValues();
+        final long[] oldhigh = oldExtent.getHigh().getCoordinateValues();
+        final long[] low = new long[newDim];
+        final long[] high = new long[newDim];
+        low[idx] = oldlow[oldidx];
+        low[idx+1] = oldlow[oldidx+1];
+        high[idx] = oldhigh[oldidx];
+        high[idx+1] = oldhigh[oldidx+1];
+        final GridExtent extent = new GridExtent(null, low, high, true);
+
+        GridGeometry gridGeometry = new GridGeometry(extent, env);
+        gridGeometry = preserverRatio(gridGeometry);
+        setGridGeometry(gridGeometry);
+    }
+
+    /**
+     * @return a snapshot objective To display transform, in Pixel CENTER
+     */
+    public final AffineTransform2D getObjectiveToDisplay2D() {
+        try {
+            MathTransform gridToCRS = getGridGeometry2D().getGridToCRS(PixelInCell.CELL_CENTER);
+            return (AffineTransform2D) gridToCRS.inverse();
+        } catch (org.opengis.referencing.operation.NoninvertibleTransformException ex) {
+            throw new IllegalStateException(ex.getMessage(), ex);
+        }
+    }
+
+    public final AffineTransform2D getDisplayToObjective2D() {
+        MathTransform gridToCRS = getGridGeometry2D().getGridToCRS(PixelInCell.CELL_CENTER);
+        return (AffineTransform2D) gridToCRS;
+    }
+
+    /**
+     * Set the proportions support between X and Y axis.
+     * if false then no correction will be applied
+     * if true then one unit in X will be equal to one unit in Y
+     */
+    public final void setAxisProportions(final boolean prop) {
+        this.proportion = prop;
+    }
+
+    /**
+     *
+     * @return the X/Y proportion
+     */
+    public final boolean getAxisProportions() {
+        return proportion;
+    }
+
+    public final Rectangle2D getDisplayBounds() {
+
+        final GridGeometry gridGeometry = getGridGeometry();
+        final CoordinateReferenceSystem crs = gridGeometry.getCoordinateReferenceSystem();
+        final int idx = getHorizontalIndex(crs);
+
+        //we are expecting axis index to be preserved from grid to crs
+        final GridExtent extent = gridGeometry.getExtent().reduce(idx, idx+1);
+
+        final Rectangle2D.Double bounds = new Rectangle2D.Double();
+        bounds.x = extent.getLow(0);
+        bounds.y = extent.getLow(1);
+        bounds.width = extent.getSize(0);
+        bounds.height = extent.getSize(1);
+        return bounds;
     }
 
+    public void setDisplayBounds(Rectangle2D bounds) {
+        ArgumentChecks.ensureNonNull("Display bounds", bounds);
+
+        final GridGeometry gridGeometry = getGridGeometry();
+        final GridExtent extent = gridGeometry.getExtent();
+        final int idx = getHorizontalIndex(gridGeometry.getCoordinateReferenceSystem());
+
+        final long[] low = extent.getLow().getCoordinateValues();
+        low[idx] = (long) bounds.getMinX();
+        low[idx+1] = ((long) bounds.getMinY());
+        final long[] high = extent.getHigh().getCoordinateValues();
+        high[idx] = ((long) bounds.getMaxX()) - 1;
+        high[idx+1] = ((long) bounds.getMaxY()) - 1;
+        final GridExtent newExt = new GridExtent(null, low, high, true);
+
+        final GridGeometry newGrid = new GridGeometry(newExt, PixelInCell.CELL_CENTER, gridGeometry.getGridToCRS(PixelInCell.CELL_CENTER),
gridGeometry.getCoordinateReferenceSystem());
+        try {
+            setGridGeometry(newGrid);
+        } catch (FactoryException ex) {
+            //we are just changing the size, this should not cause the exception
+            //should not happen with current parameters
+            throw new BackingStoreException(ex.getMessage(), ex);
+        }
+    }
+
+    private GridGeometry preserverRatio(GridGeometry gridGeometry) {
+        if (proportion) {
+            final CoordinateReferenceSystem crs = gridGeometry.getCoordinateReferenceSystem();
+            final GridExtent extent = gridGeometry.getExtent();
+            final Envelope envelope = gridGeometry.getEnvelope();
+            final int idx = getHorizontalIndex(crs);
+            final long width = extent.getSize(idx);
+            final long height = extent.getSize(idx+1);
+            final double sx = envelope.getSpan(idx) / width;
+            final double sy = envelope.getSpan(idx+1) / height;
+            if (sx != sy) {
+                final GeneralEnvelope env = new GeneralEnvelope(envelope);
+                if (sx < sy) {
+                    double halfSpan = (sy * width / 2.0);
+                    double median = env.getMedian(idx);
+                    env.setRange(idx, median - halfSpan, median + halfSpan);
+                } else {
+                    double halfSpan = (sx * height / 2.0);
+                    double median = env.getMedian(idx+1);
+                    env.setRange(idx+1, median - halfSpan, median + halfSpan);
+                }
+                gridGeometry = new GridGeometry(extent, env);
+            }
+        }
+        return gridGeometry;
+    }
+
+    private static CoordinateReferenceSystem getHorizontalComponent(CoordinateReferenceSystem
envCRS) {
+        final List<SingleCRS> dcrss = CRS.getSingleComponents(envCRS);
+        // Following loop is a temporary hack for decomposing Geographic3D into Geographic2D
+ ellipsoidal height.
+        // This is a wrong thing to do according international standards; we will revisit
in a future version.
+        for (SingleCRS crs : dcrss) {
+            SingleCRS hcrs = CRS.getHorizontalComponent(crs);
+            if (hcrs != null) {
+                return hcrs;
+            }
+        }
+        throw new RuntimeException("Coordinate system has no horizontal component");
+    }
+
+    private static int getHorizontalIndex(CoordinateReferenceSystem envCRS) {
+        //set the extra xis if some exist
+        int index=0;
+        final List<SingleCRS> dcrss = CRS.getSingleComponents(envCRS);
+
+        // Following loop is a temporary hack for decomposing Geographic3D into Geographic2D
+ ellipsoidal height.
+        // This is a wrong thing to do according international standards; we will revisit
in a future version.
+        for (SingleCRS crs : dcrss) {
+            SingleCRS hcrs = CRS.getHorizontalComponent(crs);
+            if (hcrs != null) {
+                return index;
+            }
+            index += crs.getCoordinateSystem().getDimension();
+        }
+        throw new RuntimeException("Coordinate system has no horizontal component");
+    }
+
+    /**
+     * Transform the given envelope to the given crs.
+     * Unlike Envelopes.transform this method handle growing number of dimensions by filling
+     * other axes with default values.
+     *
+     * @param env source Envelope
+     * @param targetCRS target CoordinateReferenceSystem
+     * @return transformed envelope
+     */
+    private static Envelope transform(Envelope env, CoordinateReferenceSystem targetCRS)
throws TransformException{
+        try {
+            return Envelopes.transform(env, targetCRS);
+        } catch (TransformException ex) {
+            //we tried...
+        }
+
+        //lazy transform
+        final CoordinateReferenceSystem sourceCRS = env.getCoordinateReferenceSystem();
+        final GeneralEnvelope result = new GeneralEnvelope(targetCRS);
+
+        //decompose crs
+        final List<SingleCRS> sourceParts = getSingleComponents(sourceCRS);
+        final List<SingleCRS> targetParts = getSingleComponents(targetCRS);
+
+        int sourceAxeIndex=0;
+        sourceLoop:
+        for (CoordinateReferenceSystem sourcePart : sourceParts){
+            final int sourcePartDimension = sourcePart.getCoordinateSystem().getDimension();
+            int targetAxeIndex=0;
+
+            targetLoop:
+            for (CoordinateReferenceSystem targetPart : targetParts){
+                final int targetPartDimension = targetPart.getCoordinateSystem().getDimension();
+
+                //try conversion
+                try {
+                    final MathTransform trs = CRS.findOperation(sourcePart, targetPart, null).getMathTransform();
+                    //we could transform by using two coordinate, but envelope conversion
allows to handle
+                    //crs singularities more efficiently
+                    final GeneralEnvelope partSource = new GeneralEnvelope(sourcePart);
+                    for (int i=0;i<sourcePartDimension;i++){
+                        partSource.setRange(i, env.getMinimum(sourceAxeIndex+i), env.getMaximum(sourceAxeIndex+i));
+                    }
+                    final Envelope partResult = Envelopes.transform(trs, partSource);
+                    for (int i=0;i<targetPartDimension;i++){
+                        result.setRange(targetAxeIndex+i, partResult.getMinimum(i), partResult.getMaximum(i));
+                    }
+                    break targetLoop;
+                } catch (FactoryException ex) {
+                    //we tried...
+                }
+
+                targetAxeIndex += targetPartDimension;
+            }
+            sourceAxeIndex += sourcePartDimension;
+        }
+
+        return result;
+    }
 }
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapItem.java b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapItem.java
index e47a03d..c7e64e7 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapItem.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapItem.java
@@ -23,8 +23,6 @@ package org.apache.sis.internal.map;
  * NOTE: this class is a first draft subject to modifications.
  * </p>
  *
- * @todo Rename {@code MapElement} or {@code Element}?
- *
  * @author  Johann Sorel (Geomatys)
  * @version 1.0
  * @since   1.0
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapLayer.java b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapLayer.java
index a2a1fbe..cae70ea 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapLayer.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapLayer.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.map;
 
+import org.apache.sis.storage.Query;
 import org.apache.sis.storage.Resource;
 import org.opengis.style.Style;
 
@@ -31,6 +32,7 @@ import org.opengis.style.Style;
  *
  * <p>
  * NOTE: this class is a first draft subject to modifications.
+ * TODO : missing events
  * </p>
  *
  * @author  Johann Sorel (Geomatys)
@@ -50,6 +52,11 @@ public class MapLayer extends MapItem {
     private Style style;
 
     /**
+     * User defined query.
+     */
+    private Query query;
+
+    /**
      * Constructs an initially empty map layer.
      *
      * @todo Expect {@code Resource} and {@code Style} in argument, for discouraging
@@ -101,4 +108,27 @@ public class MapLayer extends MapItem {
     public void setStyle(Style style) {
         this.style = style;
     }
+
+    /**
+     * Returns the definition query for this layer.If no definition
+     * query has been defined {@code null} is returned.
+     * @return query, or {@code null} if unspecified.
+     */
+    public Query getQuery() {
+        return query;
+    }
+
+    /**
+     * Sets a filter query for this layer.
+     *
+     * <p>
+     * Query filters should be used to reduce searched or displayed resource
+     * when rendering or analyzing this layer.
+     * </p>
+     *
+     * @param query the query for this layer, or {@code null} if unavailable.
+     */
+    public void setQuery(Query query) {
+        this.query = query;
+    }
 }


Mime
View raw message