OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimViewshedTool.cpp
Go to the documentation of this file.
1 //*****************************************************************************
2 //
3 // OSSIM Open Source Geospatial Data Processing Library
4 // See top level LICENSE.txt file for license information
5 //
6 //*****************************************************************************
7 
11 #include <ossim/base/ossimRtti.h>
16 #include <ossim/init/ossimInit.h>
29 #include <ossim/base/Thread.h>
30 
31 using namespace std;
32 
34  "Computes bitmap image representing the viewshed from specified location using only "
35  "DEM information.";
36 static const string FOV_KW = "fov";
37 static const string HEIGHT_OF_EYE_KW = "height_of_eye";
38 static const string HORIZON_FILE_KW = "horizon_file";
39 static const string OBSERVER_KW = "observer";
40 static const string VISIBILITY_RADIUS_KW = "visibility_radius";
41 static const string RETICLE_SIZE_KW = "reticle_size";
42 static const string VIEWSHED_CODING_KW = "viewshed_coding";
43 static const string AOI_SIZE_METERS_KW = "aoi_size_meters";
44 static const string THREADS_KW = "threads";
45 
46 
48 : m_obsHgtAbvTer (1.5),
49  m_visRadius (0.0),
50  m_radials (0),
51  m_obsInsideAoi (true),
52  m_displayAsRadar (false),
53  m_halfWindow (0),
54  m_outBuffer (NULL),
55  m_visibleValue (1),
56  m_hiddenValue (128),
57  m_overlayValue (255),
58  m_reticleSize(25),
59  m_simulation (false),
60  m_numThreads(1),
61  m_startFov(0),
62  m_stopFov(0),
63  m_threadBySector(false),
64  d_accumT(0)
65 {
67 }
68 
70 {
71  if (m_radials)
72  {
73  for (int i=0; i<4; ++i)
74  delete [] m_radials[i];
75  delete [] m_radials;
76  }
77 }
78 
80 {
81  // Set the general usage:
83  ossimString appName = ap.getApplicationName();
84  ossimString usageString = appName;
85  usageString += " viewshed [options] <obs_lat> <obs_lon> <output-image>";
86  au->setCommandLineUsage(usageString);
87 
88  // Set the command line options:
90  "--fov <start> <end>", "Optional arguments specifying the field-of"
91  "-view boundary azimuths (in degrees). By default, a 360 deg FOV is"
92  " computed. The arc is taken clockwise from start to end, so for a"
93  " FOV of 225 deg from W, through N to SE, start=270 and end=135");
95  "--height-of-eye <meters>", "Specifies the observers height-of-eye above the "
96  "terrain in meters. Defaults to 1.5 m.");
98  "--horizon <filename>", "Experimental. Outputs the max elevation angles "
99  "for all azimuths to <filename>, for horizon profiling.");
101  "--radius <meters>", "Specifies max visibility in meters. Required "
102  "unless --size is specified. This option constrains output to a circle, "
103  "similar to a radar display");
105  "--reticle <int>", "Specifies the size of the reticle at the observer"
106  "location in pixels from the center (i.e., the radius of the reticle). "
107  "Defaults to 2. A value of 0 hides the reticle. See --values option for "
108  "setting reticle color.");
110  "--simulation", "For engineering/debug purposes ");
112  "--tbs", "\"Thread By Sector\". For engineering/debug purposes ");
114  "--threads <n>", "Number of threads. Defaults to use all available cores. "
115  "For engineering/debug purposes ");
117  "--viewshed-coding <int int int>", "Specifies the pixel values (0-255) for the visible,"
118  " hidden and overlay pixels, respectively. Defaults to visible=null (0), "
119  "hidden=128, and overlay (observer position, reticle, and circumference) is "
120  "highlighted with 255.");
121 
122  // Base class has its own:
124 
125  ostringstream description;
126  description << DESCRIPTION << "\n\nExamples:\n\n"
127  " "<<appName<<" viewshed --radius 50 28.0 -80.5 output-hlz.tif\n"
128  "\nAn alternate command line provides switch for observer lat and lon:\n\n"
129  " "<<appName<<" viewshed --rlz 25 --observer 28.0 -80.5 output-hlz.tif \n";
130  au->setDescription(description.str());
131 }
132 
134 {
135  ostringstream xmsg;
136  xmsg<<"ossimViewshedUtil::initialize(ossimArgumentParser) -- ";
137 
138  int numArgsExpected = 4;
139 
140  // Base class first:
142  return false;
143  if (m_helpRequested)
144  return true;
145 
146  string ts1;
148  string ts2;
150  string ts3;
152 
153  if ( ap.read("--fov", sp1, sp2) )
154  {
155  double startFov = ossimString(ts1).toDouble();
156  if (startFov < 0)
157  startFov += 360.0;
158  ostringstream value;
159  value<<startFov<<" "<<ts2;
160  m_kwl.addPair( FOV_KW, value.str() );
161  }
162 
163  if ( ap.read("--hgt-of-eye", sp1) || ap.read("--height-of-eye", sp1) )
164  m_kwl.addPair( HEIGHT_OF_EYE_KW, ts1 );
165 
166  if ( ap.read("--horizon", sp1) || ap.read("--horizon-file", sp1))
167  m_kwl.addPair( HORIZON_FILE_KW, ts1 );
168 
169  if ( ap.read("--observer", sp1, sp2) )
170  {
171  ostringstream value;
172  value<<ts1<<" "<<ts2;
173  m_kwl.addPair( OBSERVER_KW, value.str() );
174  numArgsExpected -= 2;
175  }
176 
177  if ( ap.read("--radius", sp1) )
178  m_kwl.addPair( VISIBILITY_RADIUS_KW, ts1 );
179 
180  if ( ap.read("--reticle", sp1) )
181  m_kwl.addPair( RETICLE_SIZE_KW, ts1 );
182 
183  if ( ap.read("--values", sp1, sp2, sp3) || ap.read("--viewshed-coding", sp1, sp2, sp3))
184  {
185  ostringstream value;
186  value<<ts1<<" "<<ts2<<" "<<ts3;
187  m_kwl.addPair( VIEWSHED_CODING_KW, value.str() );
188  }
189 
190  if ( ap.read("--threads", sp1) )
191  {
192  m_kwl.addPair( THREADS_KW, ts1 );
193  }
194 
195  // The remaining options are available only via command line (i.e., no KWL entries defined)
196  if ( ap.read("--tbs") )
197  m_threadBySector = true;
198 
199  if ( ap.read("--simulation") )
200  m_simulation = true;
201 
202  // Moved to: ossimViewshedTool::initialize(const ossimKeywordlist& kwl)
203  // if ( ap.read("--threads", sp1) )
204  // m_numThreads = ossimString(ts1).toUInt32();
205 
206  if ( ap.argc() < numArgsExpected )
207  {
208  xmsg<<"Expecting more arguments.";
209  throw(ossimException(xmsg.str()));
210  }
211  else
212  {
213  ossimString latstr = ap[1];
214  ossimString lonstr = ap[2];
215  ostringstream value;
216  value<<latstr<<" "<<lonstr;
217  m_kwl.addPair( OBSERVER_KW, value.str() );
218  ap.remove(1,2);
220  }
221 
222  return true;
223 }
224 
226 {
227  ossimString value;
228 
229  value = kwl.findKey(THREADS_KW);
230  {
231  if ( value.size() )
232  {
233  m_numThreads = ossimString(value).toUInt32();
234  }
235  }
236 
237  value = kwl.findKey(FOV_KW);
238  if (!value.empty())
239  {
240  vector <ossimString> coordstr;
241  value.split(coordstr, ossimString(" ,"), false);
242  if (coordstr.size() == 2)
243  {
244  m_startFov = coordstr[0].toDouble();
245  m_stopFov = coordstr[1].toDouble();
246  if (m_startFov < 0)
247  m_startFov += 360.0;
248  }
249  }
250 
251  value = kwl.findKey(HEIGHT_OF_EYE_KW);
252  if (!value.empty())
253  m_obsHgtAbvTer = value.toDouble();
254 
255  m_horizonFile = kwl.findKey(HORIZON_FILE_KW);
256 
257  value = kwl.findKey(OBSERVER_KW);
258  if (!value.empty())
259  {
260  vector <ossimString> coordstr;
261  value.split(coordstr, ossimString(" ,"), false);
262  if (coordstr.size() == 2)
263  {
264  m_observerGpt.lat = coordstr[0].toDouble();
265  m_observerGpt.lon = coordstr[1].toDouble();
266  m_observerGpt.hgt = 0.0;
267  }
268  }
269 
270  value = kwl.findKey(RETICLE_SIZE_KW);
271  if (!value.empty())
272  m_reticleSize = value.toInt32();
273 
274  value = kwl.findKey(VIEWSHED_CODING_KW);
275  if (!value.empty())
276  {
277  vector <ossimString> coordstr;
278  value.split(coordstr, ossimString(" ,"), false);
279  if (coordstr.size() == 3)
280  {
281  m_visibleValue = coordstr[0].toUInt8();
282  m_hiddenValue = coordstr[1].toUInt8();
283  m_overlayValue = coordstr[2].toUInt8();
284  }
285  }
286 
287  value = kwl.findKey(VISIBILITY_RADIUS_KW);
288  if (!value.empty())
289  {
290  m_visRadius = value.toDouble();
291  m_displayAsRadar = true;
292  }
293 
294  // If running simulation, clear out all pre-loaded elevation databases:
295  if (m_simulation)
296  {
299  }
300 
301  // Base class does most work:
303 }
304 
306 {
308  m_visRadius = 0;
309  m_outBuffer = 0;
310  m_horizonMap.clear();
311  m_jobMtQueue = 0;
313 }
314 
316 {
317  // First try normal base class initialization. If that doesn't work, then probably no DEM
318  // or input images were provided, so need to use elev manager resoltion at observer point.
319 
321  if (!m_gsd.hasNans() || m_observerGpt.hasNans())
322  return;
323 
324  ossimMapProjection* proj = dynamic_cast<ossimMapProjection*>(m_geom->getProjection());
325  if (!proj)
326  return;
327 
331  if (m_geoScaled)
332  proj->setOrigin(m_observerGpt);
333  proj->setMetersPerPixel(m_gsd);
334 }
335 
337 {
339  if (!m_aoiGroundRect.hasNans())
340  {
341  // AOI established by base class, nothing to do except take this opportunity to set the
342  // observer location to AOI center if not already defined:
343  if (m_observerGpt.hasNans())
345  return;
346  }
347 
348  // Not enough info available to base class to determine AOI, maybe can determine from observer
349  // position and radius:
350  if ((m_visRadius == 0) && m_kwl.hasKey(AOI_SIZE_METERS_KW))
351  {
352  ossimString lookup = m_kwl.findKey( AOI_SIZE_METERS_KW );
353  lookup.trim();
354  m_visRadius = 0.5*(lookup.before(" ").toDouble() + lookup.after(" ").toDouble());
355  m_displayAsRadar = true;
356  }
357  if (m_observerGpt.hasNans())
359 
360  if ((m_visRadius != 0) && !m_observerGpt.hasNans())
361  {
362  ossimMapProjection* proj = dynamic_cast<ossimMapProjection*>(m_geom->getProjection());
363  if (!proj)
364  return;
365 
366  ossimDpt metersPerDegree (m_observerGpt.metersPerDegree());
367  double dlat = m_visRadius/metersPerDegree.y;
368  double dlon = m_visRadius/metersPerDegree.x;
369  ossimGpt ulg (m_observerGpt.lat + dlat, m_observerGpt.lon - dlon);
370  ossimGpt lrg (m_observerGpt.lat - dlat, m_observerGpt.lon + dlon);
371 
372  m_aoiGroundRect = ossimGrect(ulg, lrg);
373  proj->setUlTiePoints(ulg);
374 
376  }
377 }
378 
380 {
381  ostringstream xmsg;
382 
383  if (m_observerGpt.hasNans())
384  {
385  xmsg<<"ossimViewshedUtil:"<<__LINE__<<" Observer ground position has not been set."<<ends;
386  throw ossimException(xmsg.str());
387  }
388 
390 
391  // Initialize the height of eye component of observer position:
395 
397  dynamic_cast<ossimMapProjection*>(m_geom->getProjection());
398 
399  // If no radius specified, need to compute R large enough to cover the requested AOI:
400  if (m_visRadius == 0)
401  computeRadius();
402  if (m_halfWindow == 0)
403  m_halfWindow = ossim::round<ossim_int32, double>(m_visRadius/m_gsd.x);
405 
406  // If no AOI defined, just use the visibility rectangle:
408  if (m_aoiViewRect.hasNans())
409  {
410  m_aoiViewRect = visRect;
413  }
414 
415  // Allocate the output image buffer:
416  ossimIrect bufViewRect = visRect.clipToRect(m_aoiViewRect);
417  if (bufViewRect.area() == 0)
418  {
419  xmsg<<"ossimViewshedUtil:"<<__LINE__<<" The requested AOI rect is outside the visibility range." << ends;
420  throw ossimException(xmsg.str());
421  }
423  create(0, OSSIM_UINT8, 1, m_aoiViewRect.width(), m_aoiViewRect.height());
424  if(!m_outBuffer.valid())
425  {
426  xmsg<<"ossimViewshedUtil:"<<__LINE__<<" Output buffer allocation failed." << ends;
427  throw ossimException(xmsg.str());
428  }
430 
431  // The processing chain for this class is simply a memory source containing the output buffer:
435 
436  // If input image(s) provided, need to combine them with the product:
437  if (m_imgLayers.empty())
438  {
440  }
441  else
442  {
444  combiner->connectMyInputTo(m_memSource.get());
445  m_procChain->add(combiner.get());
446  }
447 }
448 
450 {
451  cerr<<"ossimViewshedUtil:"<<__LINE__<<endl;//TODO:remove debug
452  ostringstream xmsg;
453  if (!m_geom.valid())
454  return 0;
455 
456  cerr<<"ossimViewshedUtil:"<<__LINE__<<endl;//TODO:remove debug
457  m_aoiViewRect = bounding_irect;
460 
461  cerr<<"ossimViewshedUtil:"<<__LINE__<<endl;//TODO:remove debug
462  if (computeViewshed())
463  {
464  // The memory source has been populated, now do the getTile on the full chain to pick up
465  // other filters inserted after the memsource:
466  return m_procChain->getTile( m_aoiViewRect, 0 );
467  }
468  // else:
469  return 0;
470 }
471 
473 {
474  if (m_helpRequested)
475  return true;
476 
477  if (!computeViewshed())
478  return false;
479 
481  ossimNotify(ossimNotifyLevel_INFO) << "Wrote horizon profile to <"<<m_horizonFile<<">" <<endl;
482 
484 }
485 
487 {
488  // Allocate the output image buffer:
491 
492  ostringstream xmsg;
493  if (!m_outBuffer.valid() || !m_memSource.valid())
494  {
495  xmsg<<"ossimViewshedUtil:"<<__LINE__<<" Error encountered allocating output image buffer.";
496  throw ossimException(xmsg.str());
497  }
498 
499  // Initialize the image with all points hidden:
504 
505  // Initialize the radials after intersecting the requested FOV with the FOV required to see the
506  // full AOI (not applicable if observer inside AOI). Skip radial init if no intersection found:
507  if (!optimizeFOV())
508  return false;
509  initRadials();
510 
511  // The viewshed process necessarily first fills the output buffer with the complete result before
512  // the writer requests a tile. Control is passed later to the base class execute() for writing.
513  d_accumT = 0;
514 
515  if (m_numThreads == 0)
517 
518  if (m_numThreads > 1)
519  {
520  std::shared_ptr<ossimJobQueue> jobQueue = std::make_shared<ossimJobQueue>();
521  for (int sector=0; sector<8; ++sector)
522  {
523  if (m_radials[sector] == 0)
524  continue;
525 
526  if (m_threadBySector)
527  {
528  std::shared_ptr<SectorProcessorJob> job = std::make_shared<SectorProcessorJob>(this, sector, m_halfWindow);
529  jobQueue->add(job, false);
530  }
531  else
532  {
533  for (ossim_uint32 r=0; r<=m_halfWindow; ++r)
534  {
535  std::shared_ptr<RadialProcessorJob> job = std::make_shared<RadialProcessorJob>(this, sector, r, m_halfWindow);
536  jobQueue->add(job, false);
537  }
538  }
539  if (needsAborting())
540  return 0;
541  }
542 
543  ossimNotify(ossimNotifyLevel_INFO) << "\nSubmitting "<<jobQueue->size()<<" jobs..."<<endl;
544  m_jobMtQueue = std::make_shared<ossimJobMultiThreadQueue>(jobQueue, m_numThreads);
545 
546  // Wait until all radials have been processed before proceeding:
547  ossimNotify(ossimNotifyLevel_INFO) << "Waiting for job threads to finish..."<<endl;
548  while (m_jobMtQueue->hasJobsToProcess() || m_jobMtQueue->numberOfBusyThreads())
550  }
551  else
552  {
553  // Unthreaded processing:
554  ossimNotify(ossimNotifyLevel_INFO) << "\nProcessing radials (non-threaded)..."<<endl;
555 
556  // Loop over pixels in layer for each sector:
557  for (int sector=0; sector<8; ++sector)
558  {
559  if (m_radials[sector] == 0)
560  continue;
561 
562  std::shared_ptr<SectorProcessorJob> spj = std::make_shared<SectorProcessorJob>(this, sector, m_halfWindow);
563  spj->start();
564 
565  if (needsAborting())
566  return false;
567 
568  } // end loop over sectors
569  }
570 
571  ossimNotify(ossimNotifyLevel_INFO) << "Finished processing radials."<<endl;
572  paintReticle();
573 
574  return true;
575 }
576 
578 {
579  bool intersects = false;
580 
581  // If the observer position lies outside of the requested AOI, we can reduce the search arc:
583  return true;
584 
585  // Determine cardinal region (N, NE, E, ...) of observer relative to AOI:
586  enum CardinalDirections { N=1, S=2, E=4, W=8, NE=5, NW=9, SE=6, SW=10 };
587  int direction = 0;
589  direction = (int) N;
590  else if (m_observerGpt.lat < m_aoiGroundRect.ll().lat)
591  direction = (int) S;
593  direction += (int) W;
594  else if (m_observerGpt.lon > m_aoiGroundRect.ur().lon)
595  direction += (int) E;
596 
597  // Calculate start and stop FOV depending on region:
598  double start, stop;
599  switch ((CardinalDirections) direction)
600  {
601  case N:
604  break;
605  case NE:
608  break;
609  case E:
612  break;
613  case SE:
616  break;
617  case S:
620  break;
621  case SW:
624  break;
625  case W:
628  break;
629  case NW:
630  default:
633  break;
634  }
635 
636  // Now need to intersect this arc with the requested FOV:
637  if (m_startFov == m_stopFov)
638  {
639  // There was no requested FOV (i.e, FOV = 360). So use the optimized FOV straight away:
640  m_startFov = start;
641  m_stopFov = stop;
642  intersects = true;
643  }
644  else
645  {
646  // Pick m_startFov as reference, and make sure all others are greater:
647  double a1 = m_stopFov;
648  double a2 = start;
649  double a3 = stop;
650  if (m_startFov > m_stopFov)
651  a1 += 360;
652  if (m_startFov > start)
653  a2 += 360;
654  if (m_startFov > stop)
655  a3 += 360;
656 
657  // Map to sort remaining azimuths by increasing angle clockwise:
658  map<double, int> angle_map;
659  angle_map.insert(pair<double, int>(a1, 1));
660  angle_map.insert(pair<double, int>(a2, 2));
661  angle_map.insert(pair<double, int>(a3, 3));
662 
663  map<double, int>::iterator iter = angle_map.begin();
664  if (iter->second == 1)
665  {
666  ++iter;
667  if (iter->second == 3)
668  intersects = true;
669  }
670  else if (iter->second == 2)
671  {
672  m_startFov = start;
673  intersects = true;
674  ++iter;
675  if (iter->second == 3)
676  m_stopFov = stop;
677  }
678  else
679  {
680  intersects = true;
681  m_stopFov = stop;
682  }
683  }
684 
685  if (!intersects)
686  {
688  "ossimViewshedUtil::optimizeFOV() -- No FOV intersection found. Nothing to do."<<endl;
689  }
690  else
691  {
692  ossimNotify(ossimNotifyLevel_INFO)<<"ossimViewshedUtil::optimizeFOV() -- "
693  "The start and stop FOV azimuths have been optimized to "<<m_startFov<<" -> "
694  <<m_stopFov<<" deg."<<endl;
695  }
696 
697  return intersects;
698 }
699 
701 {
702  ostringstream xmsg ("ossimViewshedUtil::computeRadius() -- ");
703 
704  // AOI is required for computing R
705  if (m_aoiViewRect.hasNans())
706  {
707  xmsg<<"AOI undefined. Cannot compute visibility radius." << ends;
708  throw ossimException(xmsg.str());
709  }
710 
711  // Compute distance from observer to farthest corner of AOI. This is the radius
714  if (d > m_visRadius)
715  m_visRadius = d;
717  if (d > m_visRadius)
718  m_visRadius = d;
720  if (d > m_visRadius)
721  m_visRadius = d;
722 }
723 
725 {
726  // All eaight sectors' radials have the same azimuths except that the abscissa and ordinate are
727  // reversed between the N-S and E-W sectors, i.e., the N and S sectors use the y-axis as the
728  // abscissa (u) and the x-axis is the ordinate (v). The azimuth (dv/du) is therefore DX/DY for
729  // the north and south, while the azimuth is DY/DX for the east and west sectors. Nevertheless,
730  // each sectors radials must be maintained separately as they contain the max elevation angle.
731 
732  // First determine which sectors are involved given the desired FOV:
733  bool* sectorInFov = new bool[8];
734  memset(sectorInFov, false, 8);
735  bool crossed_north = true;
736  if (m_stopFov <= m_startFov) // Crosses 0 azimuth
737  crossed_north = false;
738  double azimuth = m_startFov;
739  for (int i=0; (i < 8) && ((azimuth < m_stopFov) || !crossed_north); ++i)
740  {
741  if ((azimuth >= 0) && (azimuth < 45.0))
742  sectorInFov[0] = true;
743  else if (azimuth < 90.0)
744  sectorInFov[1] = true;
745  else if (azimuth < 135.0)
746  sectorInFov[2] = true;
747  else if (azimuth < 180.0)
748  sectorInFov[3] = true;
749  else if (azimuth < 225.0)
750  sectorInFov[4] = true;
751  else if (azimuth < 270.0)
752  sectorInFov[5] = true;
753  else if (azimuth < 315.0)
754  sectorInFov[6] = true;
755  else if (azimuth < 360.0)
756  sectorInFov[7] = true;
757 
758  azimuth += 45.0;
759  if (azimuth > 360.0)
760  {
761  azimuth -= 360.0;
762  crossed_north = true;
763  }
764  }
765 
766  // Compute the azimuth slopes for each radial in the sector.
767  m_radials = new Radial* [8];
768  double du = m_halfWindow;
769  for (int sector=0; sector<8; ++sector)
770  {
771  if (!sectorInFov[sector])
772  {
773  m_radials[sector] = 0;
774  continue;
775  }
776 
777  ossim_uint32 ridx = 0;
778  m_radials[sector] = new Radial [m_halfWindow+1];
779  for (ossim_int32 dv = 0; dv <= (ossim_int32) m_halfWindow; ++dv)
780  {
781  if (sector & 1) // odd-numbered sector, azimuths computed in reverse order
782  m_radials[sector][m_halfWindow-ridx].azimuth = ((double)dv)/du;
783  else
784  m_radials[sector][ridx].azimuth = ((double)dv)/du;
785  ++ridx;
786  }
787  }
788 
789  // Cleanup:
790  delete [] sectorInFov;
791  sectorInFov = 0;
792 }
793 
795 {
796  if (m_reticleSize == 0)
797  return;
798 
799  // Highlight the observer position with X reticle:
800  ossimDpt obsViewPt;
801  m_geom->worldToLocal(m_observerGpt, obsViewPt);
802  if (m_aoiViewRect.pointWithin(ossimIpt(obsViewPt)))
803  {
804  for (int i=-m_reticleSize; i<=m_reticleSize; ++i)
805  {
810  }
811  }
812 
813  // Paint boundary rectangle if no visibility radius painted:
814  if (!m_displayAsRadar)
815  {
816  for (int y=m_aoiViewRect.ul().y; y<=m_aoiViewRect.lr().y; y++)
817  {
820  }
821  for (int x=m_aoiViewRect.ul().x; x<=m_aoiViewRect.lr().x; x++)
822  {
825  }
826  }
827 }
828 
830 {
831  // Store the max elevation angles for horizon profiling:
832  double az_deg, arctan;
833  for (ossim_uint32 sector=0; sector<8; ++sector)
834  {
835  if (m_radials[sector] == 0)
836  continue;
837 
838  for (ossim_uint32 radial = 0; radial <= m_halfWindow; ++radial)
839  {
840  arctan = ossim::atand(m_radials[sector][radial].azimuth);
841  switch (sector)
842  {
843  case 0: // 0 - 45
844  az_deg = arctan;
845  break;
846  case 1: // 45 - 90
847  az_deg = 90 - arctan;
848  break;
849  case 2: // 90 - 135
850  az_deg = 90 + arctan;
851  break;
852  case 3: // 135 - 180
853  az_deg = 180 - arctan;
854  break;
855  case 4: // 180 - 225
856  az_deg = 180 + arctan;
857  break;
858  case 5: // 225 - 270
859  az_deg = 270 - arctan;
860  break;
861  case 6: // 270 - 315
862  az_deg = 270 + arctan;
863  break;
864  case 7: // 315 - 360
865  az_deg = 360 - arctan;
866  break;
867  default:
868  break;
869  }
870 
871  m_horizonMap.insert(pair<double, double>(az_deg, m_radials[sector][radial].elevation));
872  }
873  }
874 
875  // Open output file and write the map:
876  ofstream fstr (m_horizonFile.chars());
877  if (!fstr.is_open())
878  return false;
879  map<double, double>::iterator iter = m_horizonMap.begin();
880  while (iter != m_horizonMap.end())
881  {
882  fstr << iter->first << ", " << iter->second << endl;
883  ++iter;
884  }
885 
886  fstr.close();
887  return true;
888 }
889 
891 {
892  // Loop over all the sector's radials and walk over each one.
893  for (ossim_uint32 r=0; r<=m_numRadials; ++r)
894  {
896  }
897 }
898 
900 {
902 }
903 
904 std::mutex RadialProcessor::m_bufMutex;
905 
907  ossim_uint32 sector_idx,
908  ossim_uint32 radial_idx)
909 {
910  double u, v;
911  ossimDpt pt_i, vpt_i;
912  ossimGpt gpt_i;
913  double elev_i, elev;
914  double r2_max = vsUtil->m_halfWindow*vsUtil->m_halfWindow;
915 
916  // Establish shorthand access to radial:
917  ossimViewshedTool::Radial& radial = vsUtil->m_radials[sector_idx][radial_idx];
918 
919  // Walk along the radial using the appropriate coordinate abscissa for that sector and
920  // compute ordinate using the radials azimuth:
921  for (u=1.0; u <= (double) vsUtil->m_halfWindow; u += 1.0)
922  {
923  // Compute ordinate from abscissa and slope of this radial:
924  v = radial.azimuth*(u);
925  switch (sector_idx)
926  {
927  case 0: // N-NE, (u, v) = (-y, x)
928  pt_i.y = -u;
929  pt_i.x = v;
930  break;
931  case 1: // NE-E, (u, v) = (x, -y)
932  pt_i.x = u;
933  pt_i.y = -v;
934  break;
935  case 2: // E-SE, (u, v) = (x, y)
936  pt_i.x = u;
937  pt_i.y = v;
938  break;
939  case 3: // SE-S, (u, v) = (y, x)
940  pt_i.y = u;
941  pt_i.x = v;
942  break;
943  case 4: // S-SW, (u, v) = (y, -x)
944  pt_i.y = u;
945  pt_i.x = -v;
946  break;
947  case 5: // SW-W, (u, v) = (-x, y)
948  pt_i.x = -u;
949  pt_i.y = v;
950  break;
951  case 6: // W-NW, (u, v) = (-x, -y)
952  pt_i.x = -u;
953  pt_i.y = -v;
954  break;
955  case 7: // NW-N, (u, v) = (-y, -x)
956  pt_i.y = -u;
957  pt_i.x = -v;
958  break;
959  default:
960  break;
961  }
962 
963  // Shift to actual view coordinates:
964  vpt_i = pt_i + vsUtil->m_observerVpt;
965  ossimIpt ipt (vpt_i);
966 
967  // Check if alread accounted for at this location:
968  //if (!vsUtil->m_outBuffer->isNull(vpt_i))
969  // continue;
970 
971  // Check if we are exiting the AOI (no more processing required for this radial):
972  bool pointInsideAoi = vsUtil->m_aoiViewRect.pointWithin(ipt);
973  if (radial.insideAoi && !pointInsideAoi)
974  break;
975 
976  // Alternatively, check if we were OUTSIDE and now moving INSIDE:
977  if (!radial.insideAoi && pointInsideAoi)
978  radial.insideAoi = true;
979 
980  // Check if we passed beyong the visibilty radius, and exit loop if so:
981  if (vsUtil->m_displayAsRadar && ((u*u + v*v) >= r2_max))
982  {
983  vsUtil->m_outBuffer->setValue(ipt.x, ipt.y, vsUtil->m_overlayValue);
984  break;
985  }
986 
987  // Fetch the pixel value as the elevation value and compute elevation angle from
988  // the observer pt as dz/dx
989  vsUtil->m_geom->localToWorld(vpt_i, gpt_i);
990  if (vsUtil->m_simulation && ossim::isnan(gpt_i.hgt))
991  gpt_i.hgt = vsUtil->m_observerGpt.hgt-vsUtil->m_obsHgtAbvTer; // ground level
992 
993  if (!gpt_i.hasNans())
994  {
995  // Compare elev angle to max angle latched so far along this radial:
996  elev_i = (gpt_i.hgt - vsUtil->m_observerGpt.hgt) / u;
997  elev = radial.elevation;
998  if (elev_i > elev)
999  {
1000  // point is visible, latch this line-of-sight as the new max elevation angle for this
1001  // radial, and mark the output pixel as visible:
1002  radial.elevation = elev_i;
1003  vsUtil->m_outBuffer->setValue(ipt.x, ipt.y, vsUtil->m_visibleValue);
1004  }
1005  else
1006  {
1007  vsUtil->m_outBuffer->setValue(ipt.x, ipt.y, vsUtil->m_hiddenValue);
1008  }
1009  }
1010  } // end loop over radial's abscissas
1011 }
1012 
1014 {
1015  m_aoiGroundRect = ossimGrect(1.0, 0.0, 0.0, 1.0);
1016 
1017  m_observerGpt = ossimGpt(1.5, 0.5);
1018  m_startFov = 180;
1019  m_stopFov = 270;
1020  cout<<"Before: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<endl;
1021  optimizeFOV();
1022  cout<<"After: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<"\n"<<endl;
1023 
1024  m_observerGpt = ossimGpt(1.5, 0.5);
1025  m_startFov = 335;
1026  m_stopFov = 180;
1027  cout<<"Before: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<endl;
1028  optimizeFOV();
1029  cout<<"After: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<"\n"<<endl;
1030 
1031  m_observerGpt = ossimGpt(1.5, 0.5);
1032  m_startFov = 270;
1033  m_stopFov = 0;
1034  cout<<"Before: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<endl;
1035  optimizeFOV();
1036  cout<<"After: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<"\n"<<endl;
1037 
1038  m_observerGpt = ossimGpt(-0.5, 0.5);
1039  m_startFov = 270;
1040  m_stopFov = 10;
1041  cout<<"Before: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<endl;
1042  optimizeFOV();
1043  cout<<"After: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<"\n"<<endl;
1044 
1045  m_observerGpt = ossimGpt(-0.5, 0.5);
1046  m_startFov = 350;
1047  m_stopFov = 90;
1048  cout<<"Before: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<endl;
1049  optimizeFOV();
1050  cout<<"After: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<"\n"<<endl;
1051 
1052  m_observerGpt = ossimGpt(-0.5, 0.5);
1053  m_startFov = 10;
1054  m_stopFov = 20;
1055  cout<<"Before: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<endl;
1056  optimizeFOV();
1057  cout<<"After: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<"\n"<<endl;
1058 
1059  m_observerGpt = ossimGpt(-0.5, 0.5);
1060  m_startFov = 270;
1061  m_stopFov = 90;
1062  cout<<"Before: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<endl;
1063  optimizeFOV();
1064  cout<<"After: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<"\n"<<endl;
1065 
1066  m_observerGpt = ossimGpt(-0.5, 0.5);
1067  m_startFov = 90;
1068  m_stopFov = 270;
1069  cout<<"Before: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<endl;
1070  optimizeFOV();
1071  cout<<"After: m_startFov="<<m_startFov<<" m_stopFov="<<m_stopFov<<"\n"<<endl;
1072 }
virtual double getMeanSpacingMeters() const
Returns the mean post spacing (in meters) for the highest resolution DEM in the list or NaN if no DEM...
std::shared_ptr< ossimJobMultiThreadQueue > m_jobMtQueue
ossim_uint32 x
virtual bool initialize(ossimArgumentParser &ap)
Initial method to be ran prior to execute.
void fill(ossim_uint32 band, ossim_float64 value)
will fill the entire band with the value.
std::string getApplicationName() const
return the application name, as specified by argv[0]
void addCommandLineOption(const ossimString &option, const ossimString &explanation)
ossimRefPtr< ossimImageData > getChip()
Get chip method that assumes pre-initialized state.
virtual void initializeProjectionGsd()
Initializes the projection gsd.
double azimuthTo(const ossimGpt &arg_gpt) const
METHOD: azimuthTo(ossimGpt) Computes the great-circle starting azimuth (i.e., at this gpt) to the arg...
Definition: ossimGpt.cpp:446
ossimString before(const ossimString &str, std::string::size_type pos=0) const
METHOD: before(str, pos) Returns string beginning at pos and ending one before the token str If strin...
void computeAdjustedViewFromGrect()
Initializes m_aoiViewRect given m_aoiGroundRect.
virtual void setValue(ossim_int32 x, ossim_int32 y, ossim_float64 color)
virtual ossimRefPtr< ossimImageData > getTile(const ossimIrect &tileRect, ossim_uint32 resLevel=0)
Within the image chain will pass the head of the list.
ossimRefPtr< ossimImageGeometry > m_geom
std::basic_ostringstream< char > ostringstream
Class for char output memory streams.
Definition: ossimIosFwd.h:35
virtual void setImageRectangle(const ossimIrect &rect)
ossimGrect m_aoiGroundRect
Represents serializable keyword/value map.
const std::string & findKey(const std::string &key) const
Find methods that take std::string(s).
bool m_helpRequested
Definition: ossimTool.h:150
ossim_uint32 y
bool valid() const
Definition: ossimRefPtr.h:75
void setUseGeoidIfNullFlag(bool flag)
bool read(const std::string &str)
search for an occurance of a string in the argument list, on sucess remove that occurance from the li...
double y
Definition: ossimDpt.h:165
virtual void setOrigin(const ossimGpt &origin)
Sets theOrigin to origin.
ossim_uint32 height() const
Definition: ossimIrect.h:487
bool hasKey(const std::string &key) const
Checks for key in map.
virtual void run()
Abstract method and must be overriden by the base class.
ossim_uint8 m_overlayValue
void makeNan()
Definition: ossimGpt.h:130
ossim_uint8 m_visibleValue
void setImage(ossimRefPtr< ossimImageData > image)
ossim_uint8 m_hiddenValue
ossimKeywordlist m_kwl
Definition: ossimTool.h:148
ossimRefPtr< ossimImageSource > combineLayers(std::vector< ossimRefPtr< ossimSingleImageChain > > &layers) const
When multiple input sources are present, this method instantiates a combiner and adds inputs...
const ossimIpt & ul() const
Definition: ossimIrect.h:274
void split(std::vector< ossimString > &result, const ossimString &separatorList, bool skipBlankFields=false) const
Splits this string into a vector of strings (fields) using the delimiter list specified.
void addPair(const std::string &key, const std::string &value, bool overwrite=true)
ossim_float64 hgt
Height in meters above the ellipsiod.
Definition: ossimGpt.h:274
virtual double getNullPixelValue(ossim_uint32 band=0) const
Each band has a null pixel associated with it.
ossim_uint32 toUInt32() const
static ossimElevManager * instance()
METHOD: instance() Implements singelton pattern.
void setCommandLineUsage(const ossimString &explanation)
virtual void initializeProjectionGsd()
Initializes the projection gsd.
ossimRefPtr< ossimMemoryImageSource > m_memSource
ossimApplicationUsage * getApplicationUsage()
void setImageSize(const ossimIpt &size)
double distanceTo(const ossimGpt &arg_gpt) const
METHOD: distanceTo(ossimGpt) Computes straight-line distance in meters between this and arg gpt: ...
Definition: ossimGpt.cpp:431
static void sleepInMicroSeconds(ossim_uint64 micros)
Utility method to allow one to sleep in microseconds.
Definition: Thread.cpp:83
ossimIpt size() const
Definition: ossimIrect.h:510
virtual void initialize()
Initialize the data buffer.
bool hasNans() const
Definition: ossimGrect.h:298
virtual void clear()
Disconnects and clears the DEM and image layers.
virtual void setMetersPerPixel(const ossimDpt &gsd)
ossimRefPtr< ossimImageChain > m_procChain
OSSIM_DLL ossim_uint32 getNumberOfThreads()
Get the number threads to use from ossimPreferences or ossim::Thread.
ossim_int32 toInt32() const
virtual void initializeAOI()
Initializes m_aoiViewRect with the output area of interest as specified in master KWL...
virtual void setUsage(ossimArgumentParser &ap)
Initializes the aurgument parser with expected parameters and options.
virtual void setUsage(ossimArgumentParser &ap)
Initializes the aurgument parser with expected parameters and options.
bool pointWithin(const ossimGpt &gpt, bool considerHgt=false) const
METHOD: pointWithin(ossimGpt)
Definition: ossimGrect.h:232
static ossimImageDataFactory * instance()
virtual void clear()
Disconnects and clears the dem and image layers.
yy_size_t size
ossim_float64 lon
Definition: ossimGpt.h:266
bool localToWorld(const ossimDpt &local_pt, ossimGpt &world_pt) const
Exposes the 3D projection from image to world coordinates.
std::string::size_type size() const
Definition: ossimString.h:405
virtual double getHeightAboveEllipsoid(const ossimGpt &gpt)
unsigned int ossim_uint32
void processRemainingArgs(ossimArgumentParser &ap)
Intended to be called after derived class has picked off its own options from the parser...
virtual void run()
Abstract method and must be overriden by the base class.
ossimViewshedTool * m_vsUtil
const char * chars() const
For backward compatibility.
Definition: ossimString.h:77
ossimString trim(const ossimString &valueToTrim=ossimString(" \\)) const
this will strip lead and trailing character passed in.
ossimViewshedTool * m_vsUtil
ossim_int32 m_reticleSize
double toDouble() const
std::vector< ossimRefPtr< ossimSingleImageChain > > m_imgLayers
virtual bool execute()
Writes product to output file.
ossimRefPtr< ossimImageData > m_outBuffer
const ossimGpt & ul() const
Definition: ossimGrect.h:252
virtual void setImageGeometry(ossimImageGeometry *geom)
Default implementation sets geometry of the first input to the geometry specified.
static void doRadial(ossimViewshedTool *vs, ossim_uint32 s, ossim_uint32 r)
const ossimIpt & lr() const
Definition: ossimIrect.h:276
virtual bool add(ossimConnectableObject *source)
Will return true or false if an image source was added to the chain.
static std::mutex m_bufMutex
virtual ossim_int32 connectMyInputTo(ossimConnectableObject *inputObject, bool makeOutputConnection=true, bool createEventFlag=true)
Will try to connect this objects input to the passed in object.
void test()
For engineering/debug.
ossimGpt ur() const
Definition: ossimGrect.h:257
virtual ossimRefPtr< ossimImageData > create(ossimSource *owner, ossimScalarType scalar, ossim_uint32 bands=1) const
ossim_uint32 width() const
Definition: ossimIrect.h:500
ossimIrect clipToRect(const ossimIrect &rect) const
Definition: ossimIrect.cpp:501
ossim_uint32 m_halfWindow
bool hasNans() const
Definition: ossimDpt.h:67
ossimGpt midPoint() const
Definition: ossimGrect.h:178
void remove(int pos, int num=1)
remove one or more arguments from the argv argument list, and decrement the argc respectively.
const ossimProjection * getProjection() const
Access methods for projection (may be NULL pointer).
virtual void initializeAOI()
Initializes m_aoiViewRect with the output area of interest as specified in master KWL...
ossimFilename m_horizonFile
std::map< double, double > m_horizonMap
static const char * DESCRIPTION
Used by ossimUtilityFactory.
virtual void initProcessingChain()
Derived classes initialize their custom chains here.
bool hasNans() const
Definition: ossimGpt.h:135
ossim_int32 y
Definition: ossimIpt.h:142
void setDescription(const ossimString &desc)
void findCenterGpt(ossimGpt &gpt)
Tries to determine the AOI center point based on KWL entries, else returns NaNs in gpt...
double atand(double x)
Definition: ossimCommon.h:266
ossim_uint32 m_numRadials
ossim_uint32 area() const
Definition: ossimIrect.h:396
double x
Definition: ossimDpt.h:164
bool empty() const
Definition: ossimString.h:411
bool hasNans() const
Definition: ossimIrect.h:337
virtual void setUlTiePoints(const ossimGpt &gpt)
bool worldToLocal(const ossimGpt &world_pt, ossimDpt &local_pt) const
Exposes the 3D world-to-local image coordinate reverse projection.
ossimDpt metersPerDegree() const
Definition: ossimGpt.cpp:498
ossim_int32 x
Definition: ossimIpt.h:141
ossim_float64 lat
Definition: ossimGpt.h:265
std::basic_ofstream< char > ofstream
Class for char output file streams.
Definition: ossimIosFwd.h:47
8 bit unsigned integer
ossimString after(const ossimString &str, std::string::size_type pos=0) const
METHOD: after(str, pos) Returns string immediately after the token str.
const ossimGpt & lr() const
Definition: ossimGrect.h:269
int & argc()
return the argument count.
virtual bool execute()
Performs the actual product write.
ossimGpt ll() const
Definition: ossimGrect.h:263
ossim_uint32 m_numThreads
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
virtual bool initialize(ossimArgumentParser &ap)
Initializes from command line arguments.
int ossim_int32
bool pointWithin(const ossimIpt &pt) const
Definition: ossimIrect.h:729
bool isnan(const float &v)
isnan Test for floating point Not A Number (NAN) value.
Definition: ossimCommon.h:91