]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/data/Track.java
Version 1, September 2006
[GpsPrune.git] / tim / prune / data / Track.java
1 package tim.prune.data;
2
3 import tim.prune.UpdateMessageBroker;
4
5
6 /**
7  * Class to hold all track information,
8  * including track points and waypoints
9  */
10 public class Track
11 {
12         // Broker object
13         UpdateMessageBroker _broker = null;
14         // Data points
15         private DataPoint[] _dataPoints = null;
16         // Scaled x, y values
17         private double[] _xValues = null;
18         private double[] _yValues = null;
19         private boolean _scaled = false;
20         private int _numPoints = 0;
21         private boolean _mixedData = false;
22         // Master field list
23         private FieldList _masterFieldList = null;
24         // variable ranges
25         private AltitudeRange _altitudeRange = null;
26         private DoubleRange _latRange = null, _longRange = null;
27         private DoubleRange _xRange = null, _yRange = null;
28
29
30         /**
31          * Constructor giving arrays of Fields and Objects
32          * @param inFieldArray field array
33          * @param inPointArray 2d array of field values
34          */
35         public Track(UpdateMessageBroker inBroker)
36         {
37                 _broker = inBroker;
38                 // create field list
39                 _masterFieldList = new FieldList(null);
40                 // make empty DataPoint array
41                 _dataPoints = new DataPoint[0];
42                 _numPoints = 0;
43                 // needs to be scaled
44                 _scaled = false;
45         }
46
47
48         /**
49          * Load method, for initialising and reinitialising data
50          * @param inFieldArray array of Field objects describing fields
51          * @param inPointArray 2d object array containing data
52          * @param inAltFormat altitude format
53          */
54         public void load(Field[] inFieldArray, Object[][] inPointArray, int inAltFormat)
55         {
56                 // copy field list
57                 _masterFieldList = new FieldList(inFieldArray);
58                 // make DataPoint object from each point in inPointList
59                 _dataPoints = new DataPoint[inPointArray.length];
60                 String[] dataArray = null;
61                 int pointIndex = 0;
62                 for (int p=0; p < inPointArray.length; p++)
63                 {
64                         dataArray = (String[]) inPointArray[p];
65                         // Convert to DataPoint objects
66                         DataPoint point = new DataPoint(dataArray, _masterFieldList, inAltFormat);
67                         if (point.isValid())
68                         {
69                                 _dataPoints[pointIndex] = point;
70                                 pointIndex++;
71                         }
72                 }
73                 _numPoints = pointIndex;
74                 // needs to be scaled
75                 _scaled = false;
76         }
77
78
79         ////////////////// Modification methods //////////////////////
80
81
82         /**
83          * Combine this Track with new data
84          * @param inOtherTrack
85          */
86         public void combine(Track inOtherTrack)
87         {
88                 // merge field list
89                 _masterFieldList = _masterFieldList.merge(inOtherTrack._masterFieldList);
90                 // expand data array and add other track's data points
91                 int totalPoints = getNumPoints() + inOtherTrack.getNumPoints();
92                 DataPoint[] mergedPoints = new DataPoint[totalPoints];
93                 System.arraycopy(_dataPoints, 0, mergedPoints, 0, getNumPoints());
94                 System.arraycopy(inOtherTrack._dataPoints, 0, mergedPoints, getNumPoints(), inOtherTrack.getNumPoints());
95                 _dataPoints = mergedPoints;
96                 // combine point count
97                 _numPoints = totalPoints;
98                 // needs to be scaled again
99                 _scaled = false;
100                 // inform listeners
101                 _broker.informSubscribers();
102         }
103
104
105         /**
106          * Crop the track to the given size - subsequent points are not (yet) deleted
107          * @param inNewSize new number of points in track
108          */
109         public void cropTo(int inNewSize)
110         {
111                 if (inNewSize >= 0 && inNewSize < getNumPoints())
112                 {
113                         _numPoints = inNewSize;
114                         // needs to be scaled again
115                         _scaled = false;
116                         _broker.informSubscribers();
117                 }
118         }
119
120
121         /**
122          * Compress the track to the given resolution
123          * @param inResolution resolution
124          * @return number of points deleted
125          */
126         public int compress(int inResolution)
127         {
128                 // (maybe should be separate thread?)
129                 // (maybe should be in separate class?)
130                 // (maybe should be based on subtended angles instead of distances?)
131
132                 if (inResolution <= 0) return 0;
133                 int numCopied = 0;
134                 // Establish range of track and minimum range between points
135                 scalePoints();
136                 double wholeScale = _xRange.getMaximum() - _xRange.getMinimum();
137                 double yscale = _yRange.getMaximum() - _yRange.getMinimum();
138                 if (yscale > wholeScale) wholeScale = yscale;
139                 double minDist = wholeScale / inResolution;
140
141                 // Copy selected points
142                 DataPoint[] newPointArray = new DataPoint[_numPoints];
143                 int[] pointIndices = new int[_numPoints];
144                 for (int i=0; i<_numPoints; i++)
145                 {
146                         boolean keepPoint = true;
147                         if (!_dataPoints[i].isWaypoint())
148                         {
149                                 // go through newPointArray to check for range
150                                 for (int j=0; j<numCopied && keepPoint; j++)
151                                 {
152                                         // calculate distance between point j and current point
153                                         double pointDist = Math.abs(_xValues[i] - _xValues[pointIndices[j]])
154                                          + Math.abs(_yValues[i] - _yValues[pointIndices[j]]);
155                                         if (pointDist < minDist)
156                                                 keepPoint = false;
157                                 }
158                         }
159                         if (keepPoint)
160                         {
161                                 newPointArray[numCopied] = _dataPoints[i];
162                                 pointIndices[numCopied] = i;
163                                 numCopied++;
164                         }
165                 }
166
167                 // Copy array references
168                 int numDeleted = _numPoints - numCopied;
169                 if (numDeleted > 0)
170                 {
171                         _dataPoints = new DataPoint[numCopied];
172                         System.arraycopy(newPointArray, 0, _dataPoints, 0, numCopied);
173                         _numPoints = _dataPoints.length;
174                         _scaled = false;
175                         _broker.informSubscribers();
176                 }
177                 return numDeleted;
178         }
179
180
181         /**
182          * Halve the track by deleting alternate points
183          * @return number of points deleted
184          */
185         public int halve()
186         {
187                 if (_numPoints < 100) return 0;
188                 int newSize = _numPoints / 2;
189                 int numDeleted = _numPoints - newSize;
190                 DataPoint[] newPointArray = new DataPoint[newSize];
191                 // Delete alternate points
192                 for (int i=0; i<newSize; i++)
193                         newPointArray[i] = _dataPoints[i*2];
194                 // Copy array references
195                 _dataPoints = newPointArray;
196                 _numPoints = _dataPoints.length;
197                 _scaled = false;
198                 _broker.informSubscribers();
199                 return numDeleted;
200         }
201
202
203         /**
204          * Delete the specified point
205          * @return true if successful
206          */
207         public boolean deletePoint(int inIndex)
208         {
209                 boolean answer = deleteRange(inIndex, inIndex);
210                 return answer;
211         }
212
213
214         /**
215          * Delete the specified range of points from the Track
216          * @param inStart start of range (inclusive)
217          * @param inEnd end of range (inclusive)
218          * @return true if successful
219          */
220         public boolean deleteRange(int inStart, int inEnd)
221         {
222                 if (inStart < 0 || inEnd < 0 || inEnd < inStart)
223                 {
224                         // no valid range selected so can't delete
225                         return false;
226                 }
227                 // valid range, let's delete it
228                 int numToDelete = inEnd - inStart + 1;
229                 DataPoint[] newPointArray = new DataPoint[_numPoints - numToDelete];
230                 // Copy points before the selected range
231                 if (inStart > 0)
232                 {
233                         System.arraycopy(_dataPoints, 0, newPointArray, 0, inStart);
234                 }
235                 // Copy points after the deleted one(s)
236                 if (inEnd < (_numPoints - 1))
237                 {
238                         System.arraycopy(_dataPoints, inEnd + 1, newPointArray, inStart,
239                                 _numPoints - inEnd - 1);
240                 }
241                 // Copy points over original array (careful!)
242                 _dataPoints = newPointArray;
243                 _numPoints -= numToDelete;
244                 // needs to be scaled again
245                 _scaled = false;
246                 return true;
247         }
248
249
250         /**
251          * Delete all the duplicate points in the track
252          * @return number of points deleted
253          */
254         public int deleteDuplicates()
255         {
256                 // loop through Track counting duplicates first
257                 boolean[] dupes = new boolean[_numPoints];
258                 int numDupes = 0;
259                 int i, j;
260                 for (i=1; i<_numPoints; i++)
261                 {
262                         DataPoint p1 = _dataPoints[i];
263                         // Loop through all points before this one
264                         for (j=0; j<i && !dupes[i]; j++)
265                         {
266                                 DataPoint p2 = _dataPoints[j];
267                                 if (p1.isDuplicate(p2))
268                                 {
269                                         dupes[i] = true;
270                                         numDupes++;
271                                 }
272                         }
273                 }
274                 if (numDupes > 0)
275                 {
276                         // Make new resized array and copy DataPoints over
277                         DataPoint[] newPointArray = new DataPoint[_numPoints - numDupes];
278                         j = 0;
279                         for (i=0; i<_numPoints; i++)
280                         {
281                                 if (!dupes[i])
282                                 {
283                                         newPointArray[j] = _dataPoints[i];
284                                         j++;
285                                 }
286                         }
287                         // Copy array references
288                         _dataPoints = newPointArray;
289                         _numPoints = _dataPoints.length;
290                         _scaled = false;
291                         _broker.informSubscribers();
292                 }
293                 return numDupes;
294         }
295
296
297         /**
298          * Reverse the specified range of points
299          * @return true if successful, false otherwise
300          */
301         public boolean reverseRange(int inStart, int inEnd)
302         {
303                 if (inStart < 0 || inEnd < 0 || inStart >= inEnd || inEnd >= _numPoints)
304                 {
305                         return false;
306                 }
307                 // calculate how many point swaps are required
308                 int numPointsToReverse = (inEnd - inStart + 1) / 2;
309                 DataPoint p = null;
310                 for (int i=0; i<numPointsToReverse; i++)
311                 {
312                         // swap pairs of points
313                         p = _dataPoints[inStart + i];
314                         _dataPoints[inStart + i] = _dataPoints[inEnd - i];
315                         _dataPoints[inEnd - i] = p;
316                 }
317                 // needs to be scaled again
318                 _scaled = false;
319                 _broker.informSubscribers();
320                 return true;
321         }
322
323
324         /**
325          * Collect all waypoints to the start or end of the track
326          * @param inAtStart true to collect at start, false for end
327          * @return true if successful, false if no change
328          */
329         public boolean collectWaypoints(boolean inAtStart)
330         {
331                 // Check for mixed data, numbers of waypoints & nons
332                 int numWaypoints = 0, numNonWaypoints = 0;
333                 boolean wayAfterNon = false, nonAfterWay = false;
334                 DataPoint[] waypoints = new DataPoint[_numPoints];
335                 DataPoint[] nonWaypoints = new DataPoint[_numPoints];
336                 DataPoint point = null;
337                 for (int i=0; i<_numPoints; i++)
338                 {
339                         point = _dataPoints[i];
340                         if (point.isWaypoint())
341                         {
342                                 waypoints[numWaypoints] = point;
343                                 numWaypoints++;
344                                 wayAfterNon |= (numNonWaypoints > 0);
345                         }
346                         else
347                         {
348                                 nonWaypoints[numNonWaypoints] = point;
349                                 numNonWaypoints++;
350                                 nonAfterWay |= (numWaypoints > 0);
351                         }
352                 }
353                 // Exit if the data is already in the specified order
354                 if (numWaypoints == 0 || numNonWaypoints == 0
355                         || (inAtStart && !wayAfterNon && nonAfterWay)
356                         || (!inAtStart && wayAfterNon && !nonAfterWay))
357                 {
358                         return false;
359                 }
360
361                 // Copy the arrays back into _dataPoints in the specified order
362                 if (inAtStart)
363                 {
364                         System.arraycopy(waypoints, 0, _dataPoints, 0, numWaypoints);
365                         System.arraycopy(nonWaypoints, 0, _dataPoints, numWaypoints, numNonWaypoints);
366                 }
367                 else
368                 {
369                         System.arraycopy(nonWaypoints, 0, _dataPoints, 0, numNonWaypoints);
370                         System.arraycopy(waypoints, 0, _dataPoints, numNonWaypoints, numWaypoints);
371                 }
372                 // needs to be scaled again
373                 _scaled = false;
374                 _broker.informSubscribers();
375                 return true;
376         }
377
378
379         /**
380          * Interleave all waypoints by each nearest track point
381          * @return true if successful, false if no change
382          */
383         public boolean interleaveWaypoints()
384         {
385                 // Separate waypoints and find nearest track point
386                 int numWaypoints = 0;
387                 DataPoint[] waypoints = new DataPoint[_numPoints];
388                 int[] pointIndices = new int[_numPoints];
389                 DataPoint point = null;
390                 int i = 0;
391                 for (i=0; i<_numPoints; i++)
392                 {
393                         point = _dataPoints[i];
394                         if (point.isWaypoint())
395                         {
396                                 waypoints[numWaypoints] = point;
397                                 pointIndices[numWaypoints] = getNearestPointIndex(
398                                         _xValues[i], _yValues[i], -1.0, true);
399                                 numWaypoints++;
400                         }
401                 }
402                 // Exit if data not mixed
403                 if (numWaypoints == 0 || numWaypoints == _numPoints)
404                         return false;
405
406                 // Loop round points copying to correct order
407                 DataPoint[] dataCopy = new DataPoint[_numPoints];
408                 int copyIndex = 0;
409                 for (i=0; i<_numPoints; i++)
410                 {
411                         point = _dataPoints[i];
412                         // if it's a track point, copy it
413                         if (!point.isWaypoint())
414                         {
415                                 dataCopy[copyIndex] = point;
416                                 copyIndex++;
417                         }
418                         // check for waypoints with this index
419                         for (int j=0; j<numWaypoints; j++)
420                         {
421                                 if (pointIndices[j] == i)
422                                 {
423                                         dataCopy[copyIndex] = waypoints[j];
424                                         copyIndex++;
425                                 }
426                         }
427                 }
428                 // Copy data back to track
429                 _dataPoints = dataCopy;
430                 // needs to be scaled again to recalc x, y
431                 _scaled = false;
432                 _broker.informSubscribers();
433                 return true;
434         }
435
436
437         /**
438          * Interpolate extra points between two selected ones
439          * @param inStartIndex start index of interpolation
440          * @param inNumPoints num points to insert
441          * @return true if successful
442          */
443         public boolean interpolate(int inStartIndex, int inNumPoints)
444         {
445                 // check parameters
446                 if (inStartIndex < 0 || inStartIndex >= _numPoints || inNumPoints <= 0)
447                         return false;
448
449                 // get start and end points
450                 DataPoint startPoint = getPoint(inStartIndex);
451                 DataPoint endPoint = getPoint(inStartIndex + 1);
452
453                 // Make array of points to insert
454                 DataPoint[] insertedPoints = startPoint.interpolate(endPoint, inNumPoints);
455                 
456                 // Insert points into track
457                 insertRange(insertedPoints, inStartIndex + 1);
458                 return true;
459         }
460
461
462         //////// information methods /////////////
463
464
465         /**
466          * Get the point at the given index
467          * @param inPointNum index number, starting at 0
468          * @return DataPoint object, or null if out of range
469          */
470         public DataPoint getPoint(int inPointNum)
471         {
472                 if (inPointNum > -1 && inPointNum < getNumPoints())
473                 {
474                         return _dataPoints[inPointNum];
475                 }
476                 return null;
477         }
478
479
480         /**
481          * @return altitude range of points as AltitudeRange object
482          */
483         public AltitudeRange getAltitudeRange()
484         {
485                 if (!_scaled) scalePoints();
486                 return _altitudeRange;
487         }
488         /**
489          * @return the number of (valid) points in the track
490          */
491         public int getNumPoints()
492         {
493                 return _numPoints;
494         }
495
496         /**
497          * @return The range of x values as a DoubleRange object
498          */
499         public DoubleRange getXRange()
500         {
501                 if (!_scaled) scalePoints();
502                 return _xRange;
503         }
504
505         /**
506          * @return The range of y values as a DoubleRange object
507          */
508         public DoubleRange getYRange()
509         {
510                 if (!_scaled) scalePoints();
511                 return _yRange;
512         }
513
514         /**
515          * @param inPointNum point index, starting at 0
516          * @return scaled x value of specified point
517          */
518         public double getX(int inPointNum)
519         {
520                 if (!_scaled) scalePoints();
521                 return _xValues[inPointNum];
522         }
523
524         /**
525          * @param inPointNum point index, starting at 0
526          * @return scaled y value of specified point
527          */
528         public double getY(int inPointNum)
529         {
530                 if (!_scaled) scalePoints();
531                 return _yValues[inPointNum];
532         }
533
534         /**
535          * @return the master field list
536          */
537         public FieldList getFieldList()
538         {
539                 return _masterFieldList;
540         }
541
542
543         /**
544          * Checks if any data exists for the specified field
545          * @param inField Field to examine
546          * @return true if data exists for this field
547          */
548         public boolean hasData(Field inField)
549         {
550                 return hasData(inField, 0, _numPoints-1);
551         }
552
553
554         /**
555          * Checks if any data exists for the specified field in the specified range
556          * @param inField Field to examine
557          * @param inStart start of range to check
558          * @param inEnd end of range to check (inclusive)
559          * @return true if data exists for this field
560          */
561         public boolean hasData(Field inField, int inStart, int inEnd)
562         {
563                 for (int i=inStart; i<=inEnd; i++)
564                 {
565                         if (_dataPoints[i].getFieldValue(inField) != null)
566                         {
567                                 return true;
568                         }
569                 }
570                 return false;
571         }
572
573
574         /**
575          * @return true if track contains waypoints and trackpoints
576          */
577         public boolean hasMixedData()
578         {
579                 if (!_scaled) scalePoints();
580                 return _mixedData;
581         }
582
583
584         ///////// Internal processing methods ////////////////
585
586
587         /**
588          * Scale all the points in the track to gain x and y values
589          * ready for plotting
590          */
591         private void scalePoints()
592         {
593                 // Loop through all points in track, to see limits of lat, long and altitude
594                 _longRange = new DoubleRange();
595                 _latRange = new DoubleRange();
596                 _altitudeRange = new AltitudeRange();
597                 int p;
598                 boolean hasWaypoint = false, hasTrackpoint = false;
599                 for (p=0; p < getNumPoints(); p++)
600                 {
601                         DataPoint point = getPoint(p);
602                         if (point != null && point.isValid())
603                         {
604                                 _longRange.addValue(point.getLongitude().getDouble());
605                                 _latRange.addValue(point.getLatitude().getDouble());
606                                 if (point.getAltitude().isValid())
607                                         _altitudeRange.addValue(point.getAltitude());
608                                 if (point.isWaypoint())
609                                         hasWaypoint = true;
610                                 else
611                                         hasTrackpoint = true;
612                         }
613                 }
614                 _mixedData = hasWaypoint && hasTrackpoint;
615
616                 // Use medians to centre at 0
617                 double longMedian = (_longRange.getMaximum() + _longRange.getMinimum()) / 2.0;
618                 double latMedian = (_latRange.getMaximum() + _latRange.getMinimum()) / 2.0;
619                 double longFactor = Math.cos(latMedian / 180.0 * Math.PI); // Function of median latitude
620
621                 // Loop over points and calculate scales
622                 _xValues = new double[getNumPoints()];
623                 _yValues = new double[getNumPoints()];
624                 _xRange = new DoubleRange();
625                 _yRange = new DoubleRange();
626                 for (p=0; p < getNumPoints(); p++)
627                 {
628                         DataPoint point = getPoint(p);
629                         if (point != null)
630                         {
631                                 _xValues[p] = (point.getLongitude().getDouble() - longMedian) * longFactor;
632                                 _xRange.addValue(_xValues[p]);
633                                 _yValues[p] = (point.getLatitude().getDouble() - latMedian);
634                                 _yRange.addValue(_yValues[p]);
635                         }
636                 }
637                 _scaled = true;
638         }
639
640
641         /**
642          * Find the nearest point to the specified x and y coordinates
643          * or -1 if no point is within the specified max distance
644          * @param inX x coordinate
645          * @param inY y coordinate
646          * @param inMaxDist maximum distance from selected coordinates
647          * @param inJustTrackPoints true if waypoints should be ignored
648          * @return index of nearest point or -1 if not found
649          */
650         public int getNearestPointIndex(double inX, double inY, double inMaxDist, boolean inJustTrackPoints)
651         {
652                 int nearestPoint = 0;
653                 double nearestDist = -1.0;
654                 double currDist;
655                 for (int i=0; i < getNumPoints(); i++)
656                 {
657                         if (!inJustTrackPoints || !_dataPoints[i].isWaypoint())
658                         {
659                                 currDist = Math.abs(_xValues[i] - inX) + Math.abs(_yValues[i] - inY);
660                                 if (currDist < nearestDist || nearestDist < 0.0)
661                                 {
662                                         nearestPoint = i;
663                                         nearestDist = currDist;
664                                 }
665                         }
666                 }
667                 // Check whether it's within required distance
668                 if (nearestDist > inMaxDist && inMaxDist > 0.0)
669                 {
670                         return -1;
671                 }
672                 return nearestPoint;
673         }
674
675
676         ////////////////// Cloning and replacing ///////////////////
677
678         /**
679          * Clone the array of DataPoints
680          * @return shallow copy of DataPoint objects
681          */
682         public DataPoint[] cloneContents()
683         {
684                 DataPoint[] clone = new DataPoint[getNumPoints()];
685                 System.arraycopy(_dataPoints, 0, clone, 0, getNumPoints());
686                 return clone;
687         }
688
689
690         /**
691          * Clone the specified range of data points
692          * @param inStart start index (inclusive)
693          * @param inEnd end index (inclusive)
694          * @return shallow copy of DataPoint objects
695          */
696         public DataPoint[] cloneRange(int inStart, int inEnd)
697         {
698                 int numSelected = 0;
699                 if (inEnd >= 0 && inEnd >= inStart)
700                 {
701                         numSelected = inEnd - inStart + 1;
702                 }
703                 DataPoint[] result = new DataPoint[numSelected>0?numSelected:0];
704                 if (numSelected > 0)
705                 {
706                         System.arraycopy(_dataPoints, inStart, result, 0, numSelected);
707                 }
708                 return result;
709         }
710
711
712         /**
713          * Re-insert the specified point at the given index
714          * @param inPoint point to insert
715          * @param inIndex index at which to insert the point
716          * @return true if it worked, false otherwise
717          */
718         public boolean insertPoint(DataPoint inPoint, int inIndex)
719         {
720                 if (inIndex > _numPoints || inPoint == null)
721                 {
722                         return false;
723                 }
724                 // Make new array to copy points over to
725                 DataPoint[] newPointArray = new DataPoint[_numPoints + 1];
726                 if (inIndex > 0)
727                 {
728                         System.arraycopy(_dataPoints, 0, newPointArray, 0, inIndex);
729                 }
730                 newPointArray[inIndex] = inPoint;
731                 if (inIndex < _numPoints)
732                 {
733                         System.arraycopy(_dataPoints, inIndex, newPointArray, inIndex+1, _numPoints - inIndex);
734                 }
735                 // Change over to new array
736                 _dataPoints = newPointArray;
737                 _numPoints++;
738                 // needs to be scaled again
739                 _scaled = false;
740                 _broker.informSubscribers();
741                 return true;
742         }
743
744
745         /**
746          * Re-insert the specified point range at the given index
747          * @param inPoints point array to insert
748          * @param inIndex index at which to insert the points
749          * @return true if it worked, false otherwise
750          */
751         public boolean insertRange(DataPoint[] inPoints, int inIndex)
752         {
753                 if (inIndex > _numPoints || inPoints == null)
754                 {
755                         return false;
756                 }
757                 // Make new array to copy points over to
758                 DataPoint[] newPointArray = new DataPoint[_numPoints + inPoints.length];
759                 if (inIndex > 0)
760                 {
761                         System.arraycopy(_dataPoints, 0, newPointArray, 0, inIndex);
762                 }
763                 System.arraycopy(inPoints, 0, newPointArray, inIndex, inPoints.length);
764                 if (inIndex < _numPoints)
765                 {
766                         System.arraycopy(_dataPoints, inIndex, newPointArray, inIndex+inPoints.length, _numPoints - inIndex);
767                 }
768                 // Change over to new array
769                 _dataPoints = newPointArray;
770                 _numPoints += inPoints.length;
771                 // needs to be scaled again
772                 _scaled = false;
773                 _broker.informSubscribers();
774                 return true;
775         }
776
777
778         /**
779          * Replace the track contents with the given point array
780          * @param inContents array of DataPoint objects
781          */
782         public boolean replaceContents(DataPoint[] inContents)
783         {
784                 // master field array stays the same
785                 // (would need to store field array too if we wanted to redo a load)
786                 // replace data array
787                 _dataPoints = inContents;
788                 _numPoints = _dataPoints.length;
789                 _scaled = false;
790                 _broker.informSubscribers();
791                 return true;
792         }
793 }