1 #include <iostream>
2 #include <algorithm>
3 #include <iterator>
4 #include <cstdio>
5 #include <utility>
6 #include "volm_satellite_resources.h"
7 
8 #include "vil/vil_load.h"
9 #include <vil/file_formats/vil_nitf2_image.h>
10 #include "vil/vil_save.h"
11 #include "vil/vil_convert.h"
12 #include <vil/algo/vil_threshold.h>
13 #include <vil/algo/vil_binary_erode.h>
14 #include <vil/algo/vil_structuring_element.h>
15 #include <vil/algo/vil_region_finder.h>
16 #include "vul/vul_file_iterator.h"
17 #include "vul/vul_file.h"
18 #include <volm/volm_tile.h>
19 #include <volm/volm_geo_index2.h>
20 #include "vgl/vgl_intersection.h"
21 #include "vgl/vgl_polygon.h"
22 #include "vgl/vgl_polygon_scan_iterator.h"
23 #include "vgl/vgl_area.h"
24 #include "vgl/vgl_clip.h"
25 #include <vgl/algo/vgl_convex_hull_2d.h>
26 #include "vpgl/vpgl_lvcs.h"
27 #ifdef _MSC_VER
28 #  include "vcl_msvc_warnings.h"
29 #endif
30 #include <bkml/bkml_parser.h>
31 
add_directories(const std::string & root,std::vector<std::string> & directories)32 void add_directories(const std::string& root, std::vector<std::string>& directories) {
33   if (vul_file::is_directory(root))
34     directories.push_back(root);
35 
36   std::string glob = root + "/*"; // get everything directory or not
37   vul_file_iterator file_it(glob.c_str());
38   ++file_it;  // skip .
39   ++file_it;  // skip ..
40   while (file_it) {
41     std::string name(file_it());
42     if (vul_file::is_directory(name))
43       add_directories(name, directories);
44     ++file_it;
45   }
46 
47 }
48 
add_resource(const std::string & name)49 void volm_satellite_resources::add_resource(const std::string& name)
50 {
51   volm_satellite_resource res;
52   res.full_path_ = name;
53   res.name_ = vul_file::strip_directory(name);
54   res.name_ = vul_file::strip_extension(res.name_);
55   res.meta_ = new brad_image_metadata(name, "");
56   if (eliminate_same_) {
57     // first check if we already have the exact same image, (unfortunately there are resources with different name but taken at exactly same time, so same image)
58     // we define images are same if they have time less than 2 minutes, close enough extent, same name and same band number
59     for (auto & resource : resources_) {
60       if (resource.meta_->satellite_name_.compare(res.meta_->satellite_name_) == 0 &&
61           resource.meta_->band_.compare(res.meta_->band_) == 0 &&
62           this->same_time(resource, res) &&
63           this->same_view(resource, res) &&
64           this->same_extent(resource, res))
65       {
66           std::cout << "!!!!!!!!!!!!! cannot add: " << res.name_ << " with time: "; res.meta_->print_time();
67           std::cout << " view: " << res.meta_->view_azimuth_ << ", " << res.meta_->view_elevation_;
68           std::cout << "already exists: \n"   << resource.name_ << " with time: "; resource.meta_->print_time();
69           std::cout << " view: " << resource.meta_->view_azimuth_ << ", " << resource.meta_->view_elevation_;
70           std::cout << " band of resources: " <<  resource.meta_->band_ << " band trying to add: " << res.meta_->band_ << std::flush << std::endl;
71           return;
72       }
73     }
74   }
75   if (res.meta_->gsd_ > 0)  // if there are parsing problems, gsd is negative
76     resources_.push_back(res);
77 }
78 
79 //: x is lon and y is lat in the bbox, construct bbox with min point to be lower left and max to be upper right and as axis aligned with North-East
volm_satellite_resources(vgl_box_2d<double> & bbox,double min_size,bool eliminate_same)80 volm_satellite_resources::volm_satellite_resources(vgl_box_2d<double>& bbox, double min_size, bool eliminate_same) : min_size_(min_size), bbox_(bbox), eliminate_same_(eliminate_same)
81 {
82   this->construct_tree();
83 }
84 
85 //: traverse the given path recursively and add each satellite resource
add_path(std::string path)86 void volm_satellite_resources::add_path(std::string path)
87 {
88   std::vector<std::string> directories;
89   add_directories(std::move(path), directories);
90   if (!directories.size())
91     return;
92   std::cout << "found " << directories.size() << " directories!\n";
93   unsigned start = resources_.size();
94   for (const auto & directorie : directories) {
95     std::string glob = directorie + "/*.NTF";
96     vul_file_iterator file_it(glob.c_str());
97     while (file_it) {
98       std::string name(file_it());
99       //std::cout << name << "\n";
100       this->add_resource(name);
101       ++file_it;
102     }
103     glob = directorie + "/*.ntf";
104     vul_file_iterator file_it2(glob.c_str());
105     while (file_it2) {
106       std::string name(file_it2());
107       //std::cout << name << "\n";
108       this->add_resource(name);
109       ++file_it2;
110     }
111   }
112   unsigned end = resources_.size();
113   this->add_resources(start, end);
114 }
115 
construct_tree()116 void volm_satellite_resources::construct_tree()
117 {
118   // construct volm_geo_index2 quad tree with 1.0 degree leaves - satellite images are pretty large, so the leaves need to be large
119   root_ = volm_geo_index2::construct_tree<std::vector<unsigned> >(bbox_, min_size_);
120   std::vector<volm_geo_index2_node_sptr> leaves;
121   volm_geo_index2::get_leaves(root_, leaves);
122   std::cout << " the number of leaves in the quad tree of satellite resources: " << leaves.size() << '\n';
123 }
124 
add_resources(unsigned start,unsigned end)125 void volm_satellite_resources::add_resources(unsigned start, unsigned end) {
126   // insert the ids of the resources
127   for (unsigned i = start; i < end; i++) {
128     std::vector<volm_geo_index2_node_sptr> leaves;
129     // CAUTION: x is lat and y is lon in nitf_camera but we want x to be lon and y to be lat, use all the corners of satellite image by inverting x-y to create the bounding box
130     vgl_box_2d<double> satellite_footprint;
131     satellite_footprint.add(vgl_point_2d<double>(resources_[i].meta_->lower_left_.x(), resources_[i].meta_->lower_left_.y()));
132     satellite_footprint.add(vgl_point_2d<double>(resources_[i].meta_->upper_right_.x(), resources_[i].meta_->upper_right_.y()));
133     volm_geo_index2::get_leaves(root_, leaves, satellite_footprint);
134     for (auto & leave : leaves) {
135       auto* leaf_ptr = dynamic_cast<volm_geo_index2_node<std::vector<unsigned> >* >(leave.ptr());
136       leaf_ptr->contents_.push_back(i);  // push this satellite image to this leave that intersects its footprint
137     }
138   }
139 }
140 
141 //: get a list of ids in the resources_ list that overlap the given rectangular region
query(double lower_left_lon,double lower_left_lat,double upper_right_lon,double upper_right_lat,const std::string & band_str,std::vector<unsigned> & ids,double gsd_thres)142 void volm_satellite_resources::query(double lower_left_lon, double lower_left_lat, double upper_right_lon, double upper_right_lat, const std::string& band_str, std::vector<unsigned>& ids, double gsd_thres)
143 {
144   vgl_box_2d<double> area(lower_left_lon, upper_right_lon, lower_left_lat, upper_right_lat);
145   std::vector<volm_geo_index2_node_sptr> leaves;
146   volm_geo_index2::get_leaves(root_, leaves, area);
147   std::vector<unsigned> temp_ids_init;
148   for (auto & leave : leaves) {
149     auto* leaf_ptr = dynamic_cast<volm_geo_index2_node<std::vector<unsigned> >* >(leave.ptr());
150     // check which images overlap with the given bbox
151     for (unsigned int res_id : leaf_ptr->contents_) {
152       // CAUTION: x is lat and y is lon in nitf_camera but we want x to be lon and y to be lat, use all the corners of satellite image by inverting x-y to create the bounding box
153       vgl_box_2d<double> sat_box;
154       sat_box.add(vgl_point_2d<double>(resources_[res_id].meta_->lower_left_.x(), resources_[res_id].meta_->lower_left_.y()));
155       sat_box.add(vgl_point_2d<double>(resources_[res_id].meta_->upper_right_.x(), resources_[res_id].meta_->upper_right_.y()));
156       if (resources_[res_id].meta_->band_.compare(band_str) == 0 && vgl_area(vgl_intersection(sat_box, area)) > 0)
157         temp_ids_init.push_back(res_id);
158     }
159   }
160 
161   // eliminate the ones which does not satisfy the GSD (ground sampling distance) threshold
162   std::vector<unsigned> temp_ids;
163   for (unsigned int i : temp_ids_init) {
164     if (resources_[i].meta_->gsd_ <= gsd_thres)
165       temp_ids.push_back(i);
166   }
167 
168   // order the resources in the order of GeoEye1, WV2, WV1, QB/others
169   for (unsigned int temp_id : temp_ids) {
170     if (resources_[temp_id].meta_->satellite_name_.compare("GeoEye-1") == 0)
171       ids.push_back(temp_id);
172   }
173   for (unsigned int temp_id : temp_ids) {
174     if (resources_[temp_id].meta_->satellite_name_.compare("WorldView") == 0)
175       ids.push_back(temp_id);
176   }
177   for (unsigned int temp_id : temp_ids) {
178     if (resources_[temp_id].meta_->satellite_name_.compare("WorldView2") == 0)
179       ids.push_back(temp_id);
180   }
181 
182   std::vector<unsigned> temp_ids2;
183   // find the ones that are not already in ids
184   for (unsigned int temp_id : temp_ids) {
185     bool contains = false;
186     for (unsigned int id : ids) {
187       if (temp_id == id) {
188         contains = true;
189         break;
190       }
191     }
192     if (!contains)
193       temp_ids2.push_back(temp_id);
194   }
195   for (unsigned int i : temp_ids2)
196     ids.push_back(i);
197 
198 
199 }
200 //: query the resources in the given box and output the full paths to the given file
query_print_to_file(double lower_left_lon,double lower_left_lat,double upper_right_lon,double upper_right_lat,unsigned & cnt,std::string & out_file,const std::string & band_str,double gsd_thres)201 bool volm_satellite_resources::query_print_to_file(double lower_left_lon, double lower_left_lat, double upper_right_lon, double upper_right_lat, unsigned& cnt, std::string& out_file, const std::string& band_str, double gsd_thres)
202 {
203   std::vector<unsigned> ids, ids_all;
204   query(lower_left_lon, lower_left_lat, upper_right_lon, upper_right_lat, band_str, ids_all, gsd_thres);
205 
206   // eliminate the repeating ids, more than one leaf may contain the same resource
207   for (unsigned i = 0; i < ids_all.size(); i++) {
208     bool contains = false;
209     for (unsigned j = i+1; j < ids_all.size(); j++) {
210       if (ids_all[i] == ids_all[j]) {
211         contains = true;
212         break;
213       }
214     }
215     if (!contains)
216       ids.push_back(ids_all[i]);
217   }
218 
219   cnt = ids.size();
220   if (out_file.compare("") == 0)
221     return true;
222   std::ofstream ofs(out_file.c_str());
223   if (!ofs) {
224     std::cerr << "In volm_satellite_resources::query_print_to_file() -- cannot open file: " << out_file << std::endl;
225     return false;
226   }
227   for (unsigned int id : ids)
228     ofs << resources_[id].full_path_ << '\n';
229   ofs.close();
230   return true;
231 }
232 
233 //: query the resources in the given box and output the full paths of randomly selected seeds to the given file,
234 //  the order of satellites to select seeds from: GeoEye1, WorldView2, WorldView1 and then any others
query_seeds_print_to_file(double lower_left_lon,double lower_left_lat,double upper_right_lon,double upper_right_lat,int n_seeds,unsigned & cnt,std::string & out_file,std::string & band_str,double gsd_thres)235 bool volm_satellite_resources::query_seeds_print_to_file(double lower_left_lon, double lower_left_lat, double upper_right_lon, double upper_right_lat, int n_seeds, unsigned& cnt, std::string& out_file, std::string& band_str, double gsd_thres)
236 {
237   std::vector<unsigned> ids;
238   double mid_lon = (lower_left_lon + upper_right_lon) / 2;
239   double mid_lat = (lower_left_lat + upper_right_lat) / 2;
240   double lower_lon = (lower_left_lon + mid_lon) / 2;
241   double lower_lat = (lower_left_lat + mid_lat) / 2;
242   double upper_lon = (upper_right_lon + mid_lon) / 2;
243   double upper_lat = (upper_right_lat + mid_lat) / 2;
244   std::cout << "using bbox for scene: " << lower_lon << " " << lower_lat << " " << upper_lon << " " << upper_lat << std::endl;
245   query(lower_lon, lower_lat, upper_lon, upper_lat, band_str, ids, gsd_thres);
246 
247   // now select n_seeds among these ones
248   std::map<std::string, std::vector<unsigned> > possible_seeds;
249   std::vector<unsigned> tmp;
250   possible_seeds["GeoEye-1"] = tmp;
251   possible_seeds["WV01"] = tmp;
252   possible_seeds["WV02"] = tmp;
253   possible_seeds["other"] = tmp;
254   for (unsigned int id : ids) {
255     if (resources_[id].meta_->cloud_coverage_percentage_ < 1) {
256       auto iter = possible_seeds.find(resources_[id].meta_->satellite_name_);
257       if (iter != possible_seeds.end())
258         iter->second.push_back(id);
259       else
260         possible_seeds["other"].push_back(id);
261     }
262   }
263 #if 0
264   std::cout << "possible seeds:" << std::endl;
265   for (std::map<std::string, std::vector<unsigned> >::iterator iter = possible_seeds.begin(); iter != possible_seeds.end(); iter++) {
266     std::cout << iter->first << "\n";
267     for (unsigned k = 0; k < iter->second.size(); k++) {
268       std::cout << "\t\t" << resources_[iter->second[k]].name_ << "\n";
269     }
270   }
271 #endif
272 
273   std::vector<brad_image_metadata_sptr> selected_names;
274 #if 0
275   std::vector<std::string> names;
276   names.push_back("GeoEye-1"); names.push_back("WV02"); names.push_back("WV01"); names.push_back("other");
277   for (unsigned ii = 0; ii < names.size(); ii++) {
278     std::string name = names[ii];
279     for (unsigned i = 0; i < possible_seeds[name].size(); i++) {
280       std::cout << resources_[possible_seeds[name][i]].name_;
281       std::cout << " time " << name << ": " << resources_[possible_seeds[name][i]].meta_->t_.year;
282       std::cout << " " << resources_[possible_seeds[name][i]].meta_->t_.month;
283       std::cout << " " << resources_[possible_seeds[name][i]].meta_->t_.day;
284       std::cout << " " << resources_[possible_seeds[name][i]].meta_->t_.hour;
285       std::cout << " " << resources_[possible_seeds[name][i]].meta_->t_.min;
286       std::cout << "\n";
287     }
288   }
289 #endif
290 
291   std::vector<std::string> seed_paths;
292 
293   cnt = 0;
294   bool done = false;
295   for (unsigned i = 0; i < possible_seeds["GeoEye-1"].size(); i++) {
296     // first check if there is a satellite with the same time
297     bool exists = false;
298     for (auto & selected_name : selected_names) {
299       if (resources_[possible_seeds["GeoEye-1"][i]].meta_->same_time(*selected_name.ptr())) {
300         exists = true;
301         break;
302       }
303     }
304     if (exists) continue;
305     selected_names.push_back(resources_[possible_seeds["GeoEye-1"][i]].meta_);
306 
307     //ofs << resources_[possible_seeds["GeoEye-1"][i]].name_ << '\n';
308     seed_paths.push_back(resources_[possible_seeds["GeoEye-1"][i]].name_);
309     //std::cout << resources_[possible_seeds["GeoEye-1"][i]].name_ << '\n';
310     cnt++;
311 
312     if (cnt == n_seeds) {
313      done = true;
314      break;
315     }
316   }
317   if (!done) {
318     for (unsigned i = 0; i < possible_seeds["WV02"].size(); i++) {
319 
320      // first check if there is a satellite with the same time
321      bool exists = false;
322      for (auto & selected_name : selected_names) {
323        if (resources_[possible_seeds["WV02"][i]].meta_->same_time(*selected_name.ptr())) {
324          exists = true;
325          break;
326        }
327      }
328      if (exists) continue;
329      selected_names.push_back(resources_[possible_seeds["WV02"][i]].meta_);
330 
331      //ofs << resources_[possible_seeds["WV02"][i]].name_ << '\n';
332      seed_paths.push_back(resources_[possible_seeds["WV02"][i]].name_);
333      //std::cout << resources_[possible_seeds["WV02"][i]].name_ << '\n';
334      cnt++;
335      if (cnt == n_seeds) {
336        done = true;
337        break;
338      }
339     }
340   }
341   if (!done) {
342     for (unsigned i = 0; i < possible_seeds["WV01"].size(); i++) {
343 
344      // first check if there is a satellite with the same time
345      bool exists = false;
346      for (auto & selected_name : selected_names) {
347        if (resources_[possible_seeds["WV01"][i]].meta_->same_time(*selected_name.ptr())) {
348          exists = true;
349          break;
350        }
351      }
352      if (exists) continue;
353      selected_names.push_back(resources_[possible_seeds["WV01"][i]].meta_);
354 
355      //ofs << resources_[possible_seeds["WV01"][i]].name_ << '\n';
356      seed_paths.push_back(resources_[possible_seeds["WV01"][i]].name_);
357      //std::cout << resources_[possible_seeds["WV01"][i]].name_ << '\n';
358      cnt++;
359      if (cnt == n_seeds) {
360        done = true;
361        break;
362      }
363     }
364   }
365   if (!done) {
366     for (unsigned i = 0; i < possible_seeds["other"].size(); i++) {
367 
368      // first check if there is a satellite with the same time
369      bool exists = false;
370      for (auto & selected_name : selected_names) {
371        if (resources_[possible_seeds["other"][i]].meta_->same_time(*selected_name.ptr())) {
372          exists = true;
373          break;
374        }
375      }
376      if (exists) continue;
377      selected_names.push_back(resources_[possible_seeds["other"][i]].meta_);
378 
379      //ofs << resources_[possible_seeds["other"][i]].name_ << '\n';
380      seed_paths.push_back(resources_[possible_seeds["other"][i]].name_);
381      //std::cout << resources_[possible_seeds["other"][i]].name_ << '\n';
382      cnt++;
383      if (cnt == n_seeds) {
384        break;
385      }
386     }
387   }
388 
389   if (out_file.compare("") == 0)
390     return true;
391 
392   std::ofstream ofs(out_file.c_str());
393   if (!ofs) {
394     std::cerr << "In volm_satellite_resources::query_print_to_file() -- cannot open file: " << out_file << std::endl;
395     return false;
396   }
397 
398   for (const auto & seed_path : seed_paths)
399     ofs << seed_path << '\n';
400 
401   ofs.close();
402   return true;
403 }
404 
405 //: get a list of ids in the resources_ list that overlap the given rectangular region
query_pairs(double lower_left_lon,double lower_left_lat,double upper_right_lon,double upper_right_lat,std::string & sat_name,float GSD_thres,std::vector<std::pair<unsigned,unsigned>> & ids)406 unsigned volm_satellite_resources::query_pairs(double lower_left_lon, double lower_left_lat, double upper_right_lon, double upper_right_lat, std::string& sat_name, float GSD_thres, std::vector<std::pair<unsigned, unsigned> >& ids)
407 {
408   // first get all the images that intersect the area
409   std::vector<unsigned> temp_ids;
410   std::string band_str = "PAN";
411   this->query(lower_left_lon, lower_left_lat, upper_right_lon, upper_right_lat, band_str, temp_ids,GSD_thres); // we're only interested in which images intersect the box, so pass gsd_thres very high
412   std::cout << "there are " << temp_ids.size() << " images that intersect the scene!\n";
413 
414   // prune out the ones from the wrong satellite
415   std::vector<unsigned> ids2;
416   for (unsigned int temp_id : temp_ids) {
417     if (resources_[temp_id].meta_->satellite_name_.compare(sat_name) == 0)
418       ids2.push_back(temp_id);
419   }
420 
421   std::cout << "there are " << ids2.size() << " images that intersect the scene from sat: " << sat_name << "!\n";
422   // check the time of collection to find pairs
423   for (unsigned i = 0; i < ids2.size(); i++) {
424     for (unsigned j = i+1; j < ids2.size(); j++) {
425       if (!resources_[ids2[i]].meta_->same_day(*(resources_[ids2[j]].meta_)))
426         continue;
427 
428       std::cout << resources_[ids2[i]].name_ << ": ";
429       resources_[ids2[i]].meta_->print_time();
430       std::cout << " azi: " << resources_[ids2[i]].meta_->sun_azimuth_ << " elev: " << resources_[ids2[i]].meta_->sun_elevation_ << " res: " << resources_[ids2[i]].meta_->gsd_ << std::endl;
431       std::cout << resources_[ids2[j]].name_ << ": ";
432       resources_[ids2[j]].meta_->print_time();
433       std::cout << " azi: " << resources_[ids2[j]].meta_->sun_azimuth_ << " elev: " << resources_[ids2[j]].meta_->sun_elevation_ << " res: " << resources_[ids2[j]].meta_->gsd_ << std::endl;
434 
435       unsigned time_dif = resources_[ids2[i]].meta_->time_minute_dif(*(resources_[ids2[j]].meta_));
436       if (time_dif > 0 && time_dif < 5) { // if taken less than 5 minute apart
437         //ids.push_back(std::pair<unsigned, unsigned>(ids2[i], ids2[j]));
438         // check if one of the images have already been pushed
439         bool already_added = false;
440         for (auto & id : ids) {
441           if (resources_[ids2[i]].meta_->same_time(*(resources_[id.first].meta_)) ||
442               resources_[ids2[i]].meta_->same_time(*(resources_[id.second].meta_))) {
443                 already_added = true;
444                 break;
445           }
446         }
447         if (!already_added) {
448           ids.emplace_back(ids2[i], ids2[j]);
449           std::cout << "\t\t !!!!!! PUSHED! time dif: " << time_dif << " mins within threshold [1, 5] mins!\n";
450         }
451       }
452       std::cout << "\n";
453       }
454     }
455   return ids.size();
456 }
457 
458 //: query the resources in the given box and output the full paths of pairs to the given file
query_pairs_print_to_file(double lower_left_lon,double lower_left_lat,double upper_right_lon,double upper_right_lat,float GSD_thres,unsigned & cnt,std::string & out_file,std::string & sat_name)459 bool volm_satellite_resources::query_pairs_print_to_file(double lower_left_lon, double lower_left_lat, double upper_right_lon, double upper_right_lat, float GSD_thres, unsigned& cnt, std::string& out_file, std::string& sat_name)
460 {
461   std::vector<std::pair<unsigned, unsigned> > ids;
462   cnt = query_pairs(lower_left_lon, lower_left_lat, upper_right_lon, upper_right_lat, sat_name, GSD_thres, ids);
463 
464   std::ofstream ofs(out_file.c_str());
465   if (!ofs) {
466     std::cerr << "In volm_satellite_resources::query_pairs_print_to_file() -- cannot open file: " << out_file << std::endl;
467     return false;
468   }
469 
470   for (auto & id : ids) {
471     ofs << resources_[id.first].full_path_ << '\n';
472     ofs << resources_[id.second].full_path_ << "\n\n";
473   }
474 
475   ofs.close();
476   return true;
477 }
478 
479 //: return the full path of a satellite image given its name, if not found returns empty string
full_path(const std::string & name)480 std::pair<std::string, std::string> volm_satellite_resources::full_path(const std::string& name)
481 {
482   for (auto & resource : resources_) {
483     if (name.compare(resource.name_) == 0) {
484       std::pair<std::string, std::string> p(resource.full_path_, resource.meta_->satellite_name_);
485       return p;
486     }
487   }
488   return std::pair<std::string, std::string>("", "");
489 }
490 
find_pair(std::string const & name,double const & tol)491 std::string volm_satellite_resources::find_pair(std::string const& name, double const& tol)
492 {
493   for (auto & resource : resources_) {
494     if (name.compare(resource.name_) == 0) {
495       if (resource.pair_.compare("") == 0) {  // not yet found
496         // find all the resources that overlaps with this one
497         std::string band_str = resource.meta_->band_;
498         std::string other_band;
499         if (band_str.compare("PAN") == 0)
500           other_band = "MULTI";
501         else
502           other_band = "PAN";
503         std::vector<unsigned> ids;
504         this->query(resource.meta_->lower_left_.x(),
505                     resource.meta_->lower_left_.y(),
506                     resource.meta_->upper_right_.x(),
507                     resource.meta_->upper_right_.y(), other_band, ids,10.0);  // pass gsd_thres very high, only interested in finding all the images
508         std::cout << " there are " << ids.size() << " resources that cover the image!\n";
509         for (unsigned int ii : ids) {
510           if (resource.meta_->satellite_name_.compare(resources_[ii].meta_->satellite_name_) == 0 &&  // same satellite good!
511               this->same_time(resource, resources_[ii]) &&
512               this->same_view(resource, resources_[ii]) &&
513               this->same_extent(resource, resources_[ii], tol) )
514           {
515                   resource.pair_ = resources_[ii].name_;
516                   resources_[ii].pair_ = resource.name_;
517                 return resources_[ii].name_;
518           }
519         }
520       } else
521         return resource.pair_;
522      break;
523     }
524   }
525   return "";
526 }
527 
528 void
convert_to_global_footprints(std::vector<vgl_polygon<double>> & footprints,const vpgl_lvcs_sptr & lvcs,const std::vector<vgl_polygon<double>> & lvcs_footprints,float downsample_factor)529 volm_satellite_resources::convert_to_global_footprints(std::vector<vgl_polygon<double> >& footprints, const vpgl_lvcs_sptr& lvcs,
530   const std::vector<vgl_polygon<double> >& lvcs_footprints, float downsample_factor)
531 {
532   footprints = lvcs_footprints;
533 
534   // rescale by GSD
535   for(auto & footprint : footprints) {
536     assert(footprint.num_sheets() == 1);
537     for (auto & p : footprint[0]) {
538       p.x() *= downsample_factor;
539       p.y() *= downsample_factor;
540     }
541   }
542 
543   // convert to global lon/lat coordinate system
544   for(auto & footprint : footprints) {
545     assert(footprint.num_sheets() == 1);
546     for (auto & p : footprint[0]) {
547       double x = p.x();
548       double y = p.y();
549       double lon,lat,gz;
550       lvcs->local_to_global(x, y, 0,
551                             lvcs->get_cs_name(), // this is output global cs
552                             lon, lat, gz,
553                             lvcs->geo_angle_unit(), lvcs->local_length_unit());
554       p = vgl_point_2d<double>(lon,lat);
555     }
556   }
557 }
558 
559 void
convert_to_local_footprints(vpgl_lvcs_sptr & lvcs,std::vector<vgl_polygon<double>> & lvcs_footprints,const std::vector<vgl_polygon<double>> & footprints,float downsample_factor)560 volm_satellite_resources::convert_to_local_footprints(vpgl_lvcs_sptr& lvcs, std::vector<vgl_polygon<double> >& lvcs_footprints,
561   const std::vector<vgl_polygon<double> >& footprints, float downsample_factor)
562 {
563   lvcs_footprints.clear();
564 
565   // if no lvcs was provided, create one
566   if(!lvcs) {
567     // find the lower-left lat/lon
568     double min_lon=181.0, min_lat=91.0;
569     for(const auto & footprint : footprints) {
570       assert(footprint.num_sheets() == 1);
571       for (auto p : footprint[0]) {
572         min_lon = std::min(p.x(), min_lon);
573         min_lat = std::min(p.y(), min_lat);
574       }
575     }
576 
577     // create an lvcs coordinate system
578     lvcs = new vpgl_lvcs(min_lat, min_lon, 0, vpgl_lvcs::str_to_enum("wgs84"), vpgl_lvcs::DEG, vpgl_lvcs::METERS);
579   }
580 
581   // convert to the lvcs coordinate system
582   //std::vector<vgl_polygon<double> > lvcs_footprints;
583   for(const auto & footprint : footprints) {
584     vgl_polygon<double> lvcs_footprint(1);
585     assert(footprint.num_sheets() == 1);
586     for (auto p : footprint[0]) {
587       double lon = p.x();
588       double lat = p.y();
589       double x,y,z;
590       lvcs->global_to_local(lon, lat, 0, lvcs->get_cs_name(), x, y, z, lvcs->geo_angle_unit(), lvcs->local_length_unit());
591       lvcs_footprint.push_back(x,y);
592     }
593     lvcs_footprints.push_back(lvcs_footprint);
594   }
595 
596   // rescale by GSD
597   for(auto & footprint : lvcs_footprints) {
598     assert(footprint.num_sheets() == 1);
599     for (auto & p : footprint[0]) {
600       p.x() /= downsample_factor;
601       p.y() /= downsample_factor;
602     }
603   }
604 }
605 
606 // this is creating a raster, so ensure the union of the footprints does not cover a huge area
607 void
compute_footprints_heatmap(vil_image_view<unsigned> & heatmap,vgl_box_2d<double> & image_window,const std::vector<vgl_polygon<double>> & footprints)608 volm_satellite_resources::compute_footprints_heatmap(vil_image_view<unsigned>& heatmap, vgl_box_2d<double>& image_window,
609   const std::vector<vgl_polygon<double> >& footprints)
610 {
611   // find the lower-left & upper-right coordinates to create the image region
612   double min_x, min_y, max_x, max_y;
613   if(footprints.size() > 0 && footprints[0].num_vertices() > 0) { // not the most ideal way to initilize this...
614     const vgl_polygon<double>& footprint = footprints[0];
615 
616     unsigned i = 0;
617     while(footprint[i].size() == 0) ++i;
618     min_x = max_x = footprint[i][0].x();
619     min_y = max_y = footprint[i][0].y();
620   }
621 
622   for(const auto & footprint : footprints) {
623     assert(footprint.num_sheets() == 1);
624     for (auto p : footprint[0]) {
625       min_x = std::min(p.x(), min_x);
626       min_y = std::min(p.y(), min_y);
627       max_x = std::max(p.x(), max_x);
628       max_y = std::max(p.y(), max_y);
629     }
630   }
631   std::cout << "max_x,max_y,max_x,max_y: " << min_x << "," << min_y << "," << max_x << "," << max_y << std::endl;
632 
633   unsigned ncols = (int)std::ceil(max_x - min_x);
634   unsigned nrows = (int)std::ceil(max_y - min_y);
635 
636   // create the heatmap of intersecting polygons
637   assert(ncols != 0 && nrows != 0);
638   heatmap.set_size(ncols, nrows, 1);
639   heatmap.fill(0);
640   // define window as image size to prevent out-of-range in case of bad polygons
641   image_window = vgl_box_2d<double>(min_x, max_x, min_y, max_y);
642   vil_image_view<bool> object_mask;
643   for(const auto & footprint : footprints) {
644     rasterize(image_window, footprint, object_mask);
645     vil_math_image_sum(heatmap, object_mask, heatmap);
646   }
647 
648 #ifdef DEBUG_HIGHLY_OVERLAPPING_SAT_RESOURCES
649   vil_image_view<vxl_byte> dest;
650   vil_convert_stretch_range(heatmap, dest);
651   vil_save(dest, "./heatmap.png");
652 #endif
653 }
654 
655 template<class T> struct index_cmp {
index_cmpindex_cmp656   index_cmp(const T arr) : arr(arr) {}
operator ()index_cmp657   bool operator()(const size_t a, const size_t b) const { return arr[a] > arr[b]; }
658   const T arr;
659 };
660 
661 void
query_resources(std::vector<vgl_polygon<double>> & footprints,std::vector<unsigned> & footprint_ids,const volm_satellite_resources_sptr & res,const std::string & kml_file,const std::string & band,double gsd_thres)662 volm_satellite_resources::query_resources(std::vector<vgl_polygon<double> >& footprints, std::vector<unsigned>& footprint_ids,
663   const volm_satellite_resources_sptr& res, const std::string& kml_file, const std::string& band, double gsd_thres)
664 {
665   // parse the polygon and construct its bounding box
666   if (!vul_file::exists(kml_file)) {
667     std::cout << "can not find input kml file: " << kml_file << std::endl;
668     return;
669   }
670   vgl_polygon<double> poly = bkml_parser::parse_polygon(kml_file);
671   vgl_box_2d<double> bbox;
672   for (auto i : poly[0])
673     bbox.add(i);
674   double lower_left_lon  = bbox.min_x();
675   double lower_left_lat  = bbox.min_y();
676   double upper_right_lon = bbox.max_x();
677   double upper_right_lat = bbox.max_y();
678   std::vector<unsigned> ids;
679   res->query(lower_left_lon, lower_left_lat, upper_right_lon, upper_right_lat, band, ids, gsd_thres);
680 
681   // eliminate the repeating ids (maintaining original order), more than one leaf may contain the same resource
682   // this mangles the expected order...
683   //std::set<unsigned> s(ids.begin(),ids.end());
684   //ids.assign(s.begin(), s.end());
685   std::vector<unsigned>& deduped_ids = footprint_ids;
686   for (unsigned i = 0; i < ids.size(); i++) {
687     bool contains = false;
688     for (unsigned j = i+1; j < ids.size(); j++) {
689       if (ids[i] == ids[j]) {
690         contains = true;
691         break;
692       }
693     }
694     if (!contains) {
695       deduped_ids.push_back(ids[i]);
696     }
697   }
698 
699   // filter footprints
700   for(unsigned int deduped_id : deduped_ids) {
701     footprints.push_back(resources_[deduped_id].meta_->footprint_);
702     //std::cout << resources_[deduped_ids[res_id]].name_ << std::endl;
703   }
704   std::cout << "footprints: " << footprints.size() << std::endl;
705 }
706 
707 void
highly_overlapping_resources(std::vector<std::string> & overlapping_res,const volm_satellite_resources_sptr & res,const std::string & kml_file,float downsample_factor,const std::string & band,double gsd_thres)708 volm_satellite_resources::highly_overlapping_resources(std::vector<std::string>& overlapping_res, const volm_satellite_resources_sptr& res,
709   const std::string& kml_file, float downsample_factor, const std::string& band, double gsd_thres)
710 {
711   // query_resources
712   std::vector<vgl_polygon<double> > footprints;
713   std::vector<unsigned> resource_ids;
714   query_resources(footprints, resource_ids, res, kml_file, band, gsd_thres);
715 
716   std::vector<unsigned> filtered_ids;
717   res->highly_overlapping_resources(filtered_ids, footprints, downsample_factor);
718   //for(int i=0; i < filtered_ids.size(); ++i) {
719   //  std::cout << filtered_ids[i] << ",";
720   //}
721   //std::cout << std::endl;
722 
723   for(unsigned int filtered_id : filtered_ids) {
724     overlapping_res.push_back(resources_[resource_ids[filtered_id]].full_path_);
725   }
726 }
727 
728 void
highly_overlapping_resources(std::vector<unsigned> & overlapping_ids,const std::vector<vgl_polygon<double>> & footprints,float downsample_factor)729 volm_satellite_resources::highly_overlapping_resources(std::vector<unsigned>& overlapping_ids,
730   const std::vector<vgl_polygon<double> >& footprints, float downsample_factor)
731 {
732   // be careful with coordinate transforms
733 
734   // convert to local coordinates
735   vpgl_lvcs_sptr lvcs;
736   std::vector<vgl_polygon<double> > lvcs_footprints;
737   convert_to_local_footprints(lvcs, lvcs_footprints, footprints, downsample_factor);
738 
739   // compute the heatmap of the resource (nitf) footprints
740   vil_image_view<unsigned> heatmap;
741   vgl_box_2d<double> lvcs_window;
742   compute_footprints_heatmap(heatmap, lvcs_window, lvcs_footprints);
743   unsigned int nrows = heatmap.nj();
744   unsigned int ncols = heatmap.ni();
745 
746 
747   // compute the set of highly overlapping regions
748   float best_score = 0;
749   unsigned best_nimages = 0;
750   vgl_polygon<double> best_region_hull;
751 
752   vil_image_view<bool> mask;
753   for(unsigned nimages=1; nimages < lvcs_footprints.size(); ++nimages) {
754     // threshold image
755     vil_threshold_above(heatmap, mask, nimages);
756 //#define DEBUG_HIGHLY_OVERLAPPING_SAT_RESOURCES
757 #ifdef DEBUG_HIGHLY_OVERLAPPING_SAT_RESOURCES
758     vil_image_view<vxl_byte> dest;
759     vil_convert_stretch_range(mask, dest);
760     char path[256];
761     std::sprintf(path, "./masks/mask_%u.png", nimages);
762     vil_save(dest, path);
763 #endif
764     // find regions in the mask
765     vil_image_view<bool> to_process;
766     to_process.deep_copy(mask);
767     // traverse image, looking for pixels in regions yet to process
768     for (unsigned int j=0; j<nrows; ++j) {
769       for (unsigned int i=0; i<ncols; ++i) {
770         if (!to_process(i,j)) {
771           continue;
772         }
773         // found a new region - extract all connected pixels
774         // (i'm using vil_region_finder a little differently than it seems it was
775         // designed; i'll keep track of what to process externally and use the
776         // boolean_region_image as the binary mask).
777         vil_region_finder<bool> region_finder(mask, vil_region_finder_4_conn);
778         std::vector<unsigned> ri,rj;
779         region_finder.same_int_region(i,j,ri,rj);
780         // get the binary mask
781         vil_image_view<bool> region_mask;
782         region_mask = region_finder.boolean_region_image();
783         // mark all pixels in region as processed and compute mean score
784         for (std::vector<unsigned>::const_iterator rj_it=rj.begin(), ri_it=ri.begin();
785             rj_it!=rj.end(); ++rj_it, ++ri_it) {
786           to_process(*ri_it, *rj_it) = false;
787         }
788         //const unsigned region_mask_area = ri.size();
789 
790         // compute the convex hull of each region
791         vgl_polygon<double> region_hull = calculate_convex_hull(region_mask);
792 
793         // compute a score for each region based on its density and the number of contributing images
794         double compactness_score = compactness(region_hull, region_mask) * nimages*nimages;
795         //std::cout << compactness_score << std::endl;
796 
797         // find the best score
798         if(compactness_score > best_score) {
799           best_score = compactness_score;
800           best_region_hull = region_hull;
801           //std::cout << best_region_hull << std::endl;
802         }
803       }
804     }
805   }
806 
807 
808   // find the image ids associated with the best scoring region
809   //std::cout << "at least " << best_nimages << " footprints intersecting" << std::endl;
810   //std::cout << best_region_hull << std::endl;
811 
812   //vgl_point_2d<double> centroid = vgl_centroid(best_region_hull);
813   // map image coordinates to lvcs coordinates
814   //vgl_point_2d<double> lvcs_centroid(centroid.x()-lvcs_window.min_x(), centroid.y()-lvcs_window.min_y());
815   //std::cout << lvcs_centroid.x() << "," << lvcs_centroid.y() << std::endl;
816   // map lvcs coordinates to global coordinates
817   //double lon, lat, gz;
818   //lvcs->local_to_global(lvcs_centroid.x()*downsample_factor, lvcs_centroid.y()*downsample_factor, 0,
819   //                      lvcs->get_cs_name(), // this is output global cs
820   //                      lon, lat, gz,
821   //                      lvcs->geo_angle_unit(), lvcs->local_length_unit());
822   //
823   //for(unsigned i=0; i < lvcs_footprints.size(); ++i) {
824   //  vgl_polygon<double>& footprint = lvcs_footprints[i];
825   //
826   //  if(footprint.contains(lvcs_centroid)) {
827   //    //std::cout << footprint << std::endl;
828   //    overlapping_ids.push_back(i);
829   //  }
830   //}
831 
832   // unfortunately, there is no function to compute the intersection of two polygons, or even the
833   // intersection of a polygon and a box (there is a test for this, but it returns true/false, not
834   // a polygon); so instead, i rasterize...
835   // RE: not true anymore
836   vil_image_view<bool> best_region_mask;
837   rasterize(lvcs_window, best_region_hull, best_region_mask);
838   unsigned int best_region_mask_area;
839   vil_math_sum(best_region_mask_area, best_region_mask, 0);
840 
841   std::vector<double> covered_areas;
842   for(auto & footprint : lvcs_footprints) {
843     vil_image_view<bool> mask;
844     rasterize(lvcs_window, footprint, mask);
845 
846     vil_image_view<bool> intersection_mask;
847     vil_math_image_product(best_region_mask, mask, intersection_mask);
848     unsigned int intersection_mask_area;
849     vil_math_sum(intersection_mask_area, intersection_mask, 0);
850     covered_areas.push_back(intersection_mask_area/(double)best_region_mask_area);
851   }
852   overlapping_ids.resize(covered_areas.size());
853   for(size_t i=0; i < overlapping_ids.size(); ++i) { overlapping_ids[i] = i; }
854   std::sort(overlapping_ids.begin(), overlapping_ids.end(), index_cmp<std::vector<double>&>(covered_areas));
855 
856   double best_area = covered_areas[overlapping_ids[0]];
857   unsigned i=3;
858   for( ; i<covered_areas.size(); ++i) {
859     if(covered_areas[overlapping_ids[i]] < best_area*.95) {
860       break;
861     }
862   }
863   overlapping_ids.erase(overlapping_ids.begin()+i,overlapping_ids.end());
864 
865 
866   // map lvcs coordinates to global coordinates
867   //std::vector<vgl_polygon<double> > footprints;
868   //convert_to_global_footprints(footprints, lvcs, lvcs_footprints, downsample_factor);
869 }
870 
871 void
rasterize(const vgl_polygon<double> & bounds,vil_image_view<bool> & mask)872 volm_satellite_resources::rasterize(
873     const vgl_polygon<double> &bounds, vil_image_view<bool> &mask)
874 {
875   double min_x, max_x, min_y, max_y;
876   for (unsigned int s=0; s < bounds.num_sheets(); ++s) {
877     for (unsigned int p=0; p < bounds[s].size(); ++p) {
878       if(s==0 && p==0) { // not the most ideal way to initilize this...
879         min_x = max_x = bounds[0][0].x();
880         min_y = max_y = bounds[0][0].y();
881       }
882 
883       min_x = std::min(bounds[s][p].x(), min_x);
884       min_y = std::min(bounds[s][p].y(), min_y);
885       max_x = std::max(bounds[s][p].x(), max_x);
886       max_y = std::max(bounds[s][p].y(), max_y);
887     }
888   }
889 
890   vgl_box_2d<double> window(min_x, max_x, min_y, max_y);
891 
892   rasterize(window, bounds, mask);
893 }
894 
895 void
rasterize(const vgl_box_2d<double> & bbox_clipped,const vgl_polygon<double> & bounds,vil_image_view<bool> & mask)896 volm_satellite_resources::rasterize(
897     const vgl_box_2d<double> &bbox_clipped, const vgl_polygon<double> &bounds, vil_image_view<bool> &mask)
898 {
899   if(mask.ni() != bbox_clipped.width() && mask.nj() != bbox_clipped.height() && mask.nplanes() != 1) {
900     assert(!"mask not formated properly");
901   } else {
902     mask.set_size((int)std::ceil(bbox_clipped.width()), (int)std::ceil(bbox_clipped.height()), 1);
903     mask.fill(false);
904   }
905   // NOTE sgr - there is a bug/instability in vgl_polygon_scan_iterator that sometimes
906   // causes a scanline of pixels outside the convex hull to be included in the mask.
907   // setting this to false avoids the bug
908   const bool include_boundary = false;
909   vgl_polygon_scan_iterator<double> polyIter(bounds, include_boundary, bbox_clipped);
910   for (polyIter.reset(); polyIter.next(); ) {
911     // Y position in the mask needs to be relativized
912     int y_shifted = polyIter.scany() - bbox_clipped.min_y();
913     for (int x = polyIter.startx(); x <= polyIter.endx(); x++) {
914       int x_shifted = x - bbox_clipped.min_x();
915       mask(x_shifted, y_shifted) = true;
916     }
917   }
918 }
919 
920 double
compactness(vgl_polygon<double> const & poly,vil_image_view<bool> const & mask)921 volm_satellite_resources::compactness(vgl_polygon<double> const&poly, vil_image_view<bool> const& mask)
922 {
923   double region_hull_area = vgl_area(poly);
924   if(region_hull_area == 0) return 0.0;
925 
926   unsigned int region_mask_area;
927   vil_math_sum(region_mask_area, mask, 0);
928 
929   // region_mask_area <= region_hull_area
930   double compactness_score = region_hull_area * (region_mask_area / region_hull_area);
931 
932   return compactness_score;
933 }
934 
935 vgl_polygon<double>
calculate_convex_hull(vil_image_view<bool> const & mask,unsigned off_x,unsigned off_y)936 volm_satellite_resources::calculate_convex_hull(vil_image_view<bool> const& mask, unsigned off_x, unsigned off_y)
937 {
938   const unsigned ni = mask.ni();
939   const unsigned nj = mask.nj();
940 
941   // determine the boundary points (can't remember why I do this...)
942   vil_structuring_element strel;
943   strel.set_to_disk(1.1); // set radius to 1.1 to make sure pixels dist 1 away are included
944   vil_image_view<bool> mask_eroded(ni,nj);
945   // NOTE the erode operation could remove (very) thin elements
946   vil_binary_erode(mask, mask_eroded, strel, vil_border_create_constant(mask, false));
947 
948   // find non-zero points
949   std::vector<vgl_point_2d<double> > boundary_pts;
950   for (unsigned bj=0; bj<nj; ++bj) {
951     for (unsigned bi=0; bi<ni; ++bi) {
952       if (mask(bi,bj) && !mask_eroded(bi,bj)) {
953         boundary_pts.emplace_back((double)bi + off_x,(double)bj + off_y);
954       }
955     }
956   }
957   if (boundary_pts.size() < 3) {
958     //std::cerr << "boundary points < 3" << std::endl;
959     assert(boundary_pts.size() > 0);
960     // if not debugging, just return empty polygon
961     return vgl_polygon<double>();
962   }
963   // compute convex hull
964   vgl_convex_hull_2d<double> compute_hull(boundary_pts);
965   vgl_polygon<double> poly = compute_hull.hull();
966 
967   return poly;
968 }
969 
970 // from http://www.drdobbs.com/the-standard-librarian-defining-iterato/184401331?pgno=3 and
971 // http://www.cs.helsinki.fi/u/tpkarkka/alglib/k06/lectures/iterators.html#example-simple-list-continued
972 // with modifications
973 class CombinatorGenerator
974 {
975 public:
CombinatorGenerator(unsigned N,unsigned K)976   CombinatorGenerator(unsigned N, unsigned K) : N(N), K(K) { }
977 
978 private:
979   struct Combinator
980   {
CombinatorCombinatorGenerator::Combinator981     Combinator(unsigned N=0, unsigned K=0) : N(N), K(K) {
982       bitmask = std::string(K,1); // K leading 1's
983       bitmask.resize(N,0);       // N-K trailing 0's
984       for (unsigned i = 0; i < N; ++i) { // [0..N-1] integers
985         if (bitmask[i]) { inds.push_back(i); }
986       }
987     }
988 
operator ==CombinatorGenerator::Combinator989     bool operator==(const Combinator& other) const {
990       return bitmask == other.bitmask; // inds is derived from bitmask
991     }
operator !=CombinatorGenerator::Combinator992     bool operator!=(const Combinator& other) const {
993       return !(this == &other);
994     }
operator ++CombinatorGenerator::Combinator995     Combinator& operator++() {
996       if(!std::prev_permutation(bitmask.begin(), bitmask.end())) {
997         *this = Combinator(); // null-object pattern
998         return *this;
999       }
1000 
1001       inds.clear();
1002       for (unsigned i = 0; i < N; ++i) {
1003         if (bitmask[i]) { inds.push_back(i); }
1004       }
1005       return *this;
1006     }
1007 
1008     std::vector<unsigned> inds;
1009   private:
1010     unsigned N, K;
1011     std::string bitmask;
1012   };
1013 
1014   struct Iterator : std::iterator<std::forward_iterator_tag, std::vector<unsigned> >
1015   {
1016     Combinator asg_;
1017 
1018     typedef Iterator self_type;
1019 
IteratorCombinatorGenerator::Iterator1020     explicit Iterator(Combinator asg) : asg_(std::move(asg)) { }
1021     // implicit copy constructor, copy assignment and destructor
operator *CombinatorGenerator::Iterator1022     reference operator*() { return asg_.inds; }
operator ->CombinatorGenerator::Iterator1023     pointer operator->() { return &*(*this); }
operator ++CombinatorGenerator::Iterator1024     self_type& operator++() { ++asg_; return *this; }
operator ++CombinatorGenerator::Iterator1025     self_type operator++(int  /*junk*/) { self_type i = *this; ++asg_; return i; }
operator ==CombinatorGenerator::Iterator1026     bool operator==(const self_type& rhs) const { return asg_ == rhs.asg_; }
operator !=CombinatorGenerator::Iterator1027     bool operator!=(const self_type& rhs) const { return !(*this == rhs); }
1028   };
1029 
1030   struct ConstIterator : std::iterator<std::forward_iterator_tag, std::vector<unsigned>,
1031       std::ptrdiff_t, const std::vector<unsigned>*, const std::vector<unsigned>& >
1032   {
1033     Combinator asg_;
1034 
1035     typedef ConstIterator self_type;
1036 
ConstIteratorCombinatorGenerator::ConstIterator1037     explicit ConstIterator(Combinator asg) : asg_(std::move(asg)) { }
1038     // implicit copy constructor, copy assignment and destructor
1039     // a non-const object calling begin() will return an iterator. implicitly
1040     // convert it if a const_iterator is desired
ConstIteratorCombinatorGenerator::ConstIterator1041     ConstIterator(const Iterator& i) : asg_(i.asg_) { }
operator *CombinatorGenerator::ConstIterator1042     reference operator*() { return asg_.inds; }
operator ->CombinatorGenerator::ConstIterator1043     pointer operator->() { return &*(*this); }
operator ++CombinatorGenerator::ConstIterator1044     self_type& operator++() { ++asg_; return *this; }
operator ++CombinatorGenerator::ConstIterator1045     self_type operator++(int  /*junk*/) { self_type i = *this; ++asg_; return i; }
operator ==CombinatorGenerator::ConstIterator1046     bool operator==(const self_type& rhs) const { return asg_ == rhs.asg_; }
operator !=CombinatorGenerator::ConstIterator1047     bool operator!=(const self_type& rhs) const { return !(*this == rhs); }
1048   };
1049 
1050 public:
begin()1051   Iterator begin() {
1052     return Iterator(Combinator(N,K));
1053   }
1054 
end()1055   Iterator end() {
1056     return Iterator(Combinator());
1057   }
1058 
begin() const1059   ConstIterator begin() const {
1060     return ConstIterator(Combinator(N,K));
1061   }
1062 
end() const1063   ConstIterator end() const {
1064     return ConstIterator(Combinator());
1065   }
1066 
1067 public:
1068   typedef Iterator iterator;
1069   typedef ConstIterator const_iterator;
1070 
1071 private:
1072   unsigned N, K;
1073 };
1074 
1075 void
highly_intersecting_resources(std::vector<std::string> & overlapping_res,const volm_satellite_resources_sptr & res,const std::string & kml_file,int k,int l,const std::string & band,double gsd_thres)1076 volm_satellite_resources::highly_intersecting_resources(std::vector<std::string>& overlapping_res, const volm_satellite_resources_sptr& res,
1077   const std::string& kml_file, int k, int l, const std::string& band, double gsd_thres)
1078 {
1079   // query_resources
1080   std::vector<vgl_polygon<double> > footprints;
1081   std::vector<unsigned> resource_ids;
1082   query_resources(footprints, resource_ids, res, kml_file, band, gsd_thres);
1083 
1084   // convert to local coordinates
1085   vpgl_lvcs_sptr lvcs;
1086   std::vector<vgl_polygon<double> > lvcs_footprints;
1087   convert_to_local_footprints(lvcs, lvcs_footprints, footprints);
1088 
1089   std::vector<unsigned> filtered_ids;
1090   res->highly_intersecting_resources(filtered_ids, lvcs_footprints, k, l);
1091   //for(int i=0; i < filtered_ids.size(); ++i) {
1092   //  std::cout << filtered_ids[i] << ",";
1093   //}
1094   //std::cout << std::endl;
1095 
1096   for(unsigned int filtered_id : filtered_ids) {
1097     overlapping_res.push_back(resources_[resource_ids[filtered_id]].full_path_);
1098   }
1099 }
1100 
1101 void
highly_intersecting_resources(std::vector<unsigned> & overlapping_ids,const std::vector<vgl_polygon<double>> & footprints,unsigned k,unsigned l)1102 volm_satellite_resources::highly_intersecting_resources(std::vector<unsigned>& overlapping_ids,
1103   const std::vector<vgl_polygon<double> >& footprints, unsigned k, unsigned l)
1104 {
1105   double max_area = 0.0;
1106   std::list<std::pair<std::vector<unsigned>, double> > areas;
1107   // could be memory inefficient; TODO prune
1108   // RE only need to keep the previous level of indices (len(inds)-1)
1109   std::map<std::vector<unsigned>, vgl_polygon<double> > cache;
1110 
1111   unsigned n = footprints.size();
1112 
1113   // compute the rising powerset from k to l
1114   for(unsigned kk=k; kk < l; ++kk) {
1115     CombinatorGenerator combs(n, kk);
1116 
1117     CombinatorGenerator::const_iterator it=combs.begin();
1118     CombinatorGenerator::const_iterator end=combs.end();
1119     for( ; it != end; ++it) {
1120       const std::vector<unsigned>& inds = *it;
1121       //for(int i=0; i < inds.size(); ++i)
1122       //  std::cout << inds[i] << " ";
1123       //std::cout << std::endl;
1124 
1125       if(inds.size() < 2) {
1126         continue;
1127       }
1128       unsigned i = 0;
1129       vgl_polygon<double> intersection = footprints[inds[0]];
1130       if(inds.size() == 2) {
1131         i = 1;
1132       }
1133       else if(inds.size() <= kk) {
1134         for(i=inds.size()-2; i>0; --i) { // full sub-index cannot already be processed
1135           std::vector<unsigned> sub_inds(&inds[0], &inds[i+1]);
1136           if(cache.find(sub_inds) != cache.end()) {
1137             intersection = cache[sub_inds];
1138             i++;
1139             break;
1140           }
1141         }
1142       }
1143       else { // must have k+1 footprints to intersect
1144         i = inds.size()-2;
1145         std::vector<unsigned> sub_inds(&inds[0], &inds[i+1]);
1146         if(cache.find(sub_inds) != cache.end()) {
1147           intersection = cache[sub_inds];
1148           i++;
1149         }
1150         // if the sub-index is not in the cache, it is because the intersection area was
1151         // smaller than already observed, so just continue
1152         else {
1153           continue;
1154         }
1155       }
1156 
1157       double area = 0;
1158       for(unsigned j=i; j < inds.size(); ++j) {
1159         unsigned ind = inds[j];
1160         // compute the intersection of all images in the set
1161         intersection = vgl_clip( intersection, footprints[ind], vgl_clip_type_intersect );
1162         unsigned nimages = inds.size();
1163         area = vgl_area(intersection)*nimages*nimages;
1164         // if this has a smaller area than we've already seen, just abort
1165         if(area <= max_area) {
1166           break;
1167         }
1168         else {
1169           std::vector<unsigned> sub_inds(&inds[0], &inds[j+1]);
1170           cache[sub_inds] = intersection;
1171         }
1172       }
1173 
1174       // is this a larger area than what we've alread seen
1175       if(area > max_area) {
1176         max_area = area;
1177         areas.emplace_back(inds,area);
1178       }
1179     }
1180   }
1181 
1182 
1183   overlapping_ids = areas.back().first;
1184   //for(std::list<std::pair<std::vector<unsigned>, double> >::const_iterator it=areas.begin();
1185   //    it != areas.end(); ++it) {
1186   //  const std::vector<unsigned>& inds = it->first;
1187   //  double area = it->second;
1188   //  std::cout << "(";
1189   //  for(int i=0; i<inds.size(); ++i) {
1190   //    std::cout << inds[i] << ",";
1191   //  }
1192   //  std::cout << "), " << area << std::endl;
1193   //}
1194 }
1195 
same_time(volm_satellite_resource const & res_a,volm_satellite_resource const & res_b,float const & diff_in_sec)1196 bool volm_satellite_resources::same_time(volm_satellite_resource const& res_a, volm_satellite_resource const& res_b, float const& diff_in_sec)
1197 {
1198   if (!res_a.meta_->same_day(*res_b.meta_))
1199     return false;
1200   // calculate the time difference in seconds
1201   unsigned time_min_diff = res_a.meta_->time_minute_dif(*res_b.meta_);
1202   auto second_diff = (unsigned)std::abs(res_a.meta_->t_.sec - res_b.meta_->t_.sec);
1203   float time_second_diff = second_diff + 60.0 * time_min_diff;
1204   if (time_second_diff < diff_in_sec) {
1205     return true;
1206   }
1207   return false;
1208 }
1209 
1210 // compare the viewing elevation and azimuth angle of two satellite image
same_view(volm_satellite_resource const & res_a,volm_satellite_resource const & res_b,double const & tol)1211 bool volm_satellite_resources::same_view(volm_satellite_resource const& res_a, volm_satellite_resource const& res_b, double const & tol)
1212 {
1213   if (std::abs(res_a.meta_->view_elevation_ - res_b.meta_->view_elevation_) > tol)
1214     return false;
1215   if (std::abs(res_a.meta_->view_azimuth_ - res_b.meta_->view_azimuth_) > tol)
1216     return false;
1217   return true;
1218 }
1219 
1220 // compare the lat, lon bounding boxes to check how close of the footprint of two satellite resources
same_extent(volm_satellite_resource const & res_a,volm_satellite_resource const & res_b,double const & tol)1221 bool volm_satellite_resources::same_extent(volm_satellite_resource const& res_a, volm_satellite_resource const& res_b, double const& tol)
1222 {
1223   // check by compare lower left corner and upper right corner with a local lvcs
1224   vgl_point_2d<double> ll_a(res_a.meta_->lower_left_.x(),  res_a.meta_->lower_left_.y());
1225   vgl_point_2d<double> ur_a(res_a.meta_->upper_right_.x(), res_a.meta_->upper_right_.y());
1226   vgl_point_2d<double> ll_b(res_b.meta_->lower_left_.x(),  res_b.meta_->lower_left_.y());
1227   vgl_point_2d<double> ur_b(res_b.meta_->upper_right_.x(), res_b.meta_->upper_right_.y());
1228   vpgl_lvcs ll_lvcs(ll_a.y(), ll_a.x(), 0, vpgl_lvcs::wgs84, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
1229   double ll_x, ll_y, ll_z;
1230   ll_lvcs.global_to_local(ll_b.x(), ll_b.y(), 0.0, vpgl_lvcs::wgs84, ll_x, ll_y, ll_z);
1231   double ll_dist = std::sqrt(ll_x*ll_x + ll_y*ll_y);
1232   if (ll_dist > tol) {
1233     return false;
1234   }
1235 
1236   vpgl_lvcs ur_lvcs(ur_a.y(), ur_a.x(), 0, vpgl_lvcs::wgs84, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
1237   double ur_x, ur_y, ur_z;
1238   ur_lvcs.global_to_local(ur_b.x(), ur_b.y(), 0.0, vpgl_lvcs::wgs84, ur_x, ur_y, ur_z);
1239   double ur_dist = std::sqrt(ur_x*ur_x + ur_y*ur_y);
1240   // the footprints are allowed to have 30 meter shift (e.g. PAN and MULTI band)
1241   if (ur_dist > tol) {
1242     return false;
1243   }
1244   return true;
1245 }
1246 
1247 //: binary save self to stream
b_write(vsl_b_ostream & os) const1248 void volm_satellite_resources::b_write(vsl_b_ostream& os) const
1249 {
1250   vsl_b_write(os, version());
1251   vsl_b_write(os, min_size_);
1252   vsl_b_write(os, bbox_.min_x());
1253   vsl_b_write(os, bbox_.min_y());
1254   vsl_b_write(os, bbox_.max_x());
1255   vsl_b_write(os, bbox_.max_y());
1256   vsl_b_write(os, resources_.size());
1257   for (const auto & resource : resources_) {
1258     resource.b_write(os);
1259   }
1260 }
1261 
1262 //: binary load self from stream
b_read(vsl_b_istream & is)1263 void volm_satellite_resources::b_read(vsl_b_istream& is)
1264 {
1265   if (!is) return;
1266   short ver;
1267   vsl_b_read(is, ver);
1268   if (ver == 0) {
1269     vsl_b_read(is, min_size_);
1270     double min_x, min_y, max_x, max_y;
1271     vsl_b_read(is, min_x);
1272     vsl_b_read(is, min_y);
1273     vsl_b_read(is, max_x);
1274     vsl_b_read(is, max_y);
1275     bbox_ = vgl_box_2d<double>(min_x, max_x, min_y, max_y);
1276     unsigned size;
1277     vsl_b_read(is, size);
1278     for (unsigned i = 0; i < size; i++) {
1279       volm_satellite_resource r;
1280       r.b_read(is);
1281       resources_.push_back(r);
1282     }
1283     this->construct_tree();
1284     this->add_resources(0, resources_.size());
1285   }
1286   else {
1287     std::cout << "volm_satellite_resources -- unknown binary io version " << ver << '\n';
1288     return;
1289   }
1290 }
1291 
1292 //: binary save self to stream
b_write(vsl_b_ostream & os) const1293 void volm_satellite_resource::b_write(vsl_b_ostream& os) const
1294 {
1295   vsl_b_write(os, version());
1296   vsl_b_write(os, full_path_);
1297   vsl_b_write(os, name_);
1298   vsl_b_write(os, pair_);
1299   meta_->b_write(os);
1300 }
1301 
1302 //: binary load self from stream
b_read(vsl_b_istream & is)1303 void volm_satellite_resource::b_read(vsl_b_istream& is)
1304 {
1305   if (!is) return;
1306   short ver;
1307   vsl_b_read(is, ver);
1308   if (ver == 0) {
1309     vsl_b_read(is, full_path_);
1310     vsl_b_read(is, name_);
1311     vsl_b_read(is, pair_);
1312     brad_image_metadata meta;
1313     meta.b_read(is);
1314     meta_ = new brad_image_metadata(meta);
1315   }
1316   else {
1317     std::cout << "volm_satellite_resources -- unknown binary io version " << ver << '\n';
1318     return;
1319   }
1320 }
1321 
1322 
1323 //dummy vsl io functions to allow volm_satellite_resources to be inserted into
1324 //brdb as a dbvalue
vsl_b_write(vsl_b_ostream &,volm_satellite_resources const &)1325 void vsl_b_write(vsl_b_ostream &  /*os*/, volm_satellite_resources const & /*tc*/)
1326 { /* do nothing */ }
vsl_b_read(vsl_b_istream &,volm_satellite_resources &)1327 void vsl_b_read(vsl_b_istream &  /*is*/, volm_satellite_resources & /*tc*/)
1328 { /* do nothing */ }
vsl_print_summary(std::ostream &,const volm_satellite_resources &)1329 void vsl_print_summary(std::ostream & /*os*/, const volm_satellite_resources & /*tc*/)
1330 { /* do nothing */ }
vsl_b_read(vsl_b_istream &,volm_satellite_resources *)1331 void vsl_b_read(vsl_b_istream&  /*is*/, volm_satellite_resources*  /*tc*/)
1332 { /* do nothing */ }
vsl_b_write(vsl_b_ostream &,const volm_satellite_resources * &)1333 void vsl_b_write(vsl_b_ostream&  /*os*/, const volm_satellite_resources* & /*tc*/)
1334 { /* do nothing */ }
vsl_print_summary(std::ostream &,const volm_satellite_resources * &)1335 void vsl_print_summary(std::ostream&  /*os*/, const volm_satellite_resources* & /*tc*/)
1336 { /* do nothing */ }
vsl_b_read(vsl_b_istream &,volm_satellite_resources_sptr &)1337 void vsl_b_read(vsl_b_istream&  /*is*/, volm_satellite_resources_sptr&  /*tc*/)
1338 { /* do nothing */ }
vsl_b_write(vsl_b_ostream &,const volm_satellite_resources_sptr &)1339 void vsl_b_write(vsl_b_ostream&  /*os*/, const volm_satellite_resources_sptr & /*tc*/)
1340 { /* do nothing */ }
vsl_print_summary(std::ostream &,const volm_satellite_resources_sptr &)1341 void vsl_print_summary(std::ostream&  /*os*/, const volm_satellite_resources_sptr & /*tc*/)
1342 { /* do nothing */ }
1343 
1344 
1345 // create table with the increments to use during hypotheses generation according to each land type, the unit is in meters
create_satellite_reliability()1346 std::map<std::string, float> create_satellite_reliability()
1347 {
1348   std::map<std::string, float> m;
1349   m["GeoEye-1"]    = 0.7f;  // CE90 is 2 meter
1350   m["WV01"]    = 0.125f;  // CE90 is up to 12 meter
1351   m["WV02"]    = 0.125f;  // CE90 is up to 12 meter
1352   m["QB1"]    = 0.05f;  // CE90 is up to 23 meter
1353   /*m["GeoEye-1"]    = 0.9f;  // CE90 is 2 meter
1354   m["WV01"]    = 0.04f;  // CE90 is up to 12 meter
1355   m["WV02"]    = 0.05f;  // CE90 is up to 12 meter
1356   m["QB1"]    = 0.01f;  // CE90 is up to 23 meter*/
1357   return m;
1358 }
1359 
1360 //: use the corresponding global reliability for each satellite when setting weights for camera correction
1361 std::map<std::string, float> volm_satellite_resources::satellite_geo_reliability = create_satellite_reliability();
1362