Class PackedCoordinateSequence

  1 /*
  2  * Copyright (c) 2016 Vivid Solutions.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12 package org.locationtech.jts.geom.impl;
 13  
 14  
 15 import java.io.ObjectStreamException;
 16 import java.io.Serializable;
 17 import java.lang.ref.SoftReference;
 18 import java.util.Arrays;
 19  
 20 import org.locationtech.jts.geom.Coordinate;
 21 import org.locationtech.jts.geom.CoordinateSequence;
 22 import org.locationtech.jts.geom.CoordinateSequences;
 23 import org.locationtech.jts.geom.CoordinateXY;
 24 import org.locationtech.jts.geom.CoordinateXYM;
 25 import org.locationtech.jts.geom.CoordinateXYZM;
 26 import org.locationtech.jts.geom.Envelope;
 27  
 28 /**
 29  * A {@link CoordinateSequence} implementation based on a packed arrays.
 30  * In this implementation, {@link Coordinate}s returned by #toArray and #get are copies
 31  * of the internal values.
 32  * To change the actual values, use the provided setters.
 33  * <p>
 34  * For efficiency, created Coordinate arrays
 35  * are cached using a soft reference.
 36  * The cache is cleared each time the coordinate sequence contents are
 37  * modified through a setter method.
 38  *
 39  * @version 1.7
 40  */
 41 public abstract class PackedCoordinateSequence
 42     implements CoordinateSequence, Serializable
 43 {
 44   private static final long serialVersionUID = -3151899011275603L;
 45   /**
 46    * The dimensions of the coordinates held in the packed array
 47    */
 48   protected int dimension;
 49   
 50   /**
 51    * The number of measures of the coordinates held in the packed array.
 52    */
 53   protected int measures;
 54  
 55   /**
 56    * Creates an instance of this class
 57    * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
 58    * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has.
 59    */
 60   protected PackedCoordinateSequence(int dimension, int measures ) {
 61       if (dimension - measures < 2) {
 62          throw new IllegalArgumentException("Must have at least 2 spatial dimensions");
 63       }
 64       this.dimension = dimension;
 65       this.measures = measures;
 66   }
 67   
 68   /**
 69    * A soft reference to the Coordinate[] representation of this sequence.
 70    * Makes repeated coordinate array accesses more efficient.
 71    */
 72   protected transient SoftReference<Coordinate[]> coordRef;
 73  
 74   /**
 75    * @see CoordinateSequence#getDimension()
 76    */
 77   public int getDimension() {
 78     return this.dimension;
 79   }
 80  
 81   /**
 82    * @see CoordinateSequence#getMeasures()
 83    */
 84   @Override
 85   public int getMeasures() {
 86     return this.measures;
 87   }
 88  
 89   /**
 90    * @see CoordinateSequence#getCoordinate(int)
 91    */
 92   public Coordinate getCoordinate(int i) {
 93     Coordinate[] coords = getCachedCoords();
 94     if(coords != null)
 95       return coords[i];
 96     else
 97       return getCoordinateInternal(i);
 98   }
 99   /**
100    * @see CoordinateSequence#getCoordinate(int)
101    */
102   public Coordinate getCoordinateCopy(int i) {
103     return getCoordinateInternal(i);
104   }
105  
106   /**
107    * @see CoordinateSequence#getCoordinate(int)
108    */
109   public void getCoordinate(int i, Coordinate coord) {
110     coord.x = getOrdinate(i, 0);
111     coord.y = getOrdinate(i, 1);
112     if (hasZ()) {
113       coord.setZ(getZ(i));
114     }
115     if (hasM()) {
116       coord.setM(getM(i));
117     }
118   }
119  
120   /**
121    * @see CoordinateSequence#toCoordinateArray()
122    */
123   public Coordinate[] toCoordinateArray() {
124     Coordinate[] coords = getCachedCoords();
125 // testing - never cache
126     if (coords != null)
127       return coords;
128  
129     coords = new Coordinate[size()];
130     for (int i = 0; i < coords.length; i++) {
131       coords[i] = getCoordinateInternal(i);
132     }
133     coordRef = new SoftReference<Coordinate[]>(coords);
134  
135     return coords;
136   }
137  
138   private Coordinate[] getCachedCoords() {
139     if (coordRef != null) {
140       Coordinate[] coords = (Coordinate[]) coordRef.get();
141       if (coords != null) {
142         return coords;
143       } else {
144         // System.out.print("-");
145         coordRef = null;
146         return null;
147       }
148     } else {
149       // System.out.print("-");
150       return null;
151     }
152  
153   }
154  
155   /**
156    * @see CoordinateSequence#getX(int)
157    */
158   public double getX(int index) {
159     return getOrdinate(index, 0);
160   }
161  
162   /**
163    * @see CoordinateSequence#getY(int)
164    */
165   public double getY(int index) {
166     return getOrdinate(index, 1);
167   }
168  
169   /**
170    * @see CoordinateSequence#getOrdinate(int, int)
171    */
172   public abstract double getOrdinate(int index, int ordinateIndex);
173  
174   /**
175    * Sets the first ordinate of a coordinate in this sequence.
176    *
177    * @param index  the coordinate index
178    * @param value  the new ordinate value
179    */
180   public void setX(int index, double value) {
181     coordRef = null;
182     setOrdinate(index, 0, value);
183   }
184  
185   /**
186    * Sets the second ordinate of a coordinate in this sequence.
187    *
188    * @param index  the coordinate index
189    * @param value  the new ordinate value
190    */
191   public void setY(int index, double value) {
192     coordRef = null;
193     setOrdinate(index, 1, value);
194   }
195  
196   public String toString()
197   {
198     return CoordinateSequences.toString(this);
199   }
200  
201   protected Object readResolve() throws ObjectStreamException {
202     coordRef = null;
203     return this;
204   }
205   
206   /**
207    * Returns a Coordinate representation of the specified coordinate, by always
208    * building a new Coordinate object
209    *
210    * @param index  the coordinate index
211    * @return  the {@link Coordinate} at the given index
212    */
213   protected abstract Coordinate getCoordinateInternal(int index);
214  
215   /**
216    * @see java.lang.Object#clone()
217    * @see CoordinateSequence#clone()
218    * @deprecated
219    */
220   public abstract Object clone();
221   
222   public abstract PackedCoordinateSequence copy();
223  
224   /**
225    * Sets the ordinate of a coordinate in this sequence.
226    * <br>
227    * Warning: for performance reasons the ordinate index is not checked
228    * - if it is over dimensions you may not get an exception but a meaningless value.
229    *
230    * @param index
231    *          the coordinate index
232    * @param ordinate
233    *          the ordinate index in the coordinate, 0 based, smaller than the
234    *          number of dimensions
235    * @param value
236    *          the new ordinate value
237    */
238   public abstract void setOrdinate(int index, int ordinate, double value);
239  
240   /**
241    * Packed coordinate sequence implementation based on doubles
242    */
243   public static class Double extends PackedCoordinateSequence {
244     private static final long serialVersionUID = 5777450686367912719L;
245     /**
246      * The packed coordinate array
247      */
248     double[] coords;
249  
250     /**
251      * Builds a new packed coordinate sequence
252      *
253      * @param coords  an array of <c>double</c> values that contains the ordinate values of the sequence
254      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
255      * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has.
256      */
257     public Double(double[] coords, int dimension, int measures) {
258       super(dimension,measures);
259       if (coords.length % dimension != 0) {
260         throw new IllegalArgumentException("Packed array does not contain "
261             + "an integral number of coordinates");
262       }
263       this.coords = coords;
264     }
265     
266     /**
267      * Builds a new packed coordinate sequence out of a float coordinate array
268      *
269      * @param coords  an array of <c>float</c> values that contains the ordinate values of the sequence
270      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
271      * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has.
272      */
273     public Double(float[] coords, int dimension, int measures) {
274       super(dimension,measures);
275       this.coords = new double[coords.length];
276       for (int i = 0; i < coords.length; i++) {
277         this.coords[i] = coords[i];
278       }
279     }
280     
281     /**
282      * Builds a new packed coordinate sequence out of a coordinate array
283      * 
284      * @param coordinates an array of {@link Coordinate}s
285      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
286      */
287     public Double(Coordinate[] coordinates, int dimension) {
288       this( coordinates, dimension, 0);
289     }
290     /**
291      * Builds a new packed coordinate sequence out of a coordinate array
292      *
293      * @param coordinates an array of {@link Coordinate}s
294      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
295      * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has.
296      */
297     public Double(Coordinate[] coordinates, int dimension, int measures) {
298       super(dimension,measures);
299       if (coordinates == null)
300         coordinates = new Coordinate[0];
301       
302       coords = new double[coordinates.length * this.dimension];
303       for (int i = 0; i < coordinates.length; i++) {
304         int offset = i * dimension;
305         coords[offset] = coordinates[i].x;
306         coords[offset + 1] = coordinates[i].y;
307         if (dimension >= 3)
308           coords[offset + 2] = coordinates[i].getOrdinate(2); // Z or M
309         if (dimension >= 4)
310           coords[offset + 3] = coordinates[i].getOrdinate(3); // M
311       }
312     }
313     /**
314      * Builds a new packed coordinate sequence out of a coordinate array
315      *
316      * @param coordinates an array of {@link Coordinate}s
317      */
318     public Double(Coordinate[] coordinates) {
319       this(coordinates, 30);
320     }
321  
322     /**
323      * Builds a new empty packed coordinate sequence of a given size and dimension
324      *
325      * @param size the number of coordinates in this sequence
326      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
327      * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has.
328      */
329     public Double(int size, int dimension, int measures) {
330       super(dimension,measures);  
331       coords = new double[size * this.dimension];
332     }
333  
334     /**
335      * @see PackedCoordinateSequence#getCoordinate(int)
336      */
337     public Coordinate getCoordinateInternal(int i) {
338       double x = coords[i * dimension];
339       double y = coords[i * dimension + 1];
340       if( dimension == 2 && measures == 0 ) {
341     return new CoordinateXY(x,y);  
342       }
343       else if (dimension == 3 && measures == 0) {
344           double z = coords[i * dimension + 2];
345           return new Coordinate(x,y,z);
346       }
347       else if (dimension == 3 && measures == 1) {
348     double m = coords[i * dimension + 2];     
349           return new CoordinateXYM(x,y,m);          
350       }
351       else if (dimension == 4 && measures == 1) {
352     double z = coords[i * dimension + 2];
353     double m = coords[i * dimension + 3];
354     return new CoordinateXYZM(x,y,z,m);
355       }
356       return new Coordinate(x, y);
357     }
358  
359     /**
360      * Gets the underlying array containing the coordinate values.
361      * 
362      * @return the array of coordinate values
363      */
364     public double[] getRawCoordinates()
365     {
366       return coords;
367     }
368     
369     /**
370      * @see CoordinateSequence#size()
371      */
372     public int size() {
373       return coords.length / dimension;
374     }
375  
376     /**
377      * @see java.lang.Object#clone()
378      * @see PackedCoordinateSequence#clone()
379      * @deprecated
380      */
381     public Object clone() {
382       return copy();
383     }
384  
385     /**
386      * @see PackedCoordinateSequence#size()
387      */
388     public Double copy() {
389       double[] clone = Arrays.copyOf(coords, coords.length);
390       return new Double(clone, dimension, measures);
391     }
392     
393     /**
394      * @see PackedCoordinateSequence#getOrdinate(int, int)
395      *      Beware, for performance reasons the ordinate index is not checked, if
396      *      it's over dimensions you may not get an exception but a meaningless
397      *      value.
398      */
399     public double getOrdinate(int index, int ordinate) {
400       return coords[index * dimension + ordinate];
401     }
402  
403     /**
404      * @see PackedCoordinateSequence#setOrdinate(int, int, double)
405      */
406     public void setOrdinate(int index, int ordinate, double value) {
407       coordRef = null;
408       coords[index * dimension + ordinate] = value;
409     }
410  
411     /**
412      * @see CoordinateSequence#expandEnvelope(Envelope)
413      */
414     public Envelope expandEnvelope(Envelope env)
415     {
416       for (int i = 0; i < coords.length; i += dimension ) {
417         env.expandToInclude(coords[i], coords[i + 1]);
418       }
419       return env;
420     }
421   }
422  
423   /**
424    * Packed coordinate sequence implementation based on floats
425    */
426   public static class Float extends PackedCoordinateSequence {
427     private static final long serialVersionUID = -2902252401427938986L;
428     /**
429      * The packed coordinate array
430      */
431     float[] coords;
432  
433     /**
434      * Constructs a packed coordinate sequence from an array of <code>float</code>s
435      *
436      * @param coords  an array of <c>float</c> values that contains the ordinate values of the sequence
437      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
438      * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has.
439      */
440     public Float(float[] coords, int dimension, int measures) {
441       super(dimension,measures);
442       if (coords.length % dimension != 0) {
443         throw new IllegalArgumentException("Packed array does not contain "
444             + "an integral number of coordinates");
445       }
446       this.coords = coords;
447     }
448  
449     /**
450      * Constructs a packed coordinate sequence from an array of <code>double</code>s
451      *
452      * @param coords  an array of <c>double</c> values that contains the ordinate values of the sequence
453      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
454      * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has.
455      */
456     public Float(double[] coords, int dimension, int measures) {
457       super(dimension,measures);
458       this.coords = new float[coords.length];
459       
460       for (int i = 0; i < coords.length; i++) {
461         this.coords[i] = (float) coords[i];
462       }
463     }
464  
465     /**
466      * Builds a new packed coordinate sequence out of a coordinate array
467      *
468      * @param coordinates an array of {@link Coordinate}s
469      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
470      */
471     public Float(Coordinate[] coordinates, int dimension) {
472       this( coordinates, dimension, 0);
473     }
474     
475     /**
476      * Constructs a packed coordinate sequence out of a coordinate array
477      *
478      * @param coordinates an array of {@link Coordinate}s
479      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
480      * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has.
481      */
482     public Float(Coordinate[] coordinates, int dimension, int measures) {
483       super(dimension,measures);
484       if (coordinates == null)
485         coordinates = new Coordinate[0];
486       
487       coords = new float[coordinates.length * dimension];
488       for (int i = 0; i < coordinates.length; i++) {
489         int offset = i * dimension;
490         coords[offset] = (float) coordinates[i].x;
491         coords[offset + 1] = (float) coordinates[i].y;
492         if (dimension >= 3)
493           coords[offset + 2] = (float) coordinates[i].getOrdinate(2); // Z or M
494         if (dimension >= 4)
495           coords[offset + 3] = (float) coordinates[i].getOrdinate(3); // M
496       }
497     }
498  
499     /**
500      * Constructs an empty packed coordinate sequence of a given size and dimension
501      *
502      * @param size the number of coordinates in this sequence
503      * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence.
504      * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has.
505      */
506     public Float(int size, int dimension,int measures) {
507       super(dimension,measures);
508       coords = new float[size * this.dimension];
509     }
510  
511     /**
512      * @see PackedCoordinateSequence#getCoordinate(int)
513      */
514     public Coordinate getCoordinateInternal(int i) {
515       double x = coords[i * dimension];
516       double y = coords[i * dimension + 1];
517       if (dimension == 2 && measures == 0) {
518         return new CoordinateXY(x, y);
519       } else if (dimension == 3 && measures == 0) {
520         double z = coords[i * dimension + 2];
521         return new Coordinate(x, y, z);
522       } else if (dimension == 3 && measures == 1) {
523         double m = coords[i * dimension + 2];
524         return new CoordinateXYM(x, y, m);
525       } else if (dimension == 4 && measures == 1) {
526         double z = coords[i * dimension + 2];
527         double m = coords[i * dimension + 3];
528         return new CoordinateXYZM(x, y, z, m);
529       }
530       return new Coordinate(x, y);
531     }
532  
533     /**
534      * Gets the underlying array containing the coordinate values.
535      * 
536      * @return the array of coordinate values
537      */
538     public float[] getRawCoordinates()
539     {
540       return coords;
541     }
542     
543     /**
544      * @see CoordinateSequence#size()
545      */
546     public int size() {
547       return coords.length / dimension;
548     }
549  
550     /**
551      * @see java.lang.Object#clone()
552      * @see PackedCoordinateSequence#clone()
553      * @deprecated
554      */
555     public Object clone() {
556       return copy();
557     }
558  
559     /**
560      * @see PackedCoordinateSequence#copy()
561      */
562     public Float copy() {
563       float[] clone = Arrays.copyOf(coords, coords.length);
564       return new Float(clone, dimension,measures);
565     }
566  
567     /**
568      * @see PackedCoordinateSequence#getOrdinate(int, int)
569      *      For performance reasons the ordinate index is not checked.
570      *      If it is larger than the dimension a meaningless
571      *      value may be returned.
572      */
573     public double getOrdinate(int index, int ordinate) {
574       return coords[index * dimension + ordinate];
575     }
576  
577     /**
578      * @see PackedCoordinateSequence#setOrdinate(int, int, double)
579      */
580     public void setOrdinate(int index, int ordinate, double value) {
581       coordRef = null;
582       coords[index * dimension + ordinate] = (float) value;
583     }
584  
585     /**
586      * @see CoordinateSequence#expandEnvelope(Envelope)
587      */
588     public Envelope expandEnvelope(Envelope env)
589     {
590       for (int i = 0; i < coords.length; i += dimension ) {
591         env.expandToInclude(coords[i], coords[i + 1]);
592       }
593       return env;
594     }
595   }
596  
597 }
598