USGS

Isis 3.0 Object Programmers' Reference

Home

ControlNetFileV0002.cpp
1 #include "ControlNetFileV0002.h"
2 
3 #include <fstream>
4 
5 #include <google/protobuf/io/zero_copy_stream_impl.h>
6 #include <google/protobuf/io/coded_stream.h>
7 #include <boost/numeric/ublas/symmetric.hpp>
8 #include <boost/numeric/ublas/io.hpp>
9 
10 #include <QList>
11 #include <QDebug>
12 
13 #include "ControlMeasureLogData.h"
14 #include "ControlNetFileV0002.pb.h"
15 #include "FileName.h"
16 #include "IException.h"
17 #include "Latitude.h"
18 #include "Longitude.h"
19 #include "NaifStatus.h"
20 #include "TProjection.h"
21 #include "Pvl.h"
22 #include "SurfacePoint.h"
23 
24 using namespace google::protobuf;
25 using namespace google::protobuf::io;
26 using boost::numeric::ublas::symmetric_matrix;
27 using boost::numeric::ublas::upper;
28 using namespace std;
29 
30 namespace Isis {
31  ControlNetFileV0002::ControlNetFileV0002() {
32  p_networkHeader = new ControlNetFileHeaderV0002;
33  p_controlPoints = new QList<ControlPointFileEntryV0002>;
34  }
35 
36 
37  ControlNetFileV0002::~ControlNetFileV0002() {
38  delete p_networkHeader;
39  delete p_controlPoints;
40  }
41 
42 
43 
52  void ControlNetFileV0002::Read(const Pvl &header, const FileName &file) {
53  const PvlObject &protoBufferInfo = header.findObject("ProtoBuffer");
54  const PvlObject &protoBufferCore = protoBufferInfo.findObject("Core");
55 
56  BigInt headerStartPos = protoBufferCore["HeaderStartByte"];
57  BigInt headerLength = protoBufferCore["HeaderBytes"];
58 
59  fstream input(file.expanded().toAscii().data(), ios::in | ios::binary);
60  if (!input.is_open()) {
61  IString msg = "Failed to open control network file" + file.name();
62  throw IException(IException::Programmer, msg, _FILEINFO_);
63  }
64 
65  input.seekg(headerStartPos, ios::beg);
66  streampos filePos = input.tellg();
67  IstreamInputStream headerInStream(&input);
68  CodedInputStream headerCodedInStream(&headerInStream);
69  // max 512MB, warn at 400MB
70  headerCodedInStream.SetTotalBytesLimit(1024 * 1024 * 512,
71  1024 * 1024 * 400);
72 
73  // Now stream the rest of the input into the google protocol buffer.
74  try {
75  filePos += headerLength;
76  int oldLimit = headerCodedInStream.PushLimit(headerLength);
77  if (!p_networkHeader->ParseFromCodedStream(&headerCodedInStream)) {
78  IString msg = "Failed to read input control net file [" +
79  file.name() + "]";
80  throw IException(IException::Io, msg, _FILEINFO_);
81  }
82  headerCodedInStream.PopLimit(oldLimit);
83 
84  // Without closing and re-opening the protocol buffers break... no clue
85  // why other than it's got some static data around keeping track
86  // maybe. We need to do this for it to reset it's idea of the total
87  // bytes though. Doing it every time is too expensive - so we're going
88  // to just do it periodically.
89  IstreamInputStream *pointInStream = NULL;
90  CodedInputStream *pointCodedInStream = NULL;
91 
92  for(int cp = 0; cp < p_networkHeader->pointmessagesizes_size(); cp ++) {
93  if(cp % 50000 == 0 && pointCodedInStream && pointInStream) {
94  delete pointCodedInStream;
95  pointCodedInStream = NULL;
96 
97  delete pointInStream;
98  pointInStream = NULL;
99  }
100 
101  if(pointInStream == NULL) {
102  input.close();
103  input.open(file.expanded().toAscii().data(), ios::in | ios::binary);
104  input.seekg(filePos, ios::beg);
105 
106  pointInStream = new IstreamInputStream(&input);
107  pointCodedInStream = new CodedInputStream(pointInStream);
108  // max 512MB, warn at 400MB
109  pointCodedInStream->SetTotalBytesLimit(1024 * 1024 * 512,
110  1024 * 1024 * 400);
111  }
112 
113  int size = p_networkHeader->pointmessagesizes(cp);
114  oldLimit = pointCodedInStream->PushLimit(size);
115 
116  filePos += size;
118  newPoint.ParseFromCodedStream(pointCodedInStream);
119 
120  if (newPoint.type() == ControlPointFileEntryV0002::obsolete_Tie ||
121  newPoint.type() == ControlPointFileEntryV0002::obsolete_Ground) {
122  if (newPoint.aprioricovar_size())
123  newPoint.set_type(ControlPointFileEntryV0002::Constrained);
124  }
125 
126  p_controlPoints->append(newPoint);
127  pointCodedInStream->PopLimit(oldLimit);
128  }
129 
130  if(pointCodedInStream) {
131  delete pointCodedInStream;
132  pointCodedInStream = NULL;
133  }
134 
135  if(pointInStream) {
136  delete pointInStream;
137  pointInStream = NULL;
138  }
139  }
140  catch (...) {
141  string msg = "Cannot understand binary PB file";
142  throw IException(IException::Io, msg, _FILEINFO_);
143  }
144  }
145 
146  void ControlNetFileV0002::Write(const FileName &file) const {
147  // We need to populate ControlNetFileHeaderV0002::pointMessageSizes
148  p_networkHeader->clear_pointmessagesizes();
149  BigInt pointsSize = 0;
150  BigInt numMeasures = 0;
151  for(int cpIndex = 0; cpIndex < p_controlPoints->size(); cpIndex ++) {
152  numMeasures += p_controlPoints->at(cpIndex).measures_size();
153  int size = p_controlPoints->at(cpIndex).ByteSize();
154  pointsSize += size;
155  p_networkHeader->add_pointmessagesizes(size);
156  }
157 
158  streampos coreHeaderSize = p_networkHeader->ByteSize();
159 
160  const int labelBytes = 65536;
161  fstream output(file.expanded().toAscii().data(),
162  ios::out | ios::trunc | ios::binary);
163 
164  char *blankLabel = new char[labelBytes];
165  memset(blankLabel, 0, labelBytes);
166  output.write(blankLabel, labelBytes);
167  delete [] blankLabel;
168 
169  streampos startCoreHeaderPos = output.tellp();
170 
171  if (!p_networkHeader->SerializeToOstream(&output)) {
172  IString msg = "Failed to write output control network file [" +
173  file.name() + "]";
174  throw IException(IException::Io, msg, _FILEINFO_);
175  }
176 
177  streampos curPosition = startCoreHeaderPos + coreHeaderSize;
178  for(int cpIndex = 0; cpIndex < p_controlPoints->size(); cpIndex ++) {
179  if(!p_controlPoints->at(cpIndex).IsInitialized()) {
180  IString msg = "Failed to write output control network file [" +
181  file.name() + "] because control points are missing required "
182  "fields";
183  throw IException(IException::Io, msg, _FILEINFO_);
184  }
185 
186  if(!p_controlPoints->at(cpIndex).SerializeToOstream(&output)) {
187  IString msg = "Failed to write output control network file [" +
188  file.name() + "] while attempting to write control points";
189  throw IException(IException::Io, msg, _FILEINFO_);
190  }
191 
192  curPosition += p_controlPoints->at(cpIndex).ByteSize();
193  }
194 
195  Pvl p;
196  PvlObject protoObj("ProtoBuffer");
197 
198  PvlObject protoCore("Core");
199  protoCore.addKeyword(PvlKeyword("HeaderStartByte",
200  toString((BigInt) startCoreHeaderPos)));
201  protoCore.addKeyword(PvlKeyword("HeaderBytes", toString((BigInt) coreHeaderSize)));
202  protoCore.addKeyword(PvlKeyword("PointsStartByte",
203  toString((BigInt) ( startCoreHeaderPos + coreHeaderSize))));
204  protoCore.addKeyword(PvlKeyword("PointsBytes",
205  toString(pointsSize)));
206  protoObj.addObject(protoCore);
207 
208  PvlGroup netInfo("ControlNetworkInfo");
209  netInfo.addComment("This group is for informational purposes only");
210  netInfo += PvlKeyword("NetworkId", p_networkHeader->networkid().c_str());
211  netInfo += PvlKeyword("TargetName", p_networkHeader->targetname().c_str());
212  netInfo += PvlKeyword("UserName", p_networkHeader->username().c_str());
213  netInfo += PvlKeyword("Created", p_networkHeader->created().c_str());
214  netInfo += PvlKeyword("LastModified", p_networkHeader->lastmodified().c_str());
215  netInfo += PvlKeyword("Description", p_networkHeader->description().c_str());
216  netInfo += PvlKeyword("NumberOfPoints", toString(p_controlPoints->size()));
217  netInfo += PvlKeyword("NumberOfMeasures", toString(numMeasures));
218  netInfo += PvlKeyword("Version", "2");
219  protoObj.addGroup(netInfo);
220 
221  p.addObject(protoObj);
222 
223  output.seekp(0, ios::beg);
224  output << p;
225  output << '\n';
226  output.close();
227  }
228 
229 
246  Pvl ControlNetFileV0002::toPvl() const {
247  Pvl pvl;
248  pvl.addObject(PvlObject("ControlNetwork"));
249  PvlObject &network = pvl.findObject("ControlNetwork");
250 
251  network += PvlKeyword("NetworkId", p_networkHeader->networkid().c_str());
252  network += PvlKeyword("TargetName", p_networkHeader->targetname().c_str());
253  network += PvlKeyword("UserName", p_networkHeader->username().c_str());
254  network += PvlKeyword("Created", p_networkHeader->created().c_str());
255  network += PvlKeyword("LastModified", p_networkHeader->lastmodified().c_str());
256  network += PvlKeyword("Description", p_networkHeader->description().c_str());
257 
258  // This is the Pvl version we're converting to
259  network += PvlKeyword("Version", "3");
260 
261  // Get Target Radii from naif kernel
262  PvlGroup pvlRadii;
263  QString target = (QString)network.findKeyword("TargetName",Pvl::Traverse);
264  if (target != "") {
265  try {
266  NaifStatus::CheckErrors();
267  pvlRadii = TProjection::TargetRadii(target);
268  }
269  catch(IException &e) {
270  IString msg = "The target name, " + target + ", is not recognized.";
271  throw IException(e, IException::Unknown, msg, _FILEINFO_);
272  }
273  }
274 
275  ControlPointFileEntryV0002 binaryPoint;
276  foreach(binaryPoint, *p_controlPoints) {
277  PvlObject pvlPoint("ControlPoint");
278 
279  if(binaryPoint.type() == ControlPointFileEntryV0002::Fixed)
280  pvlPoint += PvlKeyword("PointType", "Fixed");
281  else if(binaryPoint.type() == ControlPointFileEntryV0002::Constrained)
282  pvlPoint += PvlKeyword("PointType", "Constrained");
283  else
284  pvlPoint += PvlKeyword("PointType", "Free");
285 
286  pvlPoint += PvlKeyword("PointId", binaryPoint.id().c_str());
287  pvlPoint += PvlKeyword("ChooserName", binaryPoint.choosername().c_str());
288  pvlPoint += PvlKeyword("DateTime", binaryPoint.datetime().c_str());
289 
290  if (binaryPoint.editlock()) {
291  pvlPoint += PvlKeyword("EditLock", "True");
292  }
293 
294  if (binaryPoint.ignore()) {
295  pvlPoint += PvlKeyword("Ignore", "True");
296  }
297 
298  switch (binaryPoint.apriorisurfpointsource()) {
299  case ControlPointFileEntryV0002::None:
300  break;
301  case ControlPointFileEntryV0002::User:
302  pvlPoint += PvlKeyword("AprioriXYZSource", "User");
303  break;
304  case ControlPointFileEntryV0002::AverageOfMeasures:
305  pvlPoint += PvlKeyword("AprioriXYZSource", "AverageOfMeasures");
306  break;
307  case ControlPointFileEntryV0002::Reference:
308  pvlPoint += PvlKeyword("AprioriXYZSource", "Reference");
309  break;
310  case ControlPointFileEntryV0002::Basemap:
311  pvlPoint += PvlKeyword("AprioriXYZSource", "Basemap");
312  break;
313  case ControlPointFileEntryV0002::BundleSolution:
314  pvlPoint += PvlKeyword("AprioriXYZSource", "BundleSolution");
315  break;
316  case ControlPointFileEntryV0002::Ellipsoid:
317  case ControlPointFileEntryV0002::DEM:
318  break;
319  }
320 
321  if (binaryPoint.has_apriorisurfpointsourcefile())
322  pvlPoint += PvlKeyword("AprioriXYZSourceFile",
323  binaryPoint.apriorisurfpointsourcefile().c_str());
324 
325  switch (binaryPoint.aprioriradiussource()) {
326  case ControlPointFileEntryV0002::None:
327  break;
328  case ControlPointFileEntryV0002::User:
329  pvlPoint += PvlKeyword("AprioriRadiusSource", "User");
330  break;
331  case ControlPointFileEntryV0002::AverageOfMeasures:
332  pvlPoint += PvlKeyword("AprioriRadiusSource", "AverageOfMeasures");
333  break;
334  case ControlPointFileEntryV0002::Reference:
335  pvlPoint += PvlKeyword("AprioriRadiusSource", "Reference");
336  break;
337  case ControlPointFileEntryV0002::Basemap:
338  pvlPoint += PvlKeyword("AprioriRadiusSource", "Basemap");
339  break;
340  case ControlPointFileEntryV0002::BundleSolution:
341  pvlPoint += PvlKeyword("AprioriRadiusSource", "BundleSolution");
342  break;
343  case ControlPointFileEntryV0002::Ellipsoid:
344  pvlPoint += PvlKeyword("AprioriRadiusSource", "Ellipsoid");
345  break;
346  case ControlPointFileEntryV0002::DEM:
347  pvlPoint += PvlKeyword("AprioriRadiusSource", "DEM");
348  break;
349  }
350 
351  if (binaryPoint.has_aprioriradiussourcefile())
352  pvlPoint += PvlKeyword("AprioriRadiusSourceFile",
353  binaryPoint.aprioriradiussourcefile().c_str());
354 
355  if(binaryPoint.has_apriorix()) {
356  pvlPoint += PvlKeyword("AprioriX", toString(binaryPoint.apriorix()), "meters");
357  pvlPoint += PvlKeyword("AprioriY", toString(binaryPoint.aprioriy()), "meters");
358  pvlPoint += PvlKeyword("AprioriZ", toString(binaryPoint.aprioriz()), "meters");
359 
360  // Get surface point, convert to lat,lon,radius and output as comment
361  SurfacePoint apriori;
362  apriori.SetRectangular(
363  Displacement(binaryPoint.apriorix(),Displacement::Meters),
364  Displacement(binaryPoint.aprioriy(),Displacement::Meters),
365  Displacement(binaryPoint.aprioriz(),Displacement::Meters));
366  pvlPoint.findKeyword("AprioriX").addComment("AprioriLatitude = " +
367  toString(apriori.GetLatitude().degrees()) +
368  " <degrees>");
369  pvlPoint.findKeyword("AprioriY").addComment("AprioriLongitude = " +
370  toString(apriori.GetLongitude().degrees()) +
371  " <degrees>");
372  pvlPoint.findKeyword("AprioriZ").addComment("AprioriRadius = " +
373  toString(apriori.GetLocalRadius().meters()) +
374  " <meters>");
375 
376  if(binaryPoint.aprioricovar_size()) {
377  PvlKeyword matrix("AprioriCovarianceMatrix");
378  matrix += toString(binaryPoint.aprioricovar(0));
379  matrix += toString(binaryPoint.aprioricovar(1));
380  matrix += toString(binaryPoint.aprioricovar(2));
381  matrix += toString(binaryPoint.aprioricovar(3));
382  matrix += toString(binaryPoint.aprioricovar(4));
383  matrix += toString(binaryPoint.aprioricovar(5));
384  pvlPoint += matrix;
385 
386  if (pvlRadii.hasKeyword("EquatorialRadius")) {
387  apriori.SetRadii(
388  Distance(pvlRadii["EquatorialRadius"],Distance::Meters),
389  Distance(pvlRadii["EquatorialRadius"],Distance::Meters),
390  Distance(pvlRadii["PolarRadius"],Distance::Meters));
391  symmetric_matrix<double, upper> covar;
392  covar.resize(3);
393  covar.clear();
394  covar(0, 0) = binaryPoint.aprioricovar(0);
395  covar(0, 1) = binaryPoint.aprioricovar(1);
396  covar(0, 2) = binaryPoint.aprioricovar(2);
397  covar(1, 1) = binaryPoint.aprioricovar(3);
398  covar(1, 2) = binaryPoint.aprioricovar(4);
399  covar(2, 2) = binaryPoint.aprioricovar(5);
400  apriori.SetRectangularMatrix(covar);
401  QString sigmas = "AprioriLatitudeSigma = " +
402  toString(apriori.GetLatSigmaDistance().meters()) +
403  " <meters> AprioriLongitudeSigma = " +
404  toString(apriori.GetLonSigmaDistance().meters()) +
405  " <meters> AprioriRadiusSigma = " +
406  toString(apriori.GetLocalRadiusSigma().meters()) +
407  " <meters>";
408  pvlPoint.findKeyword("AprioriCovarianceMatrix").addComment(sigmas);
409  }
410  }
411  }
412 
413  if(binaryPoint.latitudeconstrained())
414  pvlPoint += PvlKeyword("LatitudeConstrained", "True");
415 
416  if(binaryPoint.longitudeconstrained())
417  pvlPoint += PvlKeyword("LongitudeConstrained", "True");
418 
419  if(binaryPoint.radiusconstrained())
420  pvlPoint += PvlKeyword("RadiusConstrained", "True");
421 
422  if(binaryPoint.has_adjustedx()) {
423  pvlPoint += PvlKeyword("AdjustedX", toString(binaryPoint.adjustedx()), "meters");
424  pvlPoint += PvlKeyword("AdjustedY", toString(binaryPoint.adjustedy()), "meters");
425  pvlPoint += PvlKeyword("AdjustedZ", toString(binaryPoint.adjustedz()), "meters");
426 
427  // Get surface point, convert to lat,lon,radius and output as comment
428  SurfacePoint adjusted;
429  adjusted.SetRectangular(
430  Displacement(binaryPoint.adjustedx(),Displacement::Meters),
431  Displacement(binaryPoint.adjustedy(),Displacement::Meters),
432  Displacement(binaryPoint.adjustedz(),Displacement::Meters));
433  pvlPoint.findKeyword("AdjustedX").addComment("AdjustedLatitude = " +
434  toString(adjusted.GetLatitude().degrees()) +
435  " <degrees>");
436  pvlPoint.findKeyword("AdjustedY").addComment("AdjustedLongitude = " +
437  toString(adjusted.GetLongitude().degrees()) +
438  " <degrees>");
439  pvlPoint.findKeyword("AdjustedZ").addComment("AdjustedRadius = " +
440  toString(adjusted.GetLocalRadius().meters()) +
441  " <meters>");
442 
443  if(binaryPoint.adjustedcovar_size()) {
444  PvlKeyword matrix("AdjustedCovarianceMatrix");
445  matrix += toString(binaryPoint.adjustedcovar(0));
446  matrix += toString(binaryPoint.adjustedcovar(1));
447  matrix += toString(binaryPoint.adjustedcovar(2));
448  matrix += toString(binaryPoint.adjustedcovar(3));
449  matrix += toString(binaryPoint.adjustedcovar(4));
450  matrix += toString(binaryPoint.adjustedcovar(5));
451  pvlPoint += matrix;
452 
453  if (pvlRadii.hasKeyword("EquatorialRadius")) {
454  adjusted.SetRadii(
455  Distance(pvlRadii["EquatorialRadius"],Distance::Meters),
456  Distance(pvlRadii["EquatorialRadius"],Distance::Meters),
457  Distance(pvlRadii["PolarRadius"],Distance::Meters));
458  symmetric_matrix<double, upper> covar;
459  covar.resize(3);
460  covar.clear();
461  covar(0, 0) = binaryPoint.adjustedcovar(0);
462  covar(0, 1) = binaryPoint.adjustedcovar(1);
463  covar(0, 2) = binaryPoint.adjustedcovar(2);
464  covar(1, 1) = binaryPoint.adjustedcovar(3);
465  covar(1, 2) = binaryPoint.adjustedcovar(4);
466  covar(2, 2) = binaryPoint.adjustedcovar(5);
467  adjusted.SetRectangularMatrix(covar);
468  QString sigmas = "AdjustedLatitudeSigma = " +
469  toString(adjusted.GetLatSigmaDistance().meters()) +
470  " <meters> AdjustedLongitudeSigma = " +
471  toString(adjusted.GetLonSigmaDistance().meters()) +
472  " <meters> AdjustedRadiusSigma = " +
473  toString(adjusted.GetLocalRadiusSigma().meters()) +
474  " <meters>";
475  pvlPoint.findKeyword("AdjustedCovarianceMatrix").addComment(sigmas);
476  }
477  }
478  }
479 
480  for (int j = 0; j < binaryPoint.measures_size(); j++) {
481  PvlGroup pvlMeasure("ControlMeasure");
483  binaryMeasure = binaryPoint.measures(j);
484  pvlMeasure += PvlKeyword("SerialNumber", binaryMeasure.serialnumber().c_str());
485 
486  switch(binaryMeasure.type()) {
487  case ControlPointFileEntryV0002_Measure_MeasureType_Candidate:
488  pvlMeasure += PvlKeyword("MeasureType", "Candidate");
489  break;
490  case ControlPointFileEntryV0002_Measure_MeasureType_Manual:
491  pvlMeasure += PvlKeyword("MeasureType", "Manual");
492  break;
493  case ControlPointFileEntryV0002_Measure_MeasureType_RegisteredPixel:
494  pvlMeasure += PvlKeyword("MeasureType", "RegisteredPixel");
495  break;
496  case ControlPointFileEntryV0002_Measure_MeasureType_RegisteredSubPixel:
497  pvlMeasure += PvlKeyword("MeasureType", "RegisteredSubPixel");
498  break;
499  }
500 
501  if(binaryMeasure.has_choosername())
502  pvlMeasure += PvlKeyword("ChooserName", binaryMeasure.choosername().c_str());
503 
504  if(binaryMeasure.has_datetime())
505  pvlMeasure += PvlKeyword("DateTime", binaryMeasure.datetime().c_str());
506 
507  if(binaryMeasure.editlock())
508  pvlMeasure += PvlKeyword("EditLock", "True");
509 
510  if(binaryMeasure.ignore())
511  pvlMeasure += PvlKeyword("Ignore", "True");
512 
513  if(binaryMeasure.has_sample())
514  pvlMeasure += PvlKeyword("Sample", toString(binaryMeasure.sample()));
515 
516  if(binaryMeasure.has_line())
517  pvlMeasure += PvlKeyword("Line", toString(binaryMeasure.line()));
518 
519  if (binaryMeasure.has_diameter())
520  pvlMeasure += PvlKeyword("Diameter", toString(binaryMeasure.diameter()));
521 
522  if (binaryMeasure.has_apriorisample())
523  pvlMeasure += PvlKeyword("AprioriSample", toString(binaryMeasure.apriorisample()));
524 
525  if (binaryMeasure.has_aprioriline())
526  pvlMeasure += PvlKeyword("AprioriLine", toString(binaryMeasure.aprioriline()));
527 
528  if (binaryMeasure.has_samplesigma())
529  pvlMeasure += PvlKeyword("SampleSigma", toString(binaryMeasure.samplesigma()),
530  "pixels");
531 
532  if (binaryMeasure.has_samplesigma())
533  pvlMeasure += PvlKeyword("LineSigma", toString(binaryMeasure.linesigma()),
534  "pixels");
535 
536  if(binaryMeasure.has_sampleresidual())
537  pvlMeasure += PvlKeyword("SampleResidual", toString(binaryMeasure.sampleresidual()),
538  "pixels");
539 
540  if(binaryMeasure.has_lineresidual())
541  pvlMeasure += PvlKeyword("LineResidual", toString(binaryMeasure.lineresidual()),
542  "pixels");
543 
544  if(binaryMeasure.has_jigsawrejected()) {
545  pvlMeasure += PvlKeyword("JigsawRejected", toString(binaryMeasure.jigsawrejected()));
546  }
547 
548  for(int logEntry = 0;
549  logEntry < binaryMeasure.log_size();
550  logEntry ++) {
552  binaryMeasure.log(logEntry);
553 
554  ControlMeasureLogData interpreter(log);
555  pvlMeasure += interpreter.ToKeyword();
556  }
557 
558  if(binaryPoint.has_referenceindex() &&
559  binaryPoint.referenceindex() == j)
560  pvlMeasure += PvlKeyword("Reference", "True");
561 
562  pvlPoint.addGroup(pvlMeasure);
563  }
564 
565  network.addObject(pvlPoint);
566  }
567  return pvl;
568  }
569 }
570