contrib/brl/bbas/bsta/bsta_spherical_histogram.txx
Go to the documentation of this file.
00001 #ifndef bsta_spherical_histogram_txx_
00002 #define bsta_spherical_histogram_txx_
00003 //:
00004 // \file
00005 #include "bsta_spherical_histogram.h"
00006 
00007 #include <vcl_cmath.h> // for log()
00008 #include <vcl_iostream.h>
00009 #include <vcl_cassert.h>
00010 #include <vnl/vnl_math.h>
00011 #include <vnl/vnl_vector.h>
00012 //
00013 // determine if an angle lies inside an interval that possibly contains
00014 // the branch cut at 0/360
00015 //
00016 template <class T>
00017 static bool in_interval(T start, T range, T angle)
00018 {
00019   T end = start + range;
00020   bool no_cut = (end <= T(360));
00021   if (no_cut) {
00022     return angle >= start && angle < end;
00023   }
00024   // the interval contains the cut
00025   if (angle >= start && angle<=T(360))
00026     return true;
00027   T branch_end = end - T(360);
00028   if (angle >= 0 && angle < branch_end)
00029     return true;
00030   return false;
00031 }
00032 
00033 template <class T>
00034 static T start_90_180(T start, T range)
00035 {
00036   return -(start+range) + T(90);
00037 }
00038 
00039 // find the smallest distance between two azimuth angles, a and b
00040 // if it is assumed that x0 < x1 then compare (x1-x0) with pi - (x1-x0)
00041 // the smaller value is the difference. The sign of the difference is
00042 // determined by whether it is necessary to switch a and b.
00043 template <class T>
00044 static T azimuth_diff(T a, T b, T two_pi)
00045 {
00046   T x0 = a, x1 = b, sign = T(1);
00047   if (a>b) {x0 = b; x1 = a; sign = -sign;}
00048   T a1 = x1-x0;
00049   T a2 = two_pi - a1;
00050   if (a1>a2) return -sign*a2;
00051   return sign*a1;
00052 }
00053 
00054 template <class T>
00055 bsta_spherical_histogram<T>::bsta_spherical_histogram()
00056   : n_azimuth_(0), n_elevation_(0),
00057     azimuth_start_(0), azimuth_range_(0),
00058     elevation_start_(0), elevation_range_(0),
00059     az_branch_cut_(B_180_180), el_poles_(B_90_90),
00060     units_(DEG), total_counts_valid_(false), total_counts_(0)
00061 {
00062 }
00063 
00064 template <class T>
00065 bsta_spherical_histogram<T>::
00066 bsta_spherical_histogram(unsigned n_azimuth, unsigned n_elevation,
00067                          T azimuth_start, T azimuth_range, T elevation_start,
00068                          T elevation_range, ang_units units,
00069                          angle_bounds az_branch_cut, angle_bounds el_poles)
00070   : n_azimuth_(n_azimuth), n_elevation_(n_elevation),
00071     azimuth_start_(azimuth_start), azimuth_range_(azimuth_range),
00072     elevation_start_(elevation_start), elevation_range_(elevation_range),
00073     az_branch_cut_(az_branch_cut), el_poles_(el_poles),
00074     units_(units), total_counts_valid_(false), total_counts_(0)
00075 {
00076   assert(n_azimuth>0&&n_elevation>0);
00077   // convert to degrees if necessary
00078   if (units_ == RADIANS) {
00079     azimuth_start_   = rad_to_deg(azimuth_start_);
00080     azimuth_range_   = rad_to_deg(azimuth_range_);
00081     elevation_start_ = rad_to_deg(elevation_start_);
00082     elevation_range_ = rad_to_deg(elevation_range_);
00083   }
00084   //convert azimuth branch_cut to 0-360 if necessary
00085   if (az_branch_cut == B_180_180)
00086     if (azimuth_start_<T(0))
00087       azimuth_start_ = azimuth_start_+ static_cast<T>(360);
00088 
00089   //convert elevation range to 0->180
00090   if (el_poles_ == B_90_90)
00091     elevation_start_ =
00092       start_90_180(elevation_start_,elevation_range_);
00093 
00094   delta_az_ = azimuth_range_/n_azimuth_;
00095   delta_el_ = elevation_range_/n_elevation_;
00096 }
00097 
00098 //: azimuth bounds
00099 template <class T>
00100 T bsta_spherical_histogram<T>::azimuth_start() const
00101 {
00102   T az_start = azimuth_start_;
00103   if (az_branch_cut_==B_180_180)
00104     if (az_start >=T(180))
00105       az_start -= T(360);
00106   if (units_ == RADIANS)
00107     az_start = deg_to_rad(az_start);
00108   return az_start;
00109 }
00110 
00111 template <class T>
00112 T bsta_spherical_histogram<T>::azimuth_range() const
00113 {
00114   if (units_ == RADIANS)
00115     return deg_to_rad(azimuth_range_);
00116   return azimuth_range_;
00117 }
00118 
00119 //: elevation bounds
00120 template <class T>
00121 T bsta_spherical_histogram<T>::elevation_start() const
00122 {
00123   T el_start = elevation_start_;
00124   if (el_poles_==B_90_90)
00125     el_start = start_90_180(el_start, elevation_range_);
00126   if (units_ == RADIANS)
00127     el_start = deg_to_rad(el_start);
00128   return el_start;
00129 }
00130 
00131 template <class T>
00132 T bsta_spherical_histogram<T>::elevation_range() const
00133 {
00134   if (units_ == RADIANS)
00135     return deg_to_rad(elevation_range_);
00136   return elevation_range_;
00137 }
00138 
00139 template <class T>
00140 T bsta_spherical_histogram<T>::azimuth_center(int azimuth_index) const
00141 {
00142   double start = azimuth_index*delta_az_ + azimuth_start_;
00143   double end = start + delta_az_;
00144   double mid = 0.5*(start + end);
00145   if (mid>=360.0)
00146     mid -= 360.0;
00147   if (az_branch_cut_==B_180_180 && mid > 180.0)
00148     mid -= 360.0;
00149   T middle = static_cast<T>(mid);
00150   if (units_ == RADIANS)
00151     return deg_to_rad(middle);
00152   return middle;
00153 }
00154 
00155 template <class T>
00156 T bsta_spherical_histogram<T>::elevation_center(int elevation_index) const
00157 {
00158   double start = elevation_index*delta_el_;
00159   double end = start + delta_el_;
00160   double mid = 0.5*(start + end);
00161   if (el_poles_ == B_90_90)
00162     mid = 90.0-mid;
00163   T middle = static_cast<T>(mid);
00164   if (units_ == RADIANS)
00165     return deg_to_rad(middle);
00166   return middle;
00167 }
00168 
00169 template <class T>
00170 void bsta_spherical_histogram<T>::
00171 center(int linear_index, T& az_center, T& el_center)
00172 {
00173   //convert linear_index to azimuth and elevation index
00174   int el_index = linear_index/n_azimuth_;
00175   int az_index = linear_index%n_azimuth_;
00176   az_center = azimuth_center(az_index);
00177   el_center = elevation_center(el_index);
00178 }
00179 
00180 template <class T>
00181 void bsta_spherical_histogram<T>::
00182 convert_to_cartesian(T azimuth, T elevation, T& x, T& y, T& z) const
00183 {
00184   T az = azimuth, el=elevation;
00185   if (units_==DEG) {
00186     az = deg_to_rad(az);
00187     el = deg_to_rad(el);
00188   }
00189   //convert to standard spherical coordinates if necessary
00190   if (az_branch_cut_ == B_0_360)
00191     if (az>static_cast<T>(vnl_math::pi))
00192       az -= static_cast<T>(2.0*vnl_math::pi);
00193   if (el_poles_ == B_90_90)
00194     el = -el + static_cast<T>(vnl_math::pi/2.0);
00195   //convert to cartesian
00196   T s = vcl_sin(el);
00197   x = s*vcl_cos(az);
00198   y = s*vcl_sin(az);
00199   z = vcl_cos(el);
00200 }
00201 
00202 template <class T>
00203 void bsta_spherical_histogram<T>::
00204 convert_to_spherical(T x, T y, T z, T& azimuth, T& elevation) const
00205 {
00206   //internal representation is the standard spherical coordinate system
00207   //azimuth 0-360, elevation 0-180 with North Pole elevation = 0
00208 
00209   if (vcl_fabs(z-T(1))<1e-8) {//essentially at the North Pole
00210     azimuth = T(0);//units don't matter since azimuth is ambiguous
00211     if (el_poles_ == B_90_90)
00212       elevation = T(90);
00213     else
00214       elevation = T(0);
00215     if (units_ == RADIANS)
00216       elevation = deg_to_rad(elevation);
00217     return;
00218   }
00219   if (vcl_fabs(z+T(1))<1e-8) {//essentially at the South Pole
00220     azimuth = T(0);//units don't matter since azimuth is ambiguous
00221     if (el_poles_ == B_90_90)
00222       elevation = -T(90);
00223     else
00224       elevation = T(180);
00225     if (units_ == RADIANS)
00226       elevation = deg_to_rad(elevation);
00227     return;
00228   }
00229   elevation = vcl_acos(z);
00230   T s = vcl_sin(elevation);
00231   T xa = x/s;
00232   T ya = y/s;
00233   azimuth = vcl_atan2(ya, xa);//returns angles with +-180 branch cut
00234   if (az_branch_cut_ == B_0_360)
00235     if (azimuth<0) azimuth += static_cast<T>(2.0*vnl_math::pi);
00236   if (el_poles_ == B_90_90)
00237     elevation = -elevation + static_cast<T>(0.5*vnl_math::pi);
00238   if (units_ == DEG) {
00239     elevation = rad_to_deg(elevation);
00240     azimuth = rad_to_deg(azimuth);
00241   }
00242   return;
00243 }
00244 
00245 template <class T>
00246 int bsta_spherical_histogram<T>::azimuth_index(T azimuth) const
00247 {
00248   if (n_azimuth_==0) return -1;
00249   //convert to degrees if necessary
00250   T conv_az = azimuth;
00251   if (units_==RADIANS)
00252     conv_az = rad_to_deg(conv_az);
00253   //convert angle to 0/360 if necessary
00254   if (az_branch_cut_ == B_180_180)
00255     if (conv_az < 0)
00256       conv_az += T(360);
00257   for (unsigned i = 0; i<n_azimuth_; ++i) {
00258     T start = static_cast<T>(i*delta_az_)+ azimuth_start_;
00259     if (in_interval(start, delta_az_, conv_az))
00260     {
00261       return i;
00262     }
00263   }
00264   return -1;
00265 }
00266 
00267 template <class T>
00268 int bsta_spherical_histogram<T>::elevation_index(T elevation) const
00269 {
00270   if (n_elevation_==0) return -1;
00271   //convert to degrees if necessary
00272   T conv_el = elevation;
00273   if (units_==RADIANS)
00274     conv_el = rad_to_deg(conv_el);
00275   //standardized elevation coordinates range from
00276   // 0 (at North pole) to 180 at South pole
00277   if (el_poles_ == B_90_90) {
00278     conv_el = -conv_el + T(90);
00279   }
00280   for (unsigned i = 0; i<n_elevation_; ++i)
00281     if (static_cast<T>((i+1)*delta_el_) + elevation_start_ >= conv_el)
00282     {
00283       return i;
00284     }
00285   return -1;
00286 }
00287 
00288 template <class T>
00289 void bsta_spherical_histogram<T>::
00290 azimuth_interval(int azimuth_index, T& azimuth_start, T& azimuth_range) const
00291 {
00292   if (n_azimuth_==0) {
00293     azimuth_start = T(0);
00294     azimuth_range = T(0);
00295     return;
00296   }
00297   T az_start = static_cast<T>(azimuth_index*delta_az_)+azimuth_start_;
00298   //if branch _180 need to shift min, max
00299   if (az_branch_cut_ == B_180_180) {
00300     if (az_start >= T(180))
00301       az_start -= T(360);
00302   }
00303   else
00304     if (az_start>=T(360)) az_start -=T(360);
00305 
00306   // if radians
00307   if (units_ == RADIANS) {
00308     azimuth_start = deg_to_rad(az_start);
00309     azimuth_range = deg_to_rad(delta_az_);
00310     return;
00311   }
00312   azimuth_start = az_start;
00313   azimuth_range = delta_az_;
00314 }
00315 
00316 template <class T>
00317 void bsta_spherical_histogram<T>::
00318 elevation_interval(int elevation_index, T& elevation_start, T& elevation_range) const
00319 {
00320   if (n_elevation_==0) {
00321     elevation_start = T(0);
00322     elevation_range = T(0);
00323     return;
00324   }
00325 
00326   T el_start = static_cast<T>(elevation_index*delta_el_);
00327   if (el_poles_ == B_90_90)
00328     el_start = start_90_180(el_start, delta_el_);
00329 
00330   if (units_ == RADIANS) {
00331     elevation_start  = deg_to_rad(el_start);
00332     elevation_range  = deg_to_rad(delta_el_);
00333     return;
00334   }
00335   elevation_start = el_start;
00336   elevation_range = delta_el_;
00337 }
00338 
00339 template <class T>
00340 void bsta_spherical_histogram<T>::
00341 upcount(T azimuth, T elevation, T mag)
00342 {
00343   int az_indx = azimuth_index(azimuth);
00344   int el_indx = elevation_index(elevation);
00345   int lidx = linear_index(az_indx, el_indx);
00346   if (!counts_[lidx])
00347     counts_[lidx]=T(0);
00348   counts_[lidx]+=mag;
00349   total_counts_valid_ = false;
00350 }
00351 
00352 template <class T>
00353 void bsta_spherical_histogram<T>::
00354 upcount_weighted_by_area(T azimuth, T elevation, T mag)
00355 {
00356   int el_indx = elevation_index(elevation);
00357   double ecnt = elevation_center(el_indx);
00358   T s = static_cast<T>(vcl_fabs(vcl_sin(ecnt)));
00359   if (s>0)
00360     mag /= s;
00361   upcount(azimuth, elevation, mag);
00362 }
00363 
00364 template <class T>
00365 T bsta_spherical_histogram<T>::total_counts()
00366 {
00367   if (!total_counts_valid_) {
00368     total_counts_ = T(0);
00369     typename vcl_map<int, T>::iterator cit = counts_.begin();
00370     for (; cit != counts_.end(); ++cit)
00371       total_counts_ += (*cit).second;
00372     total_counts_valid_ = true;
00373   }
00374   return total_counts_;
00375 }
00376 
00377 template <class T>
00378 T bsta_spherical_histogram<T>::p(int azimuth_index, int elevation_index)
00379 {
00380   T tcnts = this->total_counts();
00381   if (tcnts == T(0)) return T(0);
00382   if (elevation_index<0||elevation_index>=static_cast<int>(n_elevation())||
00383      azimuth_index<0||azimuth_index>=static_cast<int>(n_azimuth()))
00384     return T(0);
00385   int lidx = linear_index(azimuth_index, elevation_index);
00386   if (!counts_[lidx])
00387     return 0;
00388   return counts_[lidx]/tcnts;
00389 }
00390 
00391 template <class T>
00392 void bsta_spherical_histogram<T>::mean(T& mean_az, T& mean_el)
00393 {
00394   //define mean as the average Cartesian unit vector for the
00395   //unit vectors corresponding to the centers of the occupied bins
00396   double mean_x = 0.0, mean_y = 0.0, mean_z = 0.0;
00397   for (int el = 0; el<static_cast<int>(n_elevation_); ++el) {
00398     T ec = elevation_center(el);
00399     for (int az = 0; az<static_cast<int>(n_azimuth_); ++az) {
00400       T ac = azimuth_center(az);
00401       T x, y, z;
00402       convert_to_cartesian(ac, ec, x, y, z);
00403       T pc = p(az, el);
00404       mean_x  += pc*x;      mean_y  += pc*y;       mean_z  += pc*z;
00405     }
00406   }
00407   double length = vcl_sqrt(mean_x*mean_x + mean_y*mean_y + mean_z*mean_z);
00408   if (length<1.0e-8) {
00409     mean_az = 0;
00410     mean_el = 0;
00411     return;
00412   }
00413   mean_x /= length;   mean_y /= length;   mean_z /= length;
00414   convert_to_spherical(static_cast<T>(mean_x), static_cast<T>(mean_y),
00415                        static_cast<T>(mean_z), mean_az, mean_el);
00416 }
00417 
00418 template <class T>
00419 vnl_matrix_fixed<T, 2, 2> bsta_spherical_histogram<T>::covariance_matrix()
00420 {
00421   double mean_x = T(0), mean_y = T(0) , mean_z = T(0);
00422   T two_pi = T(360);
00423   if (units_ == RADIANS)
00424     two_pi = static_cast<T>(2.0*vnl_math::pi);
00425   for (int el = 0; el<static_cast<int>(n_elevation_); ++el) {
00426     T ec = elevation_center(el);
00427     for (int az = 0; az<static_cast<int>(n_azimuth_); ++az) {
00428       T pc = p(az, el);
00429       if (pc==T(0)) continue;
00430       T ac = azimuth_center(az);
00431       T x, y, z;
00432       convert_to_cartesian(ac, ec, x, y, z);
00433       mean_x  += pc*x;      mean_y  += pc*y;  mean_z  += pc*z;
00434     }
00435   }
00436   double length = vcl_sqrt(mean_x*mean_x + mean_y*mean_y + mean_z*mean_z);
00437   if (length<1.0e-8)
00438     return vnl_matrix_fixed<T, 2 ,2> ();
00439   mean_x /= length;   mean_y /= length;   mean_z /= length;
00440   T mean_az, mean_el;
00441   convert_to_spherical(static_cast<T>(mean_x), static_cast<T>(mean_y),
00442                        static_cast<T>(mean_z), mean_az, mean_el);
00443   T caz=T(0), caz_el=T(0), cel=T(0);
00444   for (int el = 0; el<static_cast<int>(n_elevation_); ++el) {
00445     T ec = elevation_center(el);
00446     for (int az = 0; az<static_cast<int>(n_azimuth_); ++az)
00447     {
00448       T pc = p(az, el);
00449       if (pc==T(0)) continue;
00450       T ac = azimuth_center(az);
00451       T delta_el = (ec-mean_el);
00452       cel += pc*delta_el*delta_el;
00453       T delta_az = azimuth_diff(mean_az, ac, two_pi);
00454       caz_el += pc*(ec-mean_el)*delta_az;
00455       caz += pc*delta_az*delta_az;
00456     }
00457   }
00458   vnl_matrix_fixed<T, 2, 2> ret;
00459   ret[0][0] = caz; ret[0][1]=caz_el; ret[1][0]=ret[0][1]; ret[1][1]=cel;
00460   return ret;
00461 }
00462 
00463 template <class T>
00464 void bsta_spherical_histogram<T>::std_dev(T& std_dev_az, T& std_dev_el)
00465 {
00466   vnl_matrix_fixed<T, 2, 2> covar = covariance_matrix();
00467   double var_az = covar[0][0], var_el = covar[1][1];
00468   std_dev_az = static_cast<T>(vcl_sqrt(var_az));
00469   std_dev_el = static_cast<T>(vcl_sqrt(var_el));
00470 }
00471 
00472 template <class T>
00473 vcl_vector<int> bsta_spherical_histogram<T>::
00474 bins_intersecting_cone(T center_az, T center_el, T cone_half_angle)
00475 {
00476   vcl_vector<int> ret;
00477   T xc, yc ,zc;
00478   convert_to_cartesian(center_az, center_el, xc, yc, zc);
00479   vnl_vector<double> cone_axis(3);
00480   cone_axis[0]=xc; cone_axis[1]=yc; cone_axis[2]=zc;
00481   for (int az = 0; az<int(n_azimuth_); ++az)
00482     for (int el = 0; el<int(n_elevation_); ++el) {
00483       T azc = azimuth_center(az), elc = elevation_center(el);
00484       T xb, yb, zb;
00485       convert_to_cartesian(azc, elc, xb, yb, zb);
00486       vnl_vector<double> bin_vector(3);
00487       bin_vector[0]=xb; bin_vector[1]=yb; bin_vector[2]=zb;
00488       double ang = angle(cone_axis, bin_vector);
00489       if (units_ == DEG)
00490         ang = rad_to_deg(static_cast<T>(ang));
00491       if (vcl_fabs(ang)<=cone_half_angle)
00492         ret.push_back(linear_index(az, el));
00493     }
00494   return ret;
00495 }
00496 
00497 template <class T>
00498 void bsta_spherical_histogram<T>::
00499 write_counts_with_interval(vcl_ostream& os, int azimuth_index,
00500                            int elevation_index) {
00501   T az_start, az_range, el_start, el_range;
00502   int lidx = linear_index(azimuth_index,elevation_index);
00503   T cnts = counts_[lidx];
00504   if (!(cnts>T(0))) return;
00505   azimuth_interval(azimuth_index, az_start, az_range);
00506   elevation_interval(elevation_index, el_start, el_range);
00507   os << "az(s:"<< az_start << " int:" << az_range << "):el(s:"
00508      << el_start << " int:" << el_range << ") "
00509      << cnts <<'\n';
00510 }
00511 
00512 template <class T>
00513 void bsta_spherical_histogram<T>::
00514 write_counts_with_center(vcl_ostream& os, int azimuth_index,
00515                          int elevation_index) {
00516   int lidx = linear_index(azimuth_index,elevation_index);
00517   T cnts = counts_[lidx];
00518   if (!(cnts>T(0))) return;
00519   T az_center = azimuth_center(azimuth_index);
00520   T el_center = elevation_center(elevation_index);
00521   os << az_center << ' ' << el_center << ' ' << cnts << '\n';
00522 }
00523 
00524 template <class T>
00525 void bsta_spherical_histogram<T>::print_to_text(vcl_ostream& os)
00526 {
00527   for (unsigned el = 0; el<n_elevation(); ++el)
00528     for (unsigned az = 0; az<n_azimuth(); ++az)
00529       this->write_counts_with_center(os, az, el);
00530 }
00531 
00532 template <class T>
00533 void bsta_spherical_histogram<T>::print_to_vrml(vcl_ostream& os,
00534                                                 T transparency)
00535 {
00536   os << "#VRML V2.0 utf8\n"
00537      << "Background {\n"
00538      << "  skyColor [ 0 0 0 ]\n"
00539      << "  groundColor [ 0 0 0 ]\n"
00540      << "}\n"
00541      << "PointLight {\n"
00542      << "  on FALSE\n"
00543      << "  intensity 1\n"
00544      << "ambientIntensity 0\n"
00545      << "color 1 1 1\n"
00546      << "location 0 0 0\n"
00547      << "attenuation 1 0 0\n"
00548      << "radius 100\n"
00549      << "}\n";
00550     for (int az = 0; az<int(n_azimuth_); ++az)
00551       for (int el = 0; el<int(n_elevation_); ++el) {
00552         T cnts = counts(az, el);
00553         if (cnts>0) {
00554           T azc = azimuth_center(az), elc = elevation_center(el);
00555           T x, y, z;
00556           convert_to_cartesian(azc, elc, x, y, z);
00557           os<< "Transform {\n"
00558             << "translation " << x << ' ' << y << ' '
00559             << ' ' << z << '\n'
00560             << "children [\n"
00561             << "Shape {\n"
00562             << " appearance Appearance{\n"
00563             << "   material Material\n"
00564             << "    {\n"
00565             << "      diffuseColor " << 1 << ' ' << 1 << ' ' << 0 << '\n'
00566             << "      transparency " << transparency << '\n'
00567             << "    }\n"
00568             << "  }\n"
00569             << " geometry Sphere\n"
00570             <<   "{\n"
00571             << "  radius " << p(az, el) << '\n'
00572             <<  "   }\n"
00573             <<  "  }\n"
00574             <<  " ]\n"
00575             << "}\n";
00576         }
00577       }
00578   os << "Transform {\n"
00579      << "translation " << 0 << ' ' << 0 << ' '
00580      << ' ' << 0 << '\n'
00581      << "children [\n"
00582      << "Shape {\n"
00583      << " appearance Appearance{\n"
00584      << "   material Material\n"
00585      << "    {\n"
00586      << "      diffuseColor " << 0 << ' ' << 1 << ' ' << 0 << '\n'
00587      << "      transparency " << 0 << '\n'
00588      << "    }\n"
00589      << "  }\n"
00590      << " geometry Sphere\n"
00591      <<   "{\n"
00592      << "  radius " << 0.95 << '\n'
00593      <<  "   }\n"
00594      <<  "  }\n"
00595      <<  " ]\n"
00596      << "}\n";
00597 }
00598 
00599 //: Write to stream
00600 template <class T>
00601 vcl_ostream& operator<<(vcl_ostream& s, bsta_spherical_histogram<T> const& h)
00602 {
00603   bsta_spherical_histogram<T>& nch = const_cast<bsta_spherical_histogram<T>&>(h);
00604   s << "bsta_spherical_histogram<T> ==>\n";
00605   if (h.units() == bsta_spherical_histogram<T>::RADIANS)
00606     s << "units: radians\n";
00607   else s << "units: degrees\n";
00608   s << "n_bins_azimuth: " << h.n_azimuth() << " n_bins_elevation: "
00609     << h.n_elevation() << '\n'
00610     << "azimuth(start: " << h.azimuth_start() <<" interval: " << h.azimuth_range()<< " )\n"
00611     << "elevation(start: "<< h.elevation_start()<<" interval: " << h.elevation_range()<<" )\n";
00612   if (h.azimuth_branch_cut() == bsta_spherical_histogram<T>::B_0_360) {
00613     if (h.units() == bsta_spherical_histogram<T>::RADIANS)
00614       s << "azimuth branch cut: 0/2*PI\n";
00615     else
00616       s << "azimuth branch cut: 0/360\n";
00617   }
00618   else {
00619     if (h.units() == bsta_spherical_histogram<T>::RADIANS)
00620       s << "azimuth branch cut: -PI/PI\n";
00621     else
00622       s << "azimuth branch cut: -180/180\n";
00623   }
00624 
00625   if (h.elevation_poles() == bsta_spherical_histogram<T>::B_90_90) {
00626     if (h.units()==bsta_spherical_histogram<T>::RADIANS)
00627       s << "south pole: -PI/2  north_pole: PI\n";
00628     else
00629       s << "south pole: -90  north_pole: 90\n";
00630   }
00631   else {
00632     if (h.units()==bsta_spherical_histogram<T>::RADIANS)
00633       s << "south pole: PI  north_pole: 0\n";
00634     else
00635       s << "south pole: 180  north_pole: 0\n";
00636   }
00637   s << "-- counts --\n";
00638   for (unsigned r = 0; r<h.n_elevation(); ++r)
00639     for (unsigned c = 0; c<h.n_azimuth(); ++c)
00640       nch.write_counts_with_interval(s, c, r);
00641   return s;
00642 }
00643 
00644 //: Read from stream
00645 template <class T>
00646 vcl_istream& operator>>(vcl_istream& is, bsta_spherical_histogram<T>& h)
00647 {
00648   return is;
00649 }
00650 
00651 #undef BSTA_SPHERICAL_HISTOGRAM_INSTANTIATE
00652 #define BSTA_SPHERICAL_HISTOGRAM_INSTANTIATE(T) \
00653 template class bsta_spherical_histogram<T >;\
00654 template vcl_istream& operator>>(vcl_istream&, bsta_spherical_histogram<T >&);\
00655 template vcl_ostream& operator<<(vcl_ostream&, bsta_spherical_histogram<T > const&)
00656 
00657 #endif // bsta_spherical_histogram_txx_