sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1786516 - in /sis/branches/JDK8/core: sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/ sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/ sis-referencing/src/main/java/org/a...
Date Sat, 11 Mar 2017 17:22:01 GMT
Author: desruisseaux
Date: Sat Mar 11 17:22:01 2017
New Revision: 1786516

URL: http://svn.apache.org/viewvc?rev=1786516&view=rev
Log:
First bug fix in encode(areaOfInterest): return also the cells that are on the left side of
UTM zones.

Added:
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationViewer.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java?rev=1786516&r1=1786515&r2=1786516&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
[UTF-8] Sat Mar 11 17:22:01 2017
@@ -592,6 +592,7 @@ public class MilitaryGridReferenceSystem
         /**
          * Returns an iterator over all MGRS references that intersect the given envelope.
          * The given envelope must have a Coordinate Reference System (CRS) associated to
it.
+         * The MGRS references may be returned in any iteration order.
          *
          * @param  areaOfInterest  envelope of desired MGRS references.
          * @return an iterator over MGRS references intersecting the given area of interest.
@@ -609,6 +610,7 @@ public class MilitaryGridReferenceSystem
         /**
          * Returns a stream of all MGRS references that intersect the given envelope.
          * The given envelope must have a Coordinate Reference System (CRS) associated to
it.
+         * The MGRS references may be returned in any order.
          *
          * @param  areaOfInterest  envelope of desired MGRS references.
          * @param  parallel        {@code true} for a parallel stream, or {@code false} for
a sequential stream.
@@ -672,6 +674,10 @@ public class MilitaryGridReferenceSystem
                             org.apache.sis.internal.referencing.Resources.Keys.NonHorizontalCRS_1,
"areaOfInterest"));
                 }
                 final int precision = (int) getPrecision();
+                if (precision <= 0 || precision > (int) GRID_SQUARE_SIZE) {
+                    throw new GazetteerException(Errors.format(Errors.Keys.ValueOutOfRange_4,
+                            "precision", 1, (int) GRID_SQUARE_SIZE, precision));
+                }
                 final IntervalRectangle aoi = new IntervalRectangle(areaOfInterest);
                 final Envelope geographicArea = Envelopes.transform(areaOfInterest, datum.normalizedGeographic());
                 final double φmin = geographicArea.getMinimum(1);
@@ -826,6 +832,8 @@ public class MilitaryGridReferenceSystem
          * The envelope for which to return MGRS codes. This envelope can be in any CRS.
          * This rectangle shall not be modified since the same reference will be shared
          * by many {@code IteratorOneZone}s.
+         *
+         * <p><b>Note:</b> {@link #optimize} must be {@code false} if this
shape is not a rectangle.</p>
          */
         private final Shape areaOfInterest;
 
@@ -840,6 +848,13 @@ public class MilitaryGridReferenceSystem
         private final Encoder encoder;
 
         /**
+         * The easting value in the zone center. This information is used for determining
which envelope
+         * corner is closest to the map projection center. There is no {@code yCenter} case
since this
+         * more complicated case is handled by {@link #scaleToLBC} and {@link #offsetToLBC}.
+         */
+        private final int xCenter;
+
+        /**
          * The first <var>easting</var> value to use in iteration. The {@link
#gridX} value will need to be reset
          * to this value or to a greater value for each new row.
          */
@@ -874,6 +889,28 @@ public class MilitaryGridReferenceSystem
         private final int step;
 
         /**
+         * Whether this iterator should iterates downward over rows. If {@code true}, then
{@link #step} shall be
+         * subtracted to {@link #gridY} instead of added. We iterate downward in UTM south
zones in order to go
+         * from equator to pole. This direction allows some optimizations.
+         */
+        private final boolean downward;
+
+        /**
+         * Whether this iterator is allowed to skip some cells when testing for inclusion
in the area of interest.
+         * Since {@code IteratorOneZone} iterates in UTM zone from equator to pole, the range
of longitude values
+         * will only decrease (the minimal longitude increase and the maximal longitude decrease).
Consequently
+         * if we found a longitude out of range, we don't need to test that longitude again
in next row.
+         *
+         * <p>This optimization is allowed only under the following conditions:</p>
+         * <ul>
+         *   <li>{@link #areaOfInterest} is a rectangle in geographic coordinates.</li>
+         *   <li>{@link #areaOfInterest} does not intersect Norway and Svalbard special
cases.</li>
+         *   <li>We iterate in a UTM zone, or in a UPS zone contained fully in the
upper half or fully in lower half.</li>
+         * </ul>
+         */
+        private final boolean optimize;
+
+        /**
          * Temporary rectangle for computation purpose.
          */
         private final IntervalRectangle cell;
