GeoTessJavaExamples  2.0
Tomography2D.java
Go to the documentation of this file.
1 package gov.sandia.geotess.examples;
2 
3 import gov.sandia.geotess.Data;
4 import gov.sandia.geotess.GeoTessException;
5 import gov.sandia.geotess.GeoTessGrid;
6 import gov.sandia.geotess.GeoTessMetaData;
7 import gov.sandia.geotess.GeoTessModel;
8 import gov.sandia.geotess.GeoTessModelUtils;
9 import gov.sandia.geotess.GeoTessPosition;
10 import gov.sandia.geotess.GeoTessUtils;
11 import gov.sandia.gmp.util.globals.DataType;
12 import gov.sandia.gmp.util.globals.InterpolatorType;
13 import gov.sandia.gmp.util.numerical.polygon.GreatCircle;
14 import gov.sandia.gmp.util.numerical.polygon.Polygon;
15 import gov.sandia.gmp.util.numerical.vector.EarthShape;
16 
17 import java.io.File;
18 import java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Date;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.Map.Entry;
25 
26 import static java.lang.Math.toRadians;
27 
28 /**
29  * This application illustrates how to use features available in GeoTessJava to
30  * execute tomography on a 2D model. This application does not implement
31  * tomography but merely illustrates how to call methods in GeoTessJava that one
32  * would likely need to perform tomography.
33  * <p>
34  * This application illustrates the following tasks:
35  * <ol>
36  * <li>Generate 11 great circle ray paths along the surface of the WGS84
37  * ellipsoid.
38  * <li>Generate a 2D, global starting model consisting of values of attenuation
39  * as a function of geographic position on the globe.
40  * <li>Limit the application of tomography to a region of the Earth in North
41  * America. This limitation is optional.
42  * <li>Trace rays through the starting model, calculating the path integral of
43  * the attenuation along the ray path and the weights (data kernels) of all the
44  * grid nodes attributable to interpolation of points on ray paths.
45  * <li>Call methods in GeoTessJava to identify the neighbors of a specified
46  * node. These methods are needed to apply regularization of the tomography
47  * matrix.
48  * <li>Apply changes in model attribute values computed by tomographic
49  * inversion.
50  * <li>Compute a new GeoTessModel whose attribute values are the number of times
51  * each grid node was 'touched' by one of the ray paths (hit count).
52  * <li>Execute application GeoTessBuilder to generate a new grid that is more
53  * refined in areas of high hit count.
54  * <li>Generate a new model based on the new, refined grid generated with
55  * GeoTessBuilder but containing attenuation values copied or interpolated from
56  * the original model.
57  * </ol>
58  *
59  * @author sballar
60  *
61  */
62 public class Tomography2D
63 {
64  // seismic station ANMO near Albuquerque, New Mexico, USA.
65  // Latitude and longitude of the station are converted to an
66  // earth centered unit vector.
67  private static double[] ANMO = EarthShape.WGS84_RCONST.getVectorDegrees(34.9462, -106.4567);
68 
69  /**
70  * Main program that calls a bunch of methods that actually implement the tasks
71  * outlined above. This program should be run from directory GeoTessBuilderExamples/tomo2dTest.
72  * @param args no command line arguments are required.
73  */
74  public static void main(String[] args)
75  {
76  // instantiate a Tomography2D object.
77  Tomography2D tomo = new Tomography2D();
78 
79  try
80  {
81  // Generate a starting model. In this example, the starting model is
82  // very simple, consisting of constant values of attenuation at each
83  // node of the grid. Real applications could start from some other
84  // starting model loaded from a file.
85  GeoTessModel model = tomo.startingModel();
86 
87  // Generate 11 great circle ray paths for use in this example. In
88  // a real application, these ray paths would surely be loaded from
89  // a file.
90  ArrayList<double[][]> rayPaths = tomo.generateRayPaths();
91 
92  // We will limit the active nodes in the model using a Polygon
93  // object. Nodes that reside inside the polygon will be the only
94  // ones modified by tomography. The others are simply carried along
95  // but do not participate in tomography. The polygon that we will
96  // define is a small circle with radius 38 degrees centered on
97  // seismic station ANMO. For information on other ways to define
98  // Polygons, see the User's Manual or GeoTess code documentation.
99  // If we wanted to execute a global tomography model, we would
100  // not apply this step.
101  Polygon polygon = new Polygon(ANMO, toRadians(38.), 100);
102  model.setActiveRegion(polygon);
103 
104  // Trace rays though our model and extract integrated attribute
105  // values along the ray path and interpolation coefficient 'weights'
106  // associated with each ray path.
107  tomo.integrateRayPaths(model, rayPaths);
108 
109  // call a method that illustrates how to find the indices of model
110  // points that are neighbors of a specified model point. These are
111  // often used in tomography to perform regularization or smoothing.
112  tomo.regularization(model);
113 
114  // At this point, a real tomography application would actually
115  // perform tomographic inversion, resulting in a vector containing
116  // changes in attenuation that should be applied at each model
117  // point. For this example, we will simply specify these changes
118  // and apply them.
119  float[] attributeChanges = new float[model.getNPoints()];
120  Arrays.fill(attributeChanges, 0.01F);
121 
122  // apply the attenuation changes.
123  tomo.applyAttributeChanges(model, 0, attributeChanges);
124 
125  // Now we will assume that the user wishes to refine the model
126  // in regions of high hit count before executing the next
127  // iteration of tomography. In a real tomography application
128  // this involves several steps which need to be implemented in
129  // separate applications.
130  // First, we build a new GeoTessModel where the attribute
131  // is HIT_COUNT, write that model to a file and
132  // terminate execution. Then we use the GeoTessBuilder
133  // application to generate a refined grid. Then, in
134  // a new application, we build a new GeoTessModel based
135  // on the refined grid, which contains attenuation values
136  // that are either copied or interpolated from the original
137  // model.
138 
139  // Given a current model and a collection of ray paths, build
140  // a new model where the attribute is HIT_COUNT.
141  GeoTessModel hitCountModel = tomo.hitCount(model, rayPaths);
142 
143  // save the hitCountModel to a file
144  hitCountModel.writeModel("hitcount_model_original.geotess");
145 
146  // At this point, this application should be terminated and
147  // application GeoTessBuilder should be executed to build
148  // a new model that has a grid that has been refined in areas of
149  // high hit count. There is a sample GeoTessBuilder properties file in
150  // GeoTessBuilderExamples/tomo2dTest/gridbuilder_refine_model.properties
151  // that illustrates how to build model
152  // hitcount_model_refined.geotess.
153  // See the GeoTess User's Manual for more information about the
154  // GeoTessBuilder application. After GeoTessBulder has been
155  // executed, a new application should be executed that calls
156  // the remaining methods. This example includes everything in
157  // one application, i.e., the method calls below assumes that
158  // GeoTessBuilder has already been executed.
159 
160  // Load the refined hit count model from file. This model
161  // has a grid that is refined in areas of high hit count.
162  // The model attribute is HIT_COUNT. Hit count attribute
163  // values at points that did not exist in the original hit
164  // count model are interpolated values.
165  GeoTessModel hitCountModelRefined = new GeoTessModel(
166  "hitcount_model_refined.geotess");
167 
168  // At this point, we have an original model and a new, refined grid
169  // that has all the grid nodes of the original model but extra grid
170  // nodes that are not in the original model. The next method
171  // generates a new model based on the refined grid, where values for
172  // missing grid nodes are generated by either copying or
173  // interpolating attenuation values from the original model.
174  GeoTessModel refinedModel = tomo.refineModel(model,
175  hitCountModelRefined);
176 
177  refinedModel.writeModel("attenuation_model_refined_java.geotess");
178 
179  System.out.println("Done.");
180 
181  }
182  catch (Exception e)
183  {
184  e.printStackTrace();
185  }
186  }
187 
188  /**
189  * Generate a starting model for the Tomography2D example program. The model
190  * will have a single attribute (attenuation), and will be a 2D model, i.e.,
191  * there will be no radius associated with the nodes of the model. For this
192  * simple example, the model is populated with a single, constant value of
193  * attenuation, 0.1
194  *
195  * @return a GeoTessModel
196  * @throws IOException
197  * @throws GeoTessException
198  */
199  protected GeoTessModel startingModel() throws Exception
200  {
201  System.out
202  .println("************************************************************");
203  System.out.println("*");
204  System.out.println("* startingModel()");
205  System.out.println("*");
206  System.out
207  .println("************************************************************");
208  System.out.println();
209 
210  // Load an existing GeoTessGrid file. Several grid files were
211  // delivered with the GeoTess package and can be found in the
212  // GeoTessModels directory. For this example, a grid consisting of
213  // uniform 4 degree triangles, was specified.
214  File gridFile = new File("geotess_grid_04000.geotess");
215 
216  if (!gridFile.exists())
217  throw new Exception("\nGrid file geotess_grid_04000.geotess does not exist.\n"
218  + "This program should be run from directory GeoTessBuilderExamples/tomo2dTest\n");
219 
220  // Create a MetaData object in which we can specify information
221  // needed for model contruction.
222  GeoTessMetaData metaData = new GeoTessMetaData();
223 
224  // Specify a description of the model. This information is not
225  // processed in any way by GeoTess. It is carried around for
226  // information purposes.
227  metaData.setDescription("GeoTessModel for example program Tomography2D");
228 
229  // This model will have only one layer, named 'surface'.
230  metaData.setLayerNames("surface");
231 
232  // Specify one attribute: attenuation, with units of 1/km
233  metaData.setAttributes("attenuation", "1/km");
234 
235  // specify the DataType for the data. All attributes, in all
236  // profiles, will have the same data type.
237  metaData.setDataType(DataType.FLOAT);
238 
239  // specify the name of the software that is going to generate
240  // the model. This gets stored in the model for future reference.
241  metaData.setModelSoftwareVersion(getClass().getCanonicalName());
242 
243  // specify the date when the model was generated. This gets
244  // stored in the model for future reference.
245  metaData.setModelGenerationDate(new Date().toString());
246 
247  // call a GeoTessModel constructor to build the model. This will
248  // load the grid, and initialize all the data structures to null.
249  // To be useful, we will have to populate the data structures.
250  GeoTessModel model = new GeoTessModel(gridFile, metaData);
251 
252  // generate some data and store it in the model.
253  for (int vtx = 0; vtx < model.getGrid().getNVertices(); ++vtx)
254  // create ProfileSurface objects with a constant value of type
255  // float.
256  model.setProfile(vtx, Data.getDataFloat(0.1F));
257 
258  System.out.println(model);
259 
260  return model;
261 
262  }
263 
264  /**
265  * Generate 11 ray paths on the surface of the WGS84 ellipsoid. Each ray
266  * path is defined by two unit vector locations, one representing an event,
267  * and the other a station. All of the ray paths generated here have the
268  * same station, ANMO, located near Albuquerque, New Mexico, USA. The first
269  * ray path has zero length (the event is colocated with the station). The
270  * remaining events range in distance from 5 to 50 degrees in distance and 0
271  * to 360 in azimuth from the station.
272  * <p>
273  * There is no requirement in GeoTess that the ray paths be represented this
274  * way, this parameterization was designed for this example program. In
275  * fact, GeoTess has no concept of a ray path at all.
276  *
277  * @return an ArrayList of raypaths. Each ray path consists of two unit
278  * vectors, one for the event and one for the station.
279  * @throws IOException
280  * @throws GeoTessException
281  */
282  protected ArrayList<double[][]> generateRayPaths() throws Exception
283  {
284  System.out
285  .println("************************************************************");
286  System.out.println("*");
287  System.out.println("* generateRayPaths()");
288  System.out.println("*");
289  System.out
290  .println("************************************************************");
291  System.out.println();
292 
293  ArrayList<double[][]> rayPaths = new ArrayList<double[][]>(10);
294 
295  for (int i = 0; i <= 10; ++i)
296  {
297  double[] event = new double[3];
298  // populate event with a unit vector obtained by moving station ANMO
299  // some distance and azimuth specified in radians
300  GeoTessUtils.move(ANMO, toRadians(i * 5), toRadians(i * 36), event);
301 
302  // specify a new raypath from the new event to station ANMO
303  double[][] rayPath = new double[][] { event, ANMO };
304 
305  rayPaths.add(rayPath);
306  }
307 
308  // the remainder of this method prints out information about the ray
309  // paths.
310 
311  System.out
312  .println(" id event station distance azimuth");
313  for (int i = 0; i < rayPaths.size(); ++i)
314  {
315  double[][] rayPath = rayPaths.get(i);
316  double[] event = rayPath[0];
317  double[] station = rayPath[1];
318 
319  System.out.printf("%3d %s %s %10.4f %10.4f%n", i,
320  EarthShape.WGS84.getLatLonString(event, 4),
321  EarthShape.WGS84.getLatLonString(station, 4),
322  GeoTessUtils.angleDegrees(station, event),
323  GeoTessUtils.azimuthDegrees(station, event, Double.NaN));
324  }
325  System.out.println();
326 
327  ArrayList<double[]> points = new ArrayList<double[]>();
328  for (double[][] ray : rayPaths)
329  {
330  GreatCircle gc = new GreatCircle(ray[0], ray[1]);
331  for (double[] point : gc.getPoints(
332  (int) Math.ceil(gc.getDistance() / Math.toRadians(1) + 1),
333  false))
334  if (!Double.isNaN(point[0]))
335  points.add(point);
336  }
337 
338  GeoTessModelUtils.vtkPoints(points, "ray_path_points.vtk");
339 
340  return rayPaths;
341  }
342 
343  /**
344  * For every ray path, trace the ray through the model. Compute the integral
345  * of the model attribute along the ray path. Also accumulate the 'weight'
346  * associated with each grid node during interpolation of the attribute
347  * values along the ray path.
348  *
349  * <p>
350  * The GeoTess method used to compute the required information assume that
351  * each ray path is a great circle path from event to station. The radii of
352  * the points along the ray path are assumed to coincide with the surface of
353  * the WGS84 ellipsoid.
354  *
355  * <p>
356  * This method doesn't do anything with the results (the integrated value
357  * and the weights). This method merely serves as an example of how to
358  * extract the relevant information from a GeoTessModel. In a real
359  * tomography application, additional code would be required to transfer the
360  * information to tomographic matrices for inversion.
361  *
362  * @param model
363  * @param rayPaths
364  * @throws GeoTessException
365  */
366  protected void integrateRayPaths(GeoTessModel model, ArrayList<double[][]> rayPaths)
367  throws Exception
368  {
369  System.out
370  .println("************************************************************");
371  System.out.println("*");
372  System.out.println("* rayTrace()");
373  System.out.println("*");
374  System.out
375  .println("************************************************************");
376  System.out.println();
377 
378  // the index of the attribute that we want to integrate along the ray
379  // paths.
380  int attribute = 0;
381 
382  // approximate point spacing to use for numerical integration.
383  // one tenth of a degree, converted to radians.
384  double pointSpacing = toRadians(0.1);
385 
386  // the radius of the earth in km. If user wishes to assume a spherical
387  // earth, the radius can be specified here. By specifying a value
388  // <= 0 km, GeoTess will compute local values of earth radius assuming
389  // the WGS84 ellipsoid.
390  double earthRadius = -1;
391 
392  // horizontal interpolation type; either LINEAR or NATURAL_NEIGHBOR
393  InterpolatorType interpType = InterpolatorType.NATURAL_NEIGHBOR;
394 
395  // weights will be a map from a model point index to the weight
396  // ascribed to that point index by the integration points along the ray.
397  // The sum of all the weights will equal the length of the ray path in
398  // km.
399  HashMap<Integer, Double> weights = new HashMap<Integer, Double>();
400 
401  // loop over the ray paths
402  for (int i = 0; i < rayPaths.size(); ++i)
403  {
404  // each ray path is comprised of two unit vectors, one for the event
405  // and one for the station.
406  double[][] rayPath = rayPaths.get(i);
407  double[] event = rayPath[0];
408  double[] station = rayPath[1];
409 
410  GreatCircle greatCircle = new GreatCircle(event, station);
411 
412  // we want a set of weights for each ray path, so we need to clear
413  // the map in between calls to getPathIntegral().
414  weights.clear();
415 
416  // integrate the attribute of interest along the ray path.
417  // Also accumulate the weights ascribed to all the grid nodes
418  // 'touched' by the integration points that define the ray path. The
419  // sum of all the weights will equal the length of the ray path in
420  // km.
421  double attenuation = model.getPathIntegral2D(attribute,
422  greatCircle, pointSpacing, earthRadius, interpType, weights);
423 
424  // the rest of the code in this method prints information about the
425  // ray path and its weights to the screen. In a real tomography
426  // application, we would transfer the information into other data
427  // structures for use in tomography.
428 
429  // print out a bunch of information about the ray, including the
430  // value of attenuation
431  System.out
432  .println("----------------------------------------------------------------------------");
433  System.out
434  .println("ray station event distance azimuth attenuation");
435  System.out.printf("%3d %s %s %10.4f %10.4f %12.5f%n%n", i,
436  EarthShape.WGS84.getLatLonString(station, 4),
437  EarthShape.WGS84.getLatLonString(event, 4),
438  GeoTessUtils.angleDegrees(station, event),
439  GeoTessUtils.azimuthDegrees(station, event, Double.NaN),
440  attenuation);
441 
442  // print out information about the grid nodes and weights.
443  System.out
444  .println("pointId weight | point lat, lon, distance and azimuth from station");
445 
446  double sumWeights = 0;
447 
448  if (weights.size() == 0)
449  System.out
450  .println("No weights because event-station distance = 0");
451 
452  for (Entry<Integer, Double> entry : weights.entrySet())
453  {
454  int pointIndex = entry.getKey();
455  double weight = entry.getValue();
456 
457  sumWeights += weight;
458 
459  if (pointIndex < 0)
460  {
461  System.out.printf("%7d %9.2f | outside polygon%n",
462  pointIndex, weight);
463  }
464  else
465  {
466  double[] gridNode = model.getPointMap().getPointUnitVector(
467  pointIndex);
468 
469  System.out.printf("%7d %9.2f | %s %10.4f %10.4f%n",
470  pointIndex, weight, EarthShape.WGS84
471  .getLatLonString(gridNode), GeoTessUtils
472  .angleDegrees(station, gridNode),
473  GeoTessUtils.azimuthDegrees(station, gridNode,
474  Double.NaN));
475  }
476  }
477  System.out.println();
478 
479  System.out.printf("Sum of weights = %10.4f km %n%n", sumWeights);
480  }
481  }
482 
483  /**
484  * Find the indices of the model 'points' that are the neighbors of each
485  * model point. In a real tomography application, this information would be
486  * used to apply regularization. Here, the GeoTessGrid is interrogated for
487  * the required information, but nothing is done with it.
488  *
489  * @param model
490  */
491  protected void regularization(GeoTessModel model)
492  {
493  System.out
494  .println("************************************************************");
495  System.out.println("*");
496  System.out.println("* regularization()");
497  System.out.println("*");
498  System.out
499  .println("************************************************************");
500  System.out.println();
501 
502  // tessellaltion index is zero because 2D models use grids that consist
503  // of only one multi-level tessellation.
504  int tessId = 0;
505 
506  // find the index of the last level in the multi-level tessellation.
507  int level = model.getGrid().getTopLevel(tessId);
508 
509  // order specifies the maximum number of triangle edges that are to be
510  // traversed when searching for neighbors. Users are invited to try
511  // higher values to see what happens.
512  int order = 1;
513 
514  for (int pointIndex = 0; pointIndex < model.getNPoints(); ++pointIndex)
515  {
516  // for 2D models, pointIndex and vertexId will be equal, except if
517  // a polygon is used to down sample the set of active nodes.
518  // For a discussion of the difference between points and vertices,
519  // see the User's Manual.
520  int vertexId = model.getPointMap().getVertexIndex(pointIndex);
521 
522  // get the unit vector of the current vertex
523  double[] vertex = model.getGrid().getVertex(vertexId);
524 
525  // find the indices of the vertexes that are connected to the
526  // current vertex by a single triangle edge
527  HashSet<Integer> neighbors = model.getGrid().getVertexNeighbors(
528  tessId, level, vertexId, order);
529 
530  // only print information about a subset of the vertices
531  if (pointIndex % 100 == 0)
532  {
533  System.out
534  .println("-------------------------------------------------------- ");
535  System.out.printf("point %d, vertex %d, lat,lon %s:%n%n",
536  pointIndex, vertexId,
537  EarthShape.WGS84.getLatLonString(vertex, 3));
538 
539  System.out.println("neighbor neighbor distance azimuth");
540  System.out.println("vertexid pointid (deg) (deg)");
541  for (Integer neighbor : neighbors)
542  {
543  // neighbor is the vertexId of a model vertex that is
544  // a neighbor of the current vertex.
545  double[] neighborVertex = model.getGrid().getVertex(
546  neighbor);
547 
548  int neighborPoint = model.getPointMap().getPointIndex(
549  neighbor, 0, 0);
550 
551  System.out.printf("%8d %8d %8.2f %8.2f%n", neighbor,
552  neighborPoint, GeoTessUtils.angleDegrees(vertex,
553  neighborVertex), GeoTessUtils
554  .azimuthDegrees(vertex, neighborVertex,
555  Double.NaN));
556  }
557  System.out.println();
558  }
559 
560  }
561 
562  }
563 
564  /**
565  * Given a model and an array of attribute changes, apply the changes to the
566  * model.
567  *
568  * @param model
569  * @param attributeIndex
570  * @param attributeChanges
571  */
572  protected void applyAttributeChanges(GeoTessModel model,
573  int attributeIndex, float[] attributeChanges)
574  {
575  for (int pointIndex = 0; pointIndex < model.getNPoints(); ++pointIndex)
576  model.setValue(pointIndex, attributeIndex,
577  model.getValueFloat(pointIndex, attributeIndex)
578  + attributeChanges[pointIndex]);
579  }
580 
581  /**
582  * Build a new GeoTessModel with the same grid nodes as the input model.
583  * There will a single attribute value of type int assigned to each grid
584  * node. The name of the attribute is HIT_COUNT and it is unitless.
585  *
586  * @param inputModel
587  * @param rayPaths
588  * @return
589  * @throws GeoTessException
590  * @throws IOException
591  */
592  protected GeoTessModel hitCount(GeoTessModel inputModel,
593  ArrayList<double[][]> rayPaths) throws Exception
594  {
595  System.out
596  .println("************************************************************");
597  System.out.println("*");
598  System.out.println("* hitCount()");
599  System.out.println("*");
600  System.out
601  .println("************************************************************");
602  System.out.println();
603 
604  // Create a MetaData object in which we can specify information
605  // needed for model construction.
606  GeoTessMetaData metaData = new GeoTessMetaData();
607 
608  // Specify a description of the model.
609  metaData.setDescription("GeoTessModel of hit count for example program Tomography2D");
610 
611  // This model will have only one layer, named 'surface'.
612  metaData.setLayerNames("surface");
613 
614  // Specify one unitless attribute
615  metaData.setAttributes("HIT_COUNT", "count");
616 
617  // specify the DataType for the data.
618  metaData.setDataType(DataType.INT);
619 
620  // specify the name of the software that is going to generate
621  // the model. This gets stored in the model for future reference.
622  metaData.setModelSoftwareVersion(getClass().getCanonicalName()
623  + " hitCount()");
624 
625  // specify the date when the model was generated. This gets
626  // stored in the model for future reference.
627  metaData.setModelGenerationDate(new Date().toString());
628 
629  // call a GeoTessModel constructor to build the model. Use the same grid
630  // as the one used by the input model.
631  GeoTessModel hitCountModel = new GeoTessModel(inputModel.getGrid(),
632  metaData);
633 
634  // initialize the data value (hit count) of every node with int value zero.
635  for (int vertexId = 0; vertexId < hitCountModel.getNVertices(); ++vertexId)
636  hitCountModel.setProfile(vertexId, Data.getDataInt(0));
637 
638  // if the inputModel had its active nodes specified with a Polygon,
639  // then apply the same polygon to the hit count model.
640  hitCountModel.setActiveRegion(inputModel.getPointMap().getPolygon());
641 
642  // approximate point spacing to use to define the ray path
643  double pointSpacing = toRadians(0.1);
644 
645  // horizontal interpolation type; either LINEAR or NATURAL_NEIGHBOR
646  InterpolatorType interpType = InterpolatorType.LINEAR;
647 
648  // model has only one attribute with index 0.
649  int attributeIndex = 0;
650 
651  // weights will be a map from a model point index to the weight
652  // ascribed to that point index by the integration points along the ray.
653  // The sum of all the weights will equal the length of the ray path in km.
654  HashMap<Integer, Double> weights = new HashMap<Integer, Double>();
655 
656  // loop over the ray paths
657  for (int i = 0; i < rayPaths.size(); ++i)
658  {
659  // each ray path is comprised of two unit vectors, one for the event
660  // and one for the station.
661  double[][] rayPath = rayPaths.get(i);
662  double[] event = rayPath[0];
663  double[] station = rayPath[1];
664  GreatCircle greatCircle = new GreatCircle(event, station);
665 
666  // we want a set of weights for each ray path, so we need to clear
667  // the map in between calls to getPathIntegral().
668  weights.clear();
669 
670  // integrate points along the ray path. We don't care about the
671  // integral, we only want the weights assigned to each model point.
672  inputModel.getPathIntegral2D(-1, greatCircle,
673  pointSpacing, -1., interpType, weights);
674 
675  for (Integer pointIndex : weights.keySet())
676  if (pointIndex >= 0)
677  {
678  int hitcount = hitCountModel.getValueInt(pointIndex, attributeIndex);
679  ++hitcount;
680  hitCountModel.setValue(pointIndex, attributeIndex, hitcount);
681  // this could be done more compactly:
682  // model.setValue(pointIndex, attributeIndex, model.getValueInt(pointIndex, 0)+1);
683  }
684  }
685 
686  // hitCountModel has been populated with the hit count of every vertex.
687 
688  // plot maps of hit count and triangle size.
689  GeoTessModelUtils.vtk(hitCountModel, "hitcount_model_original.vtk", 0,
690  false, new int[] { 0 });
691  GeoTessModelUtils.vtkTriangleSize(hitCountModel.getGrid(), new File(
692  "triangle_size_original.vtk"), 0);
693 
694  // print information about the points that have hit count > 0
695  System.out.println(" point vertex lat lon distance hitcount");
696  for (int pointIndex = 0; pointIndex < hitCountModel.getNPoints(); ++pointIndex)
697  if (hitCountModel.getValueInt(pointIndex, 0) > 0)
698  {
699  double[] u = hitCountModel.getPointMap().getPointUnitVector(
700  pointIndex);
701 
702  System.out.printf(
703  "%8d %8d %s %9.2f %6d%n",
704  pointIndex,
705  hitCountModel.getPointMap().getVertexIndex(pointIndex),
706  hitCountModel.getPointMap().getPointLatLonString(
707  pointIndex, 3),
708  GeoTessUtils.angleDegrees(ANMO, u),
709  hitCountModel.getValueInt(pointIndex, 0));
710  }
711  System.out.println();
712 
713  return hitCountModel;
714  }
715 
716  /**
717  * At this point, we have a new GeoTessModel that has been refined to have
718  * higher resolution (more vertices) than the old model. But the new model has
719  * attribute value HIT_COUNT, not ATTENUATION. We need to make a
720  * new model using the refined grid from hitCountModelRefined but using data
721  * obtained from the old model. Where the old model has a vertex that is
722  * colocated with the vertex in the new model, the data from the old model is
723  * copied to the new model. For vertices in the new model that do not have
724  * colocated vertices in the old model, data will be interpolated from the
725  * data in the old model.
726  *
727  * @param oldModel
728  * @param hitCountModelRefined
729  * @return
730  * @throws GeoTessException
731  */
732  protected GeoTessModel refineModel(GeoTessModel oldModel,
733  GeoTessModel hitCountModelRefined) throws Exception
734  {
735  System.out
736  .println("************************************************************");
737  System.out.println("*");
738  System.out.println("* refineModel()");
739  System.out.println("*");
740  System.out
741  .println("************************************************************");
742  System.out.println();
743 
744  // get a reference to the refined grid in hitCountModelRefined
745  GeoTessGrid refinedGrid = hitCountModelRefined.getGrid();
746 
747  // plot maps of hit count and triangle size in the refined model.
748  GeoTessModelUtils.vtk(hitCountModelRefined,
749  "hitcount_model_refined.vtk", 0, false, new int[] { 0 });
750  GeoTessModelUtils.vtkTriangleSize(refinedGrid, new File(
751  "triangle_size_refined.vtk"), 0);
752 
753  // make a new model with the refined grid and a reference to the meta
754  // data from the old model.
755  GeoTessModel newModel = new GeoTessModel(
756  hitCountModelRefined.getGrid(), oldModel.getMetaData());
757 
758  // we will need to interpolate data from the old model at vertices in
759  // the new model that do not exist in the old model. For that purpose,
760  // we will need a GeoTessPosition object obtained from the old model.
761  GeoTessPosition pos = oldModel.getGeoTessPosition(InterpolatorType.LINEAR);
762 
763  // both old and new models are 2D models and hence have only a single layer.
764  int layerIndex = 0;
765 
766  // loop over every vertex in the new model and populate it with data.
767  for (int vtx = 0; vtx < newModel.getNVertices(); ++vtx)
768  {
769  // find the unit vector of the vertex from the new model.
770  // There may or may not be a vertex in the old model that is
771  // colocated with this unit vector.
772  double[] u = refinedGrid.getVertex(vtx);
773 
774  // request the index of the vertex in the old model that is
775  // colocated with the new vertex. If the old model has no colocated
776  // vertex, then oldVtx will be -1.
777  int oldVtx = oldModel.getGrid().getVertexIndex(u);
778 
779  Data data = null;
780  if (oldVtx < 0)
781  // interpolate a new Data object from values in the old model.
782  data = pos.set(layerIndex, u, 6371.).getData();
783  else
784  // retrieve a reference to the data object from the old model.
785  // Note that the new and old models share references to common
786  // Data objects. Changes made to attribute values in one model
787  // will also change the values in the other model.
788  data = oldModel.getProfile(oldVtx, layerIndex).getDataTop();
789 
790  // set the data in the new model.
791  newModel.setProfile(vtx, data);
792  }
793 
794  // print some statistics about model values in old and new models.
795  System.out.println(GeoTessModelUtils.statistics(oldModel));
796  System.out.println(GeoTessModelUtils.statistics(newModel));
797 
798  return newModel;
799  }
800 
801 }
gov.sandia.geotess.examples.Tomography2D
This application illustrates how to use features available in GeoTessJava to execute tomography on a ...
Definition: Tomography2D.java:63
gov.sandia.geotess.examples.Tomography2D.main
static void main(String[] args)
Main program that calls a bunch of methods that actually implement the tasks outlined above.
Definition: Tomography2D.java:74
gov.sandia.geotess.examples.Tomography2D.hitCount
GeoTessModel hitCount(GeoTessModel inputModel, ArrayList< double[][]> rayPaths)
Build a new GeoTessModel with the same grid nodes as the input model.
Definition: Tomography2D.java:592
gov.sandia.geotess.examples.Tomography2D.applyAttributeChanges
void applyAttributeChanges(GeoTessModel model, int attributeIndex, float[] attributeChanges)
Given a model and an array of attribute changes, apply the changes to the model.
Definition: Tomography2D.java:572
gov
gov.sandia.geotess.examples.Tomography2D.regularization
void regularization(GeoTessModel model)
Find the indices of the model 'points' that are the neighbors of each model point.
Definition: Tomography2D.java:491
gov.sandia.geotess.examples.Tomography2D.integrateRayPaths
void integrateRayPaths(GeoTessModel model, ArrayList< double[][]> rayPaths)
For every ray path, trace the ray through the model.
Definition: Tomography2D.java:366
gov.sandia.geotess
gov.sandia.geotess.examples.Tomography2D.refineModel
GeoTessModel refineModel(GeoTessModel oldModel, GeoTessModel hitCountModelRefined)
At this point, we have a new GeoTessModel that has been refined to have higher resolution (more verti...
Definition: Tomography2D.java:732
gov.sandia.geotess.examples.Tomography2D.ANMO
static double[] ANMO
Definition: Tomography2D.java:67
gov.sandia
gov.sandia.geotess.examples.Tomography2D.generateRayPaths
ArrayList< double[][]> generateRayPaths()
Generate 11 ray paths on the surface of the WGS84 ellipsoid.
Definition: Tomography2D.java:282
gov.sandia.geotess.examples.Tomography2D.startingModel
GeoTessModel startingModel()
Generate a starting model for the Tomography2D example program.
Definition: Tomography2D.java:199