Search in sources :

Example 1 with Vector

use of org.apache.sis.math.Vector in project sis by apache.

the class ESRI method createPolyline.

/**
 * Creates a polyline from the given ordinate values.
 * Each {@link Double#NaN} ordinate value start a new path.
 * The implementation returned by this method must be an instance of {@link #rootClass}.
 */
@Override
public Geometry createPolyline(final int dimension, final Vector... ordinates) {
    if (dimension != 2) {
        throw unsupported(dimension);
    }
    boolean lineTo = false;
    final Polyline path = new Polyline();
    for (final Vector v : ordinates) {
        if (v != null) {
            final int size = v.size();
            for (int i = 0; i < size; ) {
                final double x = v.doubleValue(i++);
                final double y = v.doubleValue(i++);
                if (Double.isNaN(x) || Double.isNaN(y)) {
                    lineTo = false;
                } else if (lineTo) {
                    path.lineTo(x, y);
                } else {
                    path.startPath(x, y);
                    lineTo = true;
                }
            }
        }
    }
    return path;
}
Also used : Polyline(com.esri.core.geometry.Polyline) Vector(org.apache.sis.math.Vector) Point(com.esri.core.geometry.Point)

Example 2 with Vector

use of org.apache.sis.math.Vector in project sis by apache.

the class MovingFeature method storeGeometry.

/**
 * Sets the geometry of the given attribute to the values collected by this {@code MovingFeatures}.
 * This method sets also the {@code "datetimes"} characteristic.
 *
 * @param  <G>              the type of the geometry value.
 * @param  featureName      the name of the feature containing the attribute to update, for logging purpose.
 * @param  index            index of the property for which geometry value is desired.
 * @param  dimension        number of dimensions for all coordinates.
 * @param  factory          the factory to use for creating the geometry object.
 * @param  dest             attribute where to store the geometry value.
 * @param  warningListener  where to report warnings. Implementation should set the source class name,
 *                          source method name and logger name, then forward to a {@code WarningListener}.
 */
public final <G> void storeGeometry(final String featureName, final int index, final int dimension, final Geometries<G> factory, final AbstractAttribute<G> dest, final Consumer<LogRecord> warningListener) {
    int n = count[index];
    final Vector[] vectors = new Vector[n];
    for (Period p = properties[index]; p != null; p = p.previous) {
        vectors[--n] = Vector.create(p.value, false);
    }
    if (n != 0) {
        // Should never happen unless this object has been modified concurrently in another thread.
        throw new CorruptedObjectException();
    }
    // Maximal number of warnings, for avoiding to flood the logger.
    int warnings = 10;
    // Total number of points in all vectors, ignoring null vectors.
    int numPts = 0;
    // If non-null, shall be non-empty.
    Vector previous = null;
    for (int i = 0; i < vectors.length; i++) {
        Vector v = vectors[i];
        int length;
        if (v == null || (length = v.size()) == 0) {
            continue;
        }
        if ((length % dimension) != 0) {
            if (--warnings >= 0) {
                Period p = properties[index];
                for (int j = i; --j >= 0; ) {
                    // This is inefficient but used only in case of warnings.
                    p = p.previous;
                }
                warningListener.accept(Resources.forLocale(null).getLogRecord(Level.WARNING, Resources.Keys.UnexpectedNumberOfOrdinates_4, featureName, new Date(p.startTime), dimension, length));
            }
            continue;
        }
        /*
             * At this point we have a non-empty valid sequence of ordinate values. If the first point of current
             * vector is equals to the last point of previous vector, assume that they form a continuous polyline.
             */
        if (previous != null) {
            if (equals(previous, v, dimension)) {
                // Skip the first coordinate.
                v = v.subList(dimension, length);
                length -= dimension;
                if (length == 0) {
                    vectors[i] = null;
                    continue;
                }
                vectors[i] = v;
            }
        }
        numPts += length;
        previous = v;
    }
    /*
         * At this point we got the list of all coordinates to join together in a polyline.
         * We will create the geometry at the end of this method. Before that, interpolate
         * the dates and times.
         */
    int i = vectors.length;
    numPts /= dimension;
    final long[] times = new long[numPts];
    for (Period p = properties[index]; p != null; p = p.previous) {
        final Vector v = vectors[--i];
        if (v != null) {
            int c = v.size() / dimension;
            if (c == 1) {
                times[--numPts] = p.endTime;
            } else {
                final long startTime = p.startTime;
                final double scale = (p.endTime - startTime) / (double) (c - 1);
                while (--c >= 0) {
                    times[--numPts] = startTime + Math.round(scale * c);
                }
            }
        }
    }
    if (numPts != 0) {
        // Should never happen unless this object has been modified concurrently in another thread.
        throw new CorruptedObjectException();
    }
    /*
         * Store the geometry and characteristics in the attribute.
         */
    dest.setValue(factory.createPolyline(dimension, vectors));
    final AbstractAttribute<Instant> c = TIME.newInstance();
    c.setValues(new DateList(times));
    dest.characteristics().values().add(c);
}
Also used : Instant(java.time.Instant) CorruptedObjectException(org.apache.sis.util.CorruptedObjectException) Vector(org.apache.sis.math.Vector) Date(java.util.Date)

