/* * Copyright (c) 2016 Vivid Solutions. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * * http://www.eclipse.org/org/documents/edl-v10.php. */ package org.locationtech.jts.geom; import org.locationtech.jts.util.StringUtil; /** * Utility functions for manipulating {@link CoordinateSequence}s * * @version 1.7 */ public class CoordinateSequences { /** * Reverses the coordinates in a sequence in-place. */ public static void reverse(CoordinateSequence seq) { int last = seq.size() - 1; int mid = last / 2; for (int i = 0; i <= mid; i++) { swap(seq, i, last - i); } } /** * Swaps two coordinates in a sequence. * * @param seq the sequence to modify * @param i the index of a coordinate to swap * @param j the index of a coordinate to swap */ public static void swap(CoordinateSequence seq, int i, int j) { if (i == j) return; for (int dim = 0; dim < seq.getDimension(); dim++) { double tmp = seq.getOrdinate(i, dim); seq.setOrdinate(i, dim, seq.getOrdinate(j, dim)); seq.setOrdinate(j, dim, tmp); } } /** * Copies a section of a {@link CoordinateSequence} to another {@link CoordinateSequence}. * The sequences may have different dimensions; * in this case only the common dimensions are copied. * * @param src the sequence to copy from * @param srcPos the position in the source sequence to start copying at * @param dest the sequence to copy to * @param destPos the position in the destination sequence to copy to * @param length the number of coordinates to copy */ public static void copy(CoordinateSequence src, int srcPos, CoordinateSequence dest, int destPos, int length) { for (int i = 0; i < length; i++) { copyCoord(src, srcPos + i, dest, destPos + i); } } /** * Copies a coordinate of a {@link CoordinateSequence} to another {@link CoordinateSequence}. * The sequences may have different dimensions; * in this case only the common dimensions are copied. * * @param src the sequence to copy from * @param srcPos the source coordinate to copy * @param dest the sequence to copy to * @param destPos the destination coordinate to copy to */ public static void copyCoord(CoordinateSequence src, int srcPos, CoordinateSequence dest, int destPos) { int minDim = Math.min(src.getDimension(), dest.getDimension()); for (int dim = 0; dim < minDim; dim++) { dest.setOrdinate(destPos, dim, src.getOrdinate(srcPos, dim)); } } /** * Tests whether a {@link CoordinateSequence} forms a valid {@link LinearRing}, * by checking the sequence length and closure * (whether the first and last points are identical in 2D). * Self-intersection is not checked. * * @param seq the sequence to test * @return true if the sequence is a ring * @see LinearRing */ public static boolean isRing(CoordinateSequence seq) { int n = seq.size(); if (n == 0) return true; // too few points if (n <= 3) return false; // test if closed return seq.getOrdinate(0, CoordinateSequence.X) == seq.getOrdinate(n-1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) == seq.getOrdinate(n-1, CoordinateSequence.Y); } /** * Ensures that a CoordinateSequence forms a valid ring, * returning a new closed sequence of the correct length if required. * If the input sequence is already a valid ring, it is returned * without modification. * If the input sequence is too short or is not closed, * it is extended with one or more copies of the start point. * * @param fact the CoordinateSequenceFactory to use to create the new sequence * @param seq the sequence to test * @return the original sequence, if it was a valid ring, or a new sequence which is valid. */ public static CoordinateSequence ensureValidRing(CoordinateSequenceFactory fact, CoordinateSequence seq) { int n = seq.size(); // empty sequence is valid if (n == 0) return seq; // too short - make a new one if (n <= 3) return createClosedRing(fact, seq, 4); boolean isClosed = seq.getOrdinate(0, CoordinateSequence.X) == seq.getOrdinate(n-1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) == seq.getOrdinate(n-1, CoordinateSequence.Y); if (isClosed) return seq; // make a new closed ring return createClosedRing(fact, seq, n+1); } private static CoordinateSequence createClosedRing(CoordinateSequenceFactory fact, CoordinateSequence seq, int size) { CoordinateSequence newseq = fact.create(size, seq.getDimension()); int n = seq.size(); copy(seq, 0, newseq, 0, n); // fill remaining coordinates with start point for (int i = n; i < size; i++) copy(seq, 0, newseq, i, 1); return newseq; } public static CoordinateSequence extend(CoordinateSequenceFactory fact, CoordinateSequence seq, int size) { CoordinateSequence newseq = fact.create(size, seq.getDimension()); int n = seq.size(); copy(seq, 0, newseq, 0, n); // fill remaining coordinates with end point, if it exists if (n > 0) { for (int i = n; i < size; i++) copy(seq, n-1, newseq, i, 1); } return newseq; } /** * Tests whether two {@link CoordinateSequence}s are equal. * To be equal, the sequences must be the same length. * They do not need to be of the same dimension, * but the ordinate values for the smallest dimension of the two * must be equal. * Two <code>NaN</code> ordinates values are considered to be equal. * * @param cs1 a CoordinateSequence * @param cs2 a CoordinateSequence * @return true if the sequences are equal in the common dimensions */ public static boolean isEqual(CoordinateSequence cs1, CoordinateSequence cs2) { int cs1Size = cs1.size(); int cs2Size = cs2.size(); if (cs1Size != cs2Size) return false; int dim = Math.min(cs1.getDimension(), cs2.getDimension()); for (int i = 0; i < cs1Size; i++) { for (int d = 0; d < dim; d++) { double v1 = cs1.getOrdinate(i, d); double v2 = cs2.getOrdinate(i, d); if (cs1.getOrdinate(i, d) == cs2.getOrdinate(i, d)) continue; // special check for NaNs if (Double.isNaN(v1) && Double.isNaN(v2)) continue; return false; } } return true; } /** * Creates a string representation of a {@link CoordinateSequence}. * The format is: * <pre> * ( ord0,ord1.. ord0,ord1,... ... ) * </pre> * * @param cs the sequence to output * @return the string representation of the sequence */ public static String toString(CoordinateSequence cs) { int size = cs.size(); if (size == 0) return "()"; int dim = cs.getDimension(); StringBuilder builder = new StringBuilder(); builder.append('('); for (int i = 0; i < size; i++) { if (i > 0) builder.append(" "); for (int d = 0; d < dim; d++) { if (d > 0) builder.append(","); builder.append(StringUtil.toString(cs.getOrdinate(i, d))); } } builder.append(')'); return builder.toString(); } }