@@ -903,6 +940,7 @@ public class MilitaryGridReferenceSystem
              */
             final int zone = Math.abs(encoder.crsZone);
             if (zone == Encoder.POLE) {
+                xCenter = PolarStereographicA.UPS_SHIFT;
                 if (encoder.crsZone < 0) {
                     cell.ymin = Latitude.MIN_VALUE;
                     cell.ymax = TransverseMercator.Zoner.SOUTH_BOUNDS;
@@ -913,6 +951,7 @@ public class MilitaryGridReferenceSystem
                 cell.xmin = Longitude.MIN_VALUE;
                 cell.xmax = Longitude.MAX_VALUE;
             } else {
+                xCenter = (int) ZONER.easting;
                 if (encoder.crsZone < 0) {
                     cell.ymin = TransverseMercator.Zoner.SOUTH_BOUNDS;
                 } else {
@@ -928,6 +967,7 @@ public class MilitaryGridReferenceSystem
             if ((t = geographicArea.getMaximum(1)) <= cell.ymax) cell.ymax = t; else clip
= true;
             if ((t = geographicArea.getMinimum(0)) >= cell.xmin) cell.xmin = t; else clip
= true;
             if ((t = geographicArea.getMaximum(0)) <= cell.xmax) cell.xmax = t; else clip
= true;
+            boolean isSpecialCase = ZONER.isSpecialCase(cell.ymin, cell.ymax, cell.xmin,
cell.xmax);
             if (clip) {
                 /*
                  * If we detected that the given area of interest is larger than UPS or UTM
domain of validity
@@ -951,11 +991,25 @@ public class MilitaryGridReferenceSystem
             xEnd   = (((int) (bounds.getMaxX() / step)) + 1) * step;
             yEnd   = (((int) (bounds.getMaxY() / step)) + 1) * step;
             xStart = gridX;
-            if (encoder.crsZone < 0) {      // For South hemisphere, we will iterate on
y axis in reverse order.
+            /*
+             * Determine if we should iterate on rows upward or downward. The intend is to
iterate from equator to pole
+             * in UTM zones, or from projection center to projection border in UPS cases.
 Those directions enable some
+             * optimizations.
+             */
+            if (zone != Encoder.POLE) {
+                downward = (encoder.crsZone < 0);           // Upward in UTM North zones,
downward in UTM South zones.
+            } else {
+                downward = yEnd <= PolarStereographicA.UPS_SHIFT;   // Downward if the
AOI is fully in the lower half.
+                if (!downward && gridY < PolarStereographicA.UPS_SHIFT) {
+                    isSpecialCase = true;           // If AOI overlaps both lower and upper
half, we can not optimize.
+                }
+            }
+            if (downward) {                     // For South hemisphere, we will iterate
on y axis in reverse order.
                 final int y = gridY;
                 gridY = yEnd - step;
                 yEnd  = y    - step;
             }
+            optimize = !isSpecialCase && Utilities.equalsIgnoreMetadata(geographicArea.getCoordinateReferenceSystem(),
sourceCRS);
             gridToAOI = op.inverse();
         }
 
@@ -989,6 +1043,8 @@ public class MilitaryGridReferenceSystem
 
         /**
          * Implementation of {@link #tryAdvance(Consumer)} and {@link #forEachRemaining(Consumer)}.
+         * The {@code all} argument specifies whether this method is invoked for a single
element
+         * or for all remaining ones.
          */
         private boolean advance(final Consumer<? super String> action, final boolean
all) {
             final int digits = digits();
@@ -999,25 +1055,54 @@ public class MilitaryGridReferenceSystem
             boolean found = false;
             try {
                 do {
+                    /*
+                     * Verifies if the current cell should be accepted.
+                     * To be accepted, the cell must complies with two conditions:
+                     *
+                     *   1) It must be inside the area of interest (AOI). Note that the AOI
may be in any CRS.
+                     *   2) It must be inside the area of validity (AOV). Since the constructor
clipped the AOI
+                     *      to the area of validity, this test #2 is redundant with test
#1 if both AOI and AOV
+                     *      use the same CRS. However if AOI and AOV do not use the same
CRS, then condition #1
+                     *      does not automatically implies condition #2, so we test both.
+                     *
+                     * Condition #1 is verified by the call to 'areaOfInterest.intersects(…)'
below.
+                     * Condition #2 is verified indirectly by the call to 'encoder.encode(…)',
which return null
+                     * if the given point is outside the area of validity.  However since
'encode(…)' verifies a
+                     * position rather than envelope, we need to give it the corner closest
to projection center.
+                     * If we don't, 'encode(…)' may consider that a position is outside
the domain of validity
+                     * while another corner of the same envelope would have been considered
inside.
+                     */
                     cell.setRect(gridX, gridY, step, step);
                     if (areaOfInterest.intersects(Shapes2D.transform(gridToAOI, cell, cell)))
{
                         int x = gridX;
-                        int y = gridY;
-                        // TODO: ajdust (x,y) to the corner closest to projection natural
origin.
+                        if (x < xCenter) {
+                            // Use the 'x' value closest to projection center (see above
comment for explanation),
+                            // minus one metre for preventing the position to be considered
as part of next cell.
+                            x += step - 1;
+                        }
                         normalized.setOrdinate(0, x);
-                        normalized.setOrdinate(1, y);
+                        normalized.setOrdinate(1, gridY);
                         final String ref = encoder.encode(this, normalized, false, separator,
digits);
                         if (ref != null) {
                             action.accept(ref);
                             found = true;
                         }
+                    } else if (optimize) {
+                        if (gridX < xCenter) {
+                            xStart = gridX + step;  // Next rows can ignore cells at or before
current 'gridX' position.
+                        } else {
+                            xEnd = gridX;           // Next rows can ignore cells at or after
current 'gridX' position.
+                        }
                     }
+                    /*
+                     * Move to the next cell. We need to do that regardless if the previous
block found a cell or not.
+                     */
                     if ((gridX += step) >= xEnd) {
                         gridX = xStart;
-                        if (encoder.crsZone >= 0) {
-                            if ((gridY += step) >= yEnd) break;         // North hemisphere.
+                        if (!downward) {
+                            if ((gridY += step) >= yEnd) break;         // UTM North or
upper part of UPS.
                         } else {
-                            if ((gridY -= step) <= yEnd) break;         // South hemisphere.
+                            if ((gridY -= step) <= yEnd) break;         // UTM South or
lower part of UPS.
                         }
                     }
                 } while (all || !found);
@@ -1320,7 +1405,7 @@ public class MilitaryGridReferenceSystem
                      * over with column letter A.
                      */
                     final byte[] columns = POLAR_COLUMNS;
-                    col -= (int) (PolarStereographicA.UPS_SHIFT / GRID_SQUARE_SIZE);
+                    col -= PolarStereographicA.UPS_SHIFT / GRID_SQUARE_SIZE;
                     if (!(λ >= 0)) {                    // Same condition than in GZD
block. Use of ! is for NaN.
                         col += columns.length;          // Letters Z to A from right to left.
                     }
@@ -1514,7 +1599,7 @@ parse:                  switch (part) {
                                 col = Arrays.binarySearch(POLAR_COLUMNS, (byte) c);
                                 if (col < 0) break;                                  
  // Invalid column letter.
                                 if (west) col -= POLAR_COLUMNS.length;
-                                col += (int) (PolarStereographicA.UPS_SHIFT / GRID_SQUARE_SIZE);
+                                col += PolarStereographicA.UPS_SHIFT / GRID_SQUARE_SIZE;
                                 i = nextComponent(owner, reference, base, ni, end);
                                 continue;
                             }
@@ -1771,15 +1856,10 @@ parse:                  switch (part) {
                  * if the given 100 kilometres square identification is consistent with grid
zone designation.
                  * We verify both φ and λ, but the verification of φ is actually redundant
with the check of
                  * 100 km square validity that we did previously with the help of ROW_RESOLVER
bitmask.
-                 * We check φ anyway in case of bug, but we have to allow a tolerance threshold
on the south
-                 * bound because the 100 km square may overlap two latitude bands. We do
not need equivalent
-                 * tolerance threshold for the upper bound because the coordinate that we
are testing is the
-                 * lower-left corner of the cell area.
+                 * We check φ anyway in case of bug.
                  */
                 if (isValid && zone != 0) {
-                    final double λ = (westBoundLongitude + eastBoundLongitude) / 2;
-                    final double φ = (southBoundLatitude + northBoundLatitude) / 2;
-                    isValid = (φ >= φs - LATITUDE_BAND_HEIGHT/2) && (φ <
upperBound(φs));   // See above comment.
+                    isValid = (northBoundLatitude >= φs) && (southBoundLatitude
< upperBound(φs));
                     if (isValid) {
                         /*
                          * Verification of UTM zone. We allow a tolerance for latitudes close
to a pole because
@@ -1787,6 +1867,8 @@ parse:                  switch (part) {
                          * the neighbor zone at those high latitudes is less significant.
For other latitudes,
                          * we allow a tolerance if the point is close to a line of zone change.
                          */
+                        final double λ = (westBoundLongitude + eastBoundLongitude) / 2;
+                        final double φ = (southBoundLatitude + northBoundLatitude) / 2;
                         int zoneError = ZONER.zone(φ, λ) - zone;
                         if (zoneError != 0) {
                             final int zc = ZONER.zoneCount();

Added: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationViewer.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationViewer.java?rev=1786516&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationViewer.java
(added)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationViewer.java
[UTF-8] Sat Mar 11 17:22:01 2017
@@ -0,0 +1,194 @@
+/*
+ * 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.referencing.gazetteer;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.Iterator;
+import java.awt.Shape;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.BasicStroke;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import org.opengis.geometry.Envelope;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.gazetteer.Location;
+import org.opengis.referencing.operation.MathTransform2D;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.internal.referencing.j2d.IntervalRectangle;
+import org.apache.sis.geometry.Envelope2D;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.util.Debug;
+
+
+/**
+ * A Swing panel drawing {@link Location} instances.
+ * This is used for debugging purpose only.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+@Debug
+@SuppressWarnings("serial")
+public final class LocationViewer extends JPanel {
+    /**
+     * Shows the locations tested by {@link MilitaryGridReferenceSystemTest#testIterator()}.
+     *
+     * @param  args  ignored.
+     * @throws Exception if an error occurred while transforming an envelope to the display
CRS.
+     */
+    public static void main(final String[] args) throws Exception {
+        final MilitaryGridReferenceSystem.Coder coder = new MilitaryGridReferenceSystem().createCoder();
+        coder.setPrecision(100000);
+        show(coder, new Envelope2D(CommonCRS.defaultGeographic(), 5, 47, 8, 10), CommonCRS.WGS84.universal(1,
9));
+    }
+
+    /**
+     * The coordinate reference system to use for displaying the shapes.
+     */
+    private final CoordinateReferenceSystem displayCRS;
+
+    /**
+     * The locations to show, together with their label.
+     */
+    private final Map<String,Shape> locations;
+
+    /**
+     * Bounding box of all shapes in the {@link #locations} map.
+     */
+    private Rectangle2D bounds;
+
+    /**
+     * Creates a new, initially empty, viewer. Locations must be added by calls to {@code
addLocation(…)} methods
+     * before the widget can be show.
+     *
+     * @param displayCRS  the coordinate reference system to use for displaying the location
shapes.
+     */
+    public LocationViewer(final CoordinateReferenceSystem displayCRS) {
+        this.displayCRS = displayCRS;
+        this.locations  = new LinkedHashMap<>();
+        setBackground(Color.BLACK);
+    }
+
+    /**
+     * Shows all locations in the given area of interest.
+     *
+     * @param  coder           the encoder to use for computing locations and their envelopes.
+     * @param  areaOfInterest  the geographic or projected area where to get locations.
+     * @param  displayCRS      the CRS to use for displaying the location shapes, or {@code
null} for the envelope CRS.
+     * @throws FactoryException if a transformation to the display CRS can not be obtained.
+     * @throws TransformException if an error occurred while transforming an envelope.
+     */
+    public static void show(final MilitaryGridReferenceSystem.Coder coder, final Envelope
areaOfInterest,
+            CoordinateReferenceSystem displayCRS) throws FactoryException, TransformException
+    {
+        if (displayCRS == null) {
+            displayCRS = areaOfInterest.getCoordinateReferenceSystem();
+        }
+        final LocationViewer viewer = new LocationViewer(displayCRS);
+        viewer.addLocations(coder, areaOfInterest);
+        final JFrame frame = new JFrame("Locations viewer (debug)");
+        frame.getContentPane().add(viewer);
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.setSize(600, 600);
+        frame.setVisible(true);
+    }
+
+    /**
+     * Adds all locations in the given area of interest.
+     *
+     * @param  coder           the encoder to use for computing locations and their envelopes.
+     * @param  areaOfInterest  the geographic or projected area where to get locations.
+     * @throws FactoryException if a transformation to the display CRS can not be obtained.
+     * @throws TransformException if an error occurred while transforming an envelope.
+     */
+    public void addLocations(final MilitaryGridReferenceSystem.Coder coder, final Envelope
areaOfInterest)
+            throws FactoryException, TransformException
+    {
+        final Iterator<String> it = coder.encode(areaOfInterest);
+        while (it.hasNext()) {
+            final String code = it.next();
+            addLocation(code, coder.decode(code));
+        }
+    }
+
+    /**
+     * Adds the location identified by the given label
+     *
+     * @param  label     a label that identify the location to add.
+     * @param  location  the location to add to the list of locations shown by this widget.
+     * @throws FactoryException if a transformation to the display CRS can not be obtained.
+     * @throws TransformException if an error occurred while transforming an envelope.
+     */
+    public void addLocation(final String label, final Location location) throws FactoryException,
TransformException {
+        final Envelope envelope = location.getEnvelope();
+        final MathTransform2D tr = (MathTransform2D) CRS.findOperation(
+                envelope.getCoordinateReferenceSystem(), displayCRS, null).getMathTransform();
+        final Shape shape = tr.createTransformedShape(new IntervalRectangle(envelope));
+        if (locations.putIfAbsent(label, shape) != null) {
+            throw new IllegalArgumentException("A location is already defined for " + label);
+        }
+        final Rectangle2D b = shape.getBounds2D();
+        if (bounds == null) {
+            bounds = b;
+        } else {
+            bounds.add(b);
+        }
+    }
+
+    /**
+     * Invoked by Swing for painting this widget.
+     *
+     * @param g  the graphic context where to paint.
+     */
+    @Override
+    protected void paintComponent(final Graphics g) {
+        super.paintComponent(g);
+        final Graphics2D gr = (Graphics2D) g;
+        final AffineTransform oldTr = gr.getTransform();
+        final AffineTransform tr = AffineTransform.getScaleInstance(
+                getWidth()  / bounds.getWidth(),
+               -getHeight() / bounds.getHeight());
+        tr.translate(-bounds.getMinX(), -bounds.getMaxY());
+        gr.transform(tr);
+        gr.setColor(Color.YELLOW);
+        gr.setStroke(new BasicStroke(0));
+        for (final Shape location : locations.values()) {
+            gr.draw(location);
+        }
+        gr.setTransform(oldTr);
+        gr.setColor(Color.CYAN);
+        final Point2D.Double p = new Point2D.Double();
+        for (final Map.Entry<String,Shape> entry : locations.entrySet()) {
+            final Rectangle2D b = entry.getValue().getBounds2D();
+            p.x = b.getCenterX();
+            p.y = b.getCenterY();
+            final Point2D pt = tr.transform(p, p);
+            final String label = entry.getKey();
+            gr.drawString(label, (float) (pt.getX() - 4.5*label.length()), (float) pt.getY());
+        }
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationViewer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationViewer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java?rev=1786516&r1=1786515&r2=1786516&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
[UTF-8] Sat Mar 11 17:22:01 2017
@@ -16,6 +16,10 @@
  */
 package org.apache.sis.referencing.gazetteer;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.List;
 import java.util.Locale;
 import java.util.Random;
 import java.util.Iterator;
@@ -648,31 +652,33 @@ public final strictfp class MilitaryGrid
         final MilitaryGridReferenceSystem.Coder coder = coder();
         coder.setPrecision(100000);
         final Iterator<String> it = coder.encode(new Envelope2D(CommonCRS.defaultGeographic(),
5, 47, 8, 10));
-
+        /*
+         * Following is the list of MGRS references that we expect to find in the above area
of interest.
+         * The references are distributed in 3 zones (31, 32 and 33) and 3 latitude bands
(T, U and V).
+         * This test includes the Norway special case: between 56° and 64°N (latitude band
V), zone 32
+         * is widened to 9° at the expense of zone 31. The test needs to be insensitive
to iteration order.
+         */
         // TODO: following list of expected codes is not yet accurate.
-        final String[] expected = {
-            "31TFN", "31TGN", "31TFP", "31TGP",
-            "31UFQ", "31UGQ", "31UFR", "31UGR", "31UFS", "31UGS",
-            "31UFT", "31UGT", "31UFU", "31UGU", "31UFV", "31UGV",
-            "31UFA", "31UFB", "31UFC",
-            "32TLT", "32TMT", "32TNT", "32TPT", "32TQT",
-            "32TLU", "32TMU", "32TNU", "32TPU", "32TQU",
-            "32ULV", "32UMV", "32UNV", "32UPV", "32UQV",
-            "32ULA", "32UMA", "32UNA", "32UPA", "32UQA",
-            "32ULB", "32UMB", "32UNB", "32UPB", "32UQB",
-            "32ULC", "32UMC", "32UNC", "32UPC", "32UQC",
-            "32ULD", "32UMD", "32UND", "32UPD", "32UQD",
-            "32ULE", "32UME", "32UNE", "32UPE", "32UQE",
-                     "32UMF", "32UNF", "32UPF",
-                     "32UMG", "32UNG", "32UPG",
-                     "32UMH", "32UNH", "32UPH",
-            "32VKJ", "32VLJ", "32VMJ", "32VNJ", "32VPJ",
-            "33TUN", "33TUP",
-            "33UUQ", "33UUR", "33UUS", "33UUT", "33UUU", "33UUV"
-        };
-        for (final String e : expected) {
-            assertTrue(e, it.hasNext());
-            assertEquals(e, it.next());
+        final List<String> expected = Arrays.asList(
+            "31TFN", "31TGN",    "32TKT", "32TLT", "32TMT", "32TNT", "32TPT", "32TQT",  
 "33TTN", "33TUN", "33TTP", "33TUP",
+            "31TFP", "31TGP",    "32TKU", "32TLU", "32TMU", "32TNU", "32TPU", "32TQU",
+            "31UFQ", "31UGQ",    "32UKV", "32ULV", "32UMV", "32UNV", "32UPV", "32UQV",  
 "33UTQ", "33UUQ",
+            "31UFR", "31UGR",    "32UKA", "32ULA", "32UMA", "32UNA", "32UPA", "32UQA",  
 "33UTR", "33UUR",
+            "31UFS", "31UGS",    "32UKB", "32ULB", "32UMB", "32UNB", "32UPB", "32UQB",  
 "33UTS", "33UUS",
+            "31UFT", "31UGT",    "32UKC", "32ULC", "32UMC", "32UNC", "32UPC", "32UQC",  
 "33UTT", "33UUT",
+            "31UFU", "31UGU",    "32UKD", "32ULD", "32UMD", "32UND", "32UPD", "32UQD",  
 "33UTU", "33UUU",
+            "31UFV", "31UGV",    "32UKE", "32ULE", "32UME", "32UNE", "32UPE", "32UQE",  
 "33UTV", "33UUV",
+            "31UFA",                      "32ULF", "32UMF", "32UNF", "32UPF",           
 "33UUA",
+            "31UFB",                      "32ULG", "32UMG", "32UNG", "32UPG",           
 "33UUB",
+            "31UFC",                      "32ULH", "32UMH", "32UNH", "32UPH",           
 "33UUC",
+            /* Norway case */    "32VKJ", "32VLJ", "32VMJ", "32VNJ", "32VPJ",           
 "33VUD");
+
+        final Set<String> remaining = new HashSet<>(expected);
+        assertEquals("List of expected codes has duplicated values.", expected.size(), remaining.size());
+        while (it.hasNext()) {
+            final String code = it.next();
+            assertTrue(code, remaining.remove(code));
         }
+        assertTrue(remaining.toString(), remaining.isEmpty());
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java?rev=1786516&r1=1786515&r2=1786516&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
[UTF-8] Sat Mar 11 17:22:01 2017
@@ -111,8 +111,10 @@ public final class PolarStereographicA e
 
     /**
      * False Easting and false Northing value used in Universal Polar Stereographic (UPS)
projections.
+     * Represented as an integer for the convenience of Military Reference Grid System (MGRS)
or other
+     * grid systems.
      */
-    public static final double UPS_SHIFT = 2000000;
+    public static final int UPS_SHIFT = 2000000;
 
     /**
      * Sets the parameter values for a Universal Polar Stereographic projection

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java?rev=1786516&r1=1786515&r2=1786516&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
[UTF-8] Sat Mar 11 17:22:01 2017
@@ -180,6 +180,14 @@ public final class TransverseMercator ex
                 }
                 return false;
             }
+
+            /** Indicates whether the given geographic area intersects the regions that need
to be handled in a special way. */
+            @Override public boolean isSpecialCase(final double φmin, final double φmax,
final double λmin, final double λmax) {
+                if (φmax >= NORWAY_BOUNDS && φmin < NORTH_BOUNDS) {
+                    return super.zone(0, λmax) >= 31 && super.zone(0, λmin)
<= 37;
+                }
+                return false;
+            }
         },
 
         /**
@@ -372,6 +380,19 @@ public final class TransverseMercator ex
         }
 
         /**
+         * Indicates whether the given geographic area intersects the regions that need to
be handled in a special way.
+         *
+         * @param  φmin  southernmost latitude in degrees.
+         * @param  φmax  northernmost latitude in degrees.
+         * @param  λmin  westernmost longitude in degrees.
+         * @param  λmax  easternmost longitude in degrees.
+         * @return whether the given area intersects a region that needs to be handled as
a special case.
+         */
+        public boolean isSpecialCase(final double φmin, final double φmax, final double
λmin, final double λmax) {
+            return false;
+        }
+
+        /**
          * First exception in UTM projection, corresponding to latitude band V.
          * This method is public for {@code MilitaryGridReferenceSystemTest.verifyZonerConsistency()}
purpose only.
          *
@@ -379,7 +400,7 @@ public final class TransverseMercator ex
          * @return whether the given latitude is in the Norway latitude band.
          */
         public static boolean isNorway(final double φ) {
-            return (φ >= 56) && (φ < 64);
+            return (φ >= NORWAY_BOUNDS) && (φ < 64);
         }
 
         /**
@@ -401,6 +422,12 @@ public final class TransverseMercator ex
         public static final double SOUTH_BOUNDS = -80;
 
         /**
+         * Southernmost bounds (inclusive) of the latitude band that contains Norway ({@code
'V'}).
+         * This is the first latitude band where we may need to handle special cases (Norway
and Svalbard).
+         */
+        private static final double NORWAY_BOUNDS = 56;
+
+        /**
          * Southernmost bounds (inclusive) of the last latitude band, which contains Svalbard.
          * This latitude band is 12° height instead of 8°.
          */



Mime
View raw message