Example 3 with Vector

use of org.apache.sis.math.Vector in project sis by apache.

the class VariableTest method testRead1D.

/**
 * Tests {@link Variable#read()} on a one-dimensional variable.
 *
 * @throws IOException if an error occurred while reading the netCDF file.
 * @throws DataStoreException if a logical error occurred.
 */
@Test
public void testRead1D() throws IOException, DataStoreException {
    final Variable variable = selectDataset(NCEP).getVariables()[25];
    assertEquals("lon", variable.getName());
    final Vector data = variable.read();
    assertEquals("lon", Float.class, data.getElementType());
    final int length = data.size();
    assertEquals("length", 73, length);
    for (int i = 0; i < length; i++) {
        assertEquals("Longitude value", -180 + 5 * i, data.floatValue(i), 0f);
    }
}
Also used : Vector(org.apache.sis.math.Vector) Test(org.junit.Test)

Example 4 with Vector

use of org.apache.sis.math.Vector in project sis by apache.

the class GridGeometry method localizationGrid.

/**
 * Builds a localization grid from the given GeoTIFF tie points.
 * This method may invoke itself recursively.
 *
 * @param  modelTiePoints  the model tie points read from GeoTIFF file.
 * @param  addTo           if non-null, add the transform result to this map.
 */
private static MathTransform localizationGrid(final Vector modelTiePoints, final Map<Envelope, MathTransform> addTo) throws FactoryException, TransformException {
    final int size = modelTiePoints.size();
    final int n = size / RECORD_LENGTH;
    if (n == 0)
        return null;
    final Vector x = modelTiePoints.subSampling(0, RECORD_LENGTH, n);
    final Vector y = modelTiePoints.subSampling(1, RECORD_LENGTH, n);
    try {
        final LocalizationGridBuilder grid = new LocalizationGridBuilder(x, y);
        final LinearTransform sourceToGrid = grid.getSourceToGrid();
        final double[] ordinates = new double[2];
        for (int i = 0; i < size; i += RECORD_LENGTH) {
            ordinates[0] = modelTiePoints.doubleValue(i);
            ordinates[1] = modelTiePoints.doubleValue(i + 1);
            sourceToGrid.transform(ordinates, 0, ordinates, 0, 1);
            grid.setControlPoint(Math.toIntExact(Math.round(ordinates[0])), Math.toIntExact(Math.round(ordinates[1])), modelTiePoints.doubleValue(i + 3), modelTiePoints.doubleValue(i + 4));
        }
        grid.setDesiredPrecision(PRECISION);
        final MathTransform tr = grid.create(null);
        if (addTo != null && addTo.put(grid.getSourceEnvelope(), tr) != null) {
            // Should never happen. If it does, we have a bug in our algorithm.
            throw new FactoryException();
        }
        return tr;
    } catch (ArithmeticException | FactoryException e) {
        /*
             * May happen when the model tie points are not distributed on a regular grid.
             * For example Sentinel 1 images may have tie points spaced by 1320 pixels on the X axis,
             * except the very last point which is only 1302 pixels after the previous one. We try to
             * handle such grids by splitting them in two parts: one grid for the columns where points
             * are spaced by 1320 pixels and one grid for the last column. Such splitting needs to be
             * done horizontally and vertically, which result in four grids:
             *
             *    ┌──────────────────┬───┐
             *    │                  │   │
             *    │         0        │ 1 │
             *    │                  │   │
             *    ├──────────────────┼───┤ splitY
             *    │         2        │ 3 │
             *    └──────────────────┴───┘
             *                    splitX
             */
        final Set<Double> uniques = new HashSet<>(100);
        final double splitX = threshold(x, uniques);
        final double splitY = threshold(y, uniques);
        if (Double.isNaN(splitX) && Double.isNaN(splitY)) {
            // Can not do better. Report the failure.
            throw e;
        }
        final int[][] indices = new int[4][size];
        final int[] lengths = new int[4];
        for (int i = 0; i < size; ) {
            final double px = modelTiePoints.doubleValue(i);
            final double py = modelTiePoints.doubleValue(i + 1);
            // Number of the part where to add current point.
            int part = 0;
            // Point will be added to part #1 or #3.
            if (px > splitX)
                part = 1;
            // Point will be added to part #2 or #3.
            if (py > splitY)
                part |= 2;
            // Bitmask of the parts where to add the point.
            int parts = 1 << part;
            // Add also the point to part #1 or #3.
            if (px == splitX)
                parts |= 1 << (part | 1);
            // Add also the point to part #2 or #3.
            if (py == splitY)
                parts |= 1 << (part | 2);
            if (parts == 0b0111) {
                // Add also the point to part #3.
                parts = 0b1111;
                assert px == splitX && py == splitY;
            }
            final int upper = i + RECORD_LENGTH;
            do {
                part = Integer.numberOfTrailingZeros(parts);
                @SuppressWarnings("MismatchedReadAndWriteOfArray") final int[] tileIndices = indices[part];
                int k = lengths[part];
                for (int j = i; j < upper; j++) {
                    tileIndices[k++] = j;
                }
                lengths[part] = k;
            } while (// Clear the bit of the part we processed.
            (parts &= ~(1 << part)) != 0);
            i = upper;
        }
        /*
             * At this point, we finished to collect indices of the points to use for parts #0, 1, 2 and 3.
             * Verify that each part has less points than the initial vector (otherwise it would be a bug),
             * and identify which part is the biggest one. This is usually part #0.
             */
        int maxLength = 0;
        int largestPart = 0;
        for (int i = 0; i < indices.length; i++) {
            final int length = lengths[i];
            // Safety against infinite recursivity.
            if (length >= size)
                throw e;
            indices[i] = Arrays.copyOf(indices[i], length);
            if (length > maxLength) {
                maxLength = length;
                largestPart = i;
            }
        }
        /*
             * The biggest part will define the global transform. All other parts will define a specialization
             * valid only in a sub-area. Put those information in a map for MathTransforms.specialize(…).
             */
        MathTransform global = null;
        final Map<Envelope, MathTransform> specialization = new LinkedHashMap<>(4);
        for (int i = 0; i < indices.length; i++) {
            final Vector sub = modelTiePoints.pick(indices[i]);
            if (i == largestPart) {
                global = localizationGrid(sub, null);
            } else {
                localizationGrid(sub, specialization);
            }
        }
        return MathTransforms.specialize(global, specialization);
    }
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) MathTransform(org.opengis.referencing.operation.MathTransform) FactoryException(org.opengis.util.FactoryException) LinearTransform(org.apache.sis.referencing.operation.transform.LinearTransform) LocalizationGridBuilder(org.apache.sis.referencing.operation.builder.LocalizationGridBuilder) Vector(org.apache.sis.math.Vector) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map)

