1 2 /* 3 * Copyright (c) 2016 Vivid Solutions. 4 * 5 * All rights reserved. This program and the accompanying materials 6 * are made available under the terms of the Eclipse Public License 2.0 7 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. 8 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html 9 * and the Eclipse Distribution License is available at 10 * 11 * http://www.eclipse.org/org/documents/edl-v10.php. 12 */ 13 package org.locationtech.jts.geom; 14 15 import org.locationtech.jts.geom.impl.CoordinateArraySequenceFactory; 16 import org.locationtech.jts.geom.impl.PackedCoordinateSequenceFactory; 17 import org.locationtech.jts.io.WKTReader; 18 19 import junit.framework.TestCase; 20 import junit.textui.TestRunner; 21 22 import java.util.Random; 23 24 25 /** 26 * @version 1.7 27 */ 28 public class CoordinateSequencesTest extends TestCase { 29 30 private PrecisionModel precisionModel = new PrecisionModel(); 31 private GeometryFactory geometryFactory = new GeometryFactory(precisionModel, 0); 32 WKTReader reader = new WKTReader(geometryFactory); 33 34 private static final double[][] ordinateValues = { 35 {75.76,77.43},{41.35,90.75},{73.74,41.67},{20.87,86.49},{17.49,93.59},{67.75,80.63}, 36 {63.01,52.57},{32.9,44.44},{79.36,29.8},{38.17,88.0},{19.31,49.71},{57.03,19.28}, 37 {63.76,77.35},{45.26,85.15},{51.71,50.38},{92.16,19.85},{64.18,27.7},{64.74,65.1}, 38 {80.07,13.55},{55.54,94.07}}; 39 main(String args[])40 public static void main(String args[]) { 41 TestRunner.run(CoordinateSequencesTest.class); 42 } 43 CoordinateSequencesTest(String name)44 public CoordinateSequencesTest(String name) { super(name); } 45 testCopyToLargerDim()46 public void testCopyToLargerDim() 47 { 48 PackedCoordinateSequenceFactory csFactory = new PackedCoordinateSequenceFactory(); 49 CoordinateSequence cs2D = createTestSequence(csFactory, 10, 2); 50 CoordinateSequence cs3D = csFactory.create(10, 3); 51 CoordinateSequences.copy(cs2D, 0, cs3D, 0, cs3D.size()); 52 assertTrue(CoordinateSequences.isEqual(cs2D, cs3D)); 53 } 54 testCopyToSmallerDim()55 public void testCopyToSmallerDim() 56 { 57 PackedCoordinateSequenceFactory csFactory = new PackedCoordinateSequenceFactory(); 58 CoordinateSequence cs3D = createTestSequence(csFactory, 10, 3); 59 CoordinateSequence cs2D = csFactory.create(10, 2); 60 CoordinateSequences.copy(cs3D, 0, cs2D, 0, cs2D.size()); 61 assertTrue(CoordinateSequences.isEqual(cs2D, cs3D)); 62 } 63 64 testScrollRing()65 public void testScrollRing() { 66 System.out.println("Testing scrolling of closed ring"); 67 doTestScrollRing(CoordinateArraySequenceFactory.instance(), 2); 68 doTestScrollRing(CoordinateArraySequenceFactory.instance(), 3); 69 doTestScrollRing(PackedCoordinateSequenceFactory.DOUBLE_FACTORY, 2); 70 doTestScrollRing(PackedCoordinateSequenceFactory.DOUBLE_FACTORY, 4); 71 doTestScrollRing(PackedCoordinateSequenceFactory.FLOAT_FACTORY, 2); 72 doTestScrollRing(PackedCoordinateSequenceFactory.FLOAT_FACTORY, 4); 73 } 74 testScroll()75 public void testScroll() { 76 System.out.println("Testing scrolling of circular string"); 77 doTestScroll(CoordinateArraySequenceFactory.instance(), 2); 78 doTestScroll(CoordinateArraySequenceFactory.instance(), 3); 79 doTestScroll(PackedCoordinateSequenceFactory.DOUBLE_FACTORY, 2); 80 doTestScroll(PackedCoordinateSequenceFactory.DOUBLE_FACTORY, 4); 81 doTestScroll(PackedCoordinateSequenceFactory.FLOAT_FACTORY, 2); 82 doTestScroll(PackedCoordinateSequenceFactory.FLOAT_FACTORY, 4); 83 } 84 testIndexOf()85 public void testIndexOf() { 86 System.out.println("Testing indexOf"); 87 doTestIndexOf(CoordinateArraySequenceFactory.instance(), 2); 88 doTestIndexOf(PackedCoordinateSequenceFactory.DOUBLE_FACTORY, 5); 89 doTestIndexOf(PackedCoordinateSequenceFactory.FLOAT_FACTORY, 7); 90 } 91 testMinCoordinateIndex()92 public void testMinCoordinateIndex() { 93 System.out.println("Testing minCoordinateIndex"); 94 doTestMinCoordinateIndex(CoordinateArraySequenceFactory.instance(), 2); 95 doTestMinCoordinateIndex(PackedCoordinateSequenceFactory.DOUBLE_FACTORY, 5); 96 doTestMinCoordinateIndex(PackedCoordinateSequenceFactory.FLOAT_FACTORY, 7); 97 } 98 testIsRing()99 public void testIsRing() { 100 System.out.println("Testing isRing"); 101 doTestIsRing(CoordinateArraySequenceFactory.instance(), 2); 102 doTestIsRing(PackedCoordinateSequenceFactory.DOUBLE_FACTORY, 5); 103 doTestIsRing(PackedCoordinateSequenceFactory.FLOAT_FACTORY, 7); 104 } 105 testCopy()106 public void testCopy() { 107 System.out.println("Testing copy"); 108 doTestCopy(CoordinateArraySequenceFactory.instance(), 2); 109 doTestCopy(PackedCoordinateSequenceFactory.DOUBLE_FACTORY, 5); 110 doTestCopy(PackedCoordinateSequenceFactory.FLOAT_FACTORY, 7); 111 } 112 testReverse()113 public void testReverse() { 114 System.out.println("Testing reverse"); 115 doTestReverse(CoordinateArraySequenceFactory.instance(), 2); 116 doTestReverse(PackedCoordinateSequenceFactory.DOUBLE_FACTORY, 5); 117 doTestReverse(PackedCoordinateSequenceFactory.FLOAT_FACTORY, 7); 118 } 119 120 /** 121 * Method used to create a {@link this.ordinateValues}. 122 * Usage: remove first 't' and run as unit test. 123 * Note: When parameters are changed, some unit tests may need to be 124 * changed, too. <p> 125 * This is especially true for the (@link testMinCoordinateIndex) test, 126 * which assumes that the coordinates in the sequence are all within an 127 * envelope of [Env(10, 100, 10, 100)]. 128 * </p>. 129 * 130 * @deprecated only use to update {@link this.ordinateValues} 131 */ ttestCreateRandomOrdinates()132 public void ttestCreateRandomOrdinates() { 133 CoordinateSequence sequence = createRandomTestSequence(CoordinateArraySequenceFactory.instance(), 20, 134 2, new Random(7), 135 new Envelope(10, 100, 10, 100), new PrecisionModel(100)); 136 StringBuilder ordinates; 137 ordinates = new StringBuilder("\tprivate static final double[][] ordinateValues = {"); 138 for (int i = 0; i < sequence.size(); i++) { 139 if (i%6 == 0) ordinates.append("\n\t\t"); 140 ordinates.append('{'); 141 ordinates.append(sequence.getOrdinate(i, 0)); 142 ordinates.append(','); 143 ordinates.append(sequence.getOrdinate(i, 1)); 144 if (i < sequence.size()-1) ordinates.append("},"); else ordinates.append('}'); 145 } 146 ordinates.append("};"); 147 148 System.out.println(ordinates.toString()); 149 assertTrue(true); 150 } 151 createSequenceFromOrdinates(CoordinateSequenceFactory csFactory, int dim)152 private static CoordinateSequence createSequenceFromOrdinates(CoordinateSequenceFactory csFactory, int dim) { 153 CoordinateSequence sequence = csFactory.create(ordinateValues.length, dim); 154 for (int i = 0; i < ordinateValues.length; i++) { 155 sequence.setOrdinate(i, 0, ordinateValues[i][0]); 156 sequence.setOrdinate(i, 1, ordinateValues[i][1]); 157 } 158 return fillNonPlanarDimensions(sequence); 159 } 160 createTestSequence(CoordinateSequenceFactory csFactory, int size, int dim)161 private static CoordinateSequence createTestSequence(CoordinateSequenceFactory csFactory, int size, int dim) 162 { 163 CoordinateSequence cs = csFactory.create(size, dim); 164 // initialize with a data signature where coords look like [1, 10, 100, ...] 165 for (int i = 0; i < size; i++) { 166 for (int d = 0; d < dim; d++) { 167 cs.setOrdinate(i, d, i * Math.pow(10, d)); 168 } 169 } 170 return cs; 171 } 172 173 /** 174 * @deprecated only use to update in conjunction with {@link this.ttestCreateRandomOrdinates} 175 */ createRandomTestSequence(CoordinateSequenceFactory csFactory, int size, int dim, Random rnd, Envelope range, PrecisionModel pm)176 private static CoordinateSequence createRandomTestSequence(CoordinateSequenceFactory csFactory, int size, int dim, 177 Random rnd, Envelope range, PrecisionModel pm) 178 { 179 CoordinateSequence cs = csFactory.create(size, dim); 180 for (int i = 0; i < size; i++) { 181 cs.setOrdinate(i, 0, pm.makePrecise(range.getWidth() * rnd.nextDouble() + range.getMinX())); 182 cs.setOrdinate(i, 1, pm.makePrecise(range.getHeight() * rnd.nextDouble() + range.getMinY())); 183 } 184 185 return fillNonPlanarDimensions(cs); 186 } 187 doTestReverse(CoordinateSequenceFactory factory, int dimension)188 private static void doTestReverse(CoordinateSequenceFactory factory, int dimension) { 189 190 // arrange 191 CoordinateSequence sequence = createSequenceFromOrdinates(factory, dimension); 192 CoordinateSequence reversed = sequence.copy(); 193 194 // act 195 CoordinateSequences.reverse(reversed); 196 197 // assert 198 for (int i = 0; i < sequence.size(); i++) 199 checkCoordinateAt(sequence, i, reversed, sequence.size() - i - 1, dimension); 200 } 201 doTestCopy(CoordinateSequenceFactory factory, int dimension)202 private static void doTestCopy(CoordinateSequenceFactory factory, int dimension) { 203 204 // arrange 205 CoordinateSequence sequence = createSequenceFromOrdinates(factory, dimension); 206 if (sequence.size() <= 7) { 207 System.out.println("sequence has a size of " + sequence.size() + ". Execution of this test needs a sequence "+ 208 "with more than 6 coordinates."); 209 return; 210 } 211 212 CoordinateSequence fullCopy = factory.create(sequence.size(), dimension); 213 CoordinateSequence partialCopy = factory.create(sequence.size() - 5, dimension); 214 215 // act 216 CoordinateSequences.copy(sequence, 0, fullCopy, 0, sequence.size()); 217 CoordinateSequences.copy(sequence, 2, partialCopy, 0, partialCopy.size()); 218 219 // assert 220 for (int i = 0; i < fullCopy.size(); i++) 221 checkCoordinateAt(sequence, i, fullCopy, i, dimension); 222 for (int i = 0; i < partialCopy.size(); i++) 223 checkCoordinateAt(sequence, 2 + i, partialCopy, i, dimension); 224 225 // ToDo test if dimensions don't match 226 } 227 doTestIsRing(CoordinateSequenceFactory factory, int dimension)228 private static void doTestIsRing(CoordinateSequenceFactory factory, int dimension) { 229 230 // arrange 231 CoordinateSequence ring = createCircle(factory, dimension, new Coordinate(), 5); 232 CoordinateSequence noRing = createCircularString(factory, dimension, new Coordinate(), 5, 233 0.1, 22); 234 CoordinateSequence empty = createAlmostRing(factory, dimension, 0); 235 CoordinateSequence incomplete1 = createAlmostRing(factory, dimension, 1); 236 CoordinateSequence incomplete2 = createAlmostRing(factory, dimension, 2); 237 CoordinateSequence incomplete3 = createAlmostRing(factory, dimension, 3); 238 CoordinateSequence incomplete4a = createAlmostRing(factory, dimension, 4); 239 CoordinateSequence incomplete4b = CoordinateSequences.ensureValidRing(factory, incomplete4a); 240 241 // act 242 boolean isRingRing = CoordinateSequences.isRing(ring); 243 boolean isRingNoRing = CoordinateSequences.isRing(noRing); 244 boolean isRingEmpty = CoordinateSequences.isRing(empty); 245 boolean isRingIncomplete1 = CoordinateSequences.isRing(incomplete1); 246 boolean isRingIncomplete2 = CoordinateSequences.isRing(incomplete2); 247 boolean isRingIncomplete3 = CoordinateSequences.isRing(incomplete3); 248 boolean isRingIncomplete4a = CoordinateSequences.isRing(incomplete4a); 249 boolean isRingIncomplete4b = CoordinateSequences.isRing(incomplete4b); 250 251 // assert 252 assertTrue(isRingRing); 253 assertTrue(!isRingNoRing); 254 assertTrue(isRingEmpty); 255 assertTrue(!isRingIncomplete1); 256 assertTrue(!isRingIncomplete2); 257 assertTrue(!isRingIncomplete3); 258 assertTrue(!isRingIncomplete4a); 259 assertTrue(isRingIncomplete4b); 260 } 261 doTestIndexOf(CoordinateSequenceFactory factory, int dimension)262 private static void doTestIndexOf(CoordinateSequenceFactory factory, int dimension) { 263 264 // arrange 265 CoordinateSequence sequence = createSequenceFromOrdinates(factory, dimension); 266 267 // act & assert 268 Coordinate[] coordinates = sequence.toCoordinateArray(); 269 for (int i = 0; i < sequence.size(); i++) 270 assertEquals(i, CoordinateSequences.indexOf(coordinates[i], sequence)); 271 272 } 273 doTestMinCoordinateIndex(CoordinateSequenceFactory factory, int dimension)274 private static void doTestMinCoordinateIndex(CoordinateSequenceFactory factory, int dimension) { 275 276 CoordinateSequence sequence = createSequenceFromOrdinates(factory, dimension); 277 if (sequence.size() <= 6) { 278 System.out.println("sequence has a size of " + sequence.size() + ". Execution of this test needs a sequence "+ 279 "with more than 5 coordinates."); 280 return; 281 } 282 283 int minIndex = sequence.size() / 2; 284 sequence.setOrdinate(minIndex, 0, 5); 285 sequence.setOrdinate(minIndex, 1, 5); 286 287 assertEquals(minIndex, CoordinateSequences.minCoordinateIndex(sequence)); 288 assertEquals(minIndex, CoordinateSequences.minCoordinateIndex(sequence, 2, sequence.size()-2)); 289 290 } 291 doTestScroll(CoordinateSequenceFactory factory, int dimension)292 private static void doTestScroll(CoordinateSequenceFactory factory, int dimension) { 293 294 // arrange 295 CoordinateSequence sequence = createCircularString(factory, dimension, new Coordinate(20, 20), 7d, 296 0.1, 22); 297 CoordinateSequence scrolled = sequence.copy(); 298 299 // act 300 CoordinateSequences.scroll(scrolled, 12); 301 302 // assert 303 int io = 12; 304 for (int is = 0; is < scrolled.size() - 1; is++) { 305 checkCoordinateAt(sequence, io, scrolled, is, dimension); 306 io++; 307 io%=scrolled.size(); 308 } 309 } 310 doTestScrollRing(CoordinateSequenceFactory factory, int dimension)311 private static void doTestScrollRing(CoordinateSequenceFactory factory, int dimension) { 312 313 // arrange 314 //System.out.println("Testing '" + factory.getClass().getSimpleName() + "' with dim=" +dimension ); 315 CoordinateSequence sequence = createCircle(factory, dimension, new Coordinate(10, 10), 9d); 316 CoordinateSequence scrolled = sequence.copy(); 317 318 // act 319 CoordinateSequences.scroll(scrolled, 12); 320 321 // assert 322 int io = 12; 323 for (int is = 0; is < scrolled.size() - 1; is++) { 324 checkCoordinateAt(sequence, io, scrolled, is, dimension); 325 io++; 326 io%=scrolled.size()-1; 327 } 328 checkCoordinateAt(scrolled, 0, scrolled, scrolled.size()-1, dimension); 329 } 330 checkCoordinateAt(CoordinateSequence seq1, int pos1, CoordinateSequence seq2, int pos2, int dim)331 private static void checkCoordinateAt(CoordinateSequence seq1, int pos1, 332 CoordinateSequence seq2, int pos2, int dim) { 333 assertEquals("unexpected x-ordinate at pos " + pos2, 334 seq1.getOrdinate(pos1, 0), seq2.getOrdinate(pos2, 0)); 335 assertEquals("unexpected y-ordinate at pos " + pos2, 336 seq1.getOrdinate(pos1, 1), seq2.getOrdinate(pos2, 1)); 337 338 // check additional ordinates 339 for (int j = 2; j < dim; j++) { 340 assertEquals("unexpected "+ j + "-ordinate at pos " + pos2, 341 seq1.getOrdinate(pos1, j), seq2.getOrdinate(pos2, j)); 342 } 343 } 344 createAlmostRing(CoordinateSequenceFactory factory, int dimension, int num)345 private static CoordinateSequence createAlmostRing(CoordinateSequenceFactory factory, int dimension, int num) { 346 347 if (num > 4) num = 4; 348 349 CoordinateSequence sequence = factory.create(num, dimension); 350 if (num == 0) return fillNonPlanarDimensions(sequence); 351 352 sequence.setOrdinate(0, 0, 10); 353 sequence.setOrdinate(0, 0, 10); 354 if (num == 1) return fillNonPlanarDimensions(sequence); 355 356 sequence.setOrdinate(0, 0, 20); 357 sequence.setOrdinate(0, 0, 10); 358 if (num == 2) return fillNonPlanarDimensions(sequence); 359 360 sequence.setOrdinate(0, 0, 20); 361 sequence.setOrdinate(0, 0, 20); 362 if (num == 3) return fillNonPlanarDimensions(sequence); 363 364 sequence.setOrdinate(0, 0, 10.0000000000001); 365 sequence.setOrdinate(0, 0, 9.9999999999999); 366 return fillNonPlanarDimensions(sequence); 367 368 } 369 fillNonPlanarDimensions(CoordinateSequence seq)370 private static CoordinateSequence fillNonPlanarDimensions(CoordinateSequence seq) { 371 372 if (seq.getDimension() < 3) 373 return seq; 374 375 for (int i = 0; i < seq.size(); i++) 376 for (int j = 2; j < seq.getDimension(); j++) 377 seq.setOrdinate(i, j, i* Math.pow(10, j-1)); 378 379 return seq; 380 } 381 createCircle(CoordinateSequenceFactory factory, int dimension, Coordinate center, double radius)382 private static CoordinateSequence createCircle(CoordinateSequenceFactory factory, int dimension, 383 Coordinate center, double radius) { 384 // Get a complete circular string 385 CoordinateSequence res = createCircularString(factory, dimension, center, radius, 0d,49); 386 387 // ensure it is closed 388 for (int i = 0; i < dimension; i++) 389 res.setOrdinate(48, i, res.getOrdinate(0, i)); 390 391 return res; 392 } createCircularString(CoordinateSequenceFactory factory, int dimension, Coordinate center, double radius, double startAngle, int numPoints)393 private static CoordinateSequence createCircularString(CoordinateSequenceFactory factory, int dimension, 394 Coordinate center, double radius, double startAngle, 395 int numPoints) { 396 final int numSegmentsCircle = 48; 397 final double angleCircle = 2 * Math.PI; 398 final double angleStep = angleCircle / numSegmentsCircle; 399 400 CoordinateSequence sequence = factory.create(numPoints, dimension); 401 PrecisionModel pm = new PrecisionModel(100); 402 double angle = startAngle; 403 for (int i = 0; i < numPoints; i++) 404 { 405 double dx = Math.cos(angle) * radius; 406 sequence.setOrdinate(i, 0, pm.makePrecise(center.x +dx)); 407 double dy = Math.sin(angle) * radius; 408 sequence.setOrdinate(i, 1, pm.makePrecise(center.y +dy)); 409 410 // set other ordinate values to predictable values 411 for (int j = 2; j < dimension; j++ ) 412 sequence.setOrdinate(i, j, Math.pow(10, j-1)*i); 413 414 angle += angleStep; 415 angle %= angleCircle; 416 } 417 418 return sequence; 419 } 420 } 421