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