Example 5 with Vector

use of org.apache.sis.math.Vector in project sis by apache.

the class Java2D method createPolyline.

/**
 * Creates a path from the given ordinate values.
 * Each {@link Double#NaN} ordinate value start a new path.
 * The implementation returned by this method must be an instance of {@link #rootClass}.
 */
@Override
public Shape createPolyline(final int dimension, final Vector... ordinates) {
    if (dimension != 2) {
        throw unsupported(dimension);
    }
    /*
         * Computes the total length of all vectors and verifies if all values
         * can be casted to float without precision lost.
         */
    int length = 0;
    boolean isFloat = true;
    for (final Vector v : ordinates) {
        if (v != null) {
            length = Math.addExact(length, v.size());
            if (isFloat) {
                for (int i = v.size(); --i >= 0; ) {
                    final double value = v.doubleValue(i);
                    if (Double.doubleToRawLongBits(value) != Double.doubleToRawLongBits((float) value)) {
                        isFloat = false;
                        break;
                    }
                }
            }
        }
    }
    /*
         * Note: Point2D is not an instance of Shape, so we can not make a special case for it.
         */
    length /= 2;
    if (length == 2 && ordinates.length == 1) {
        final Vector v = ordinates[0];
        final double x1, y1, x2, y2;
        if (!Double.isNaN(x1 = v.doubleValue(0)) && !Double.isNaN(y1 = v.doubleValue(1)) && !Double.isNaN(x2 = v.doubleValue(2)) && !Double.isNaN(y2 = v.doubleValue(3))) {
            final Line2D path = isFloat ? new Line2D.Float() : new Line2D.Double();
            path.setLine(x1, y1, x2, y2);
            return path;
        }
    }
    final Path2D path = isFloat ? new Path2D.Float(Path2D.WIND_NON_ZERO, length) : new Path2D.Double(Path2D.WIND_NON_ZERO, length);
    boolean lineTo = false;
    for (final Vector v : ordinates) {
        final int size = v.size();
        for (int i = 0; i < size; ) {
            final double x = v.doubleValue(i++);
            final double y = v.doubleValue(i++);
            if (Double.isNaN(x) || Double.isNaN(y)) {
                lineTo = false;
            } else if (lineTo) {
                path.lineTo(x, y);
            } else {
                path.moveTo(x, y);
                lineTo = true;
            }
        }
    }
    return ShapeUtilities.toPrimitive(path);
}
Also used : Path2D(java.awt.geom.Path2D) Vector(org.apache.sis.math.Vector) Line2D(java.awt.geom.Line2D)

Aggregations

Vector (org.apache.sis.math.Vector)6 Point (com.esri.core.geometry.Point)1 Polyline (com.esri.core.geometry.Polyline)1 Line2D (java.awt.geom.Line2D)1 Path2D (java.awt.geom.Path2D)1 Instant (java.time.Instant)1 Date (java.util.Date)1 HashSet (java.util.HashSet)1 LinkedHashMap (java.util.LinkedHashMap)1 Map (java.util.Map)1 Set (java.util.Set)1 LocalizationGridBuilder (org.apache.sis.referencing.operation.builder.LocalizationGridBuilder)1 LinearTransform (org.apache.sis.referencing.operation.transform.LinearTransform)1 CorruptedObjectException (org.apache.sis.util.CorruptedObjectException)1 Test (org.junit.Test)1 MathTransform (org.opengis.referencing.operation.MathTransform)1 FactoryException (org.opengis.util.FactoryException)1