1 //:
2 // \file
3 // \brief executable to generate hypothesis locations using DEMs or LIDAR
4 // \author Ozge C. Ozcanli
5 // \date Nov 15, 2012
6 
7 #include <volm/volm_io.h>
8 #include <volm/volm_tile.h>
9 #include <volm/volm_loc_hyp.h>
10 #include "vul/vul_arg.h"
11 #include "vul/vul_file.h"
12 #include "vul/vul_file_iterator.h"
13 #include "vil/vil_image_view.h"
14 #include "vil/vil_load.h"
15 #include "vpgl/vpgl_utm.h"
16 #include "vpgl/vpgl_lvcs.h"
17 #include <bkml/bkml_parser.h>
18 #include "vul/vul_timer.h"
19 #include <volm/volm_io_tools.h>
20 #include <volm/volm_category_io.h>
21 #include <volm/volm_geo_index.h>
22 #include <volm/volm_geo_index_sptr.h>
23 
next_mult_2(float val)24 inline float next_mult_2(float val)
25 {
26   return (float)(2.0f*std::ceil(val*0.5f));
27 }
28 
29 // find the elevation of the point and add it to the geo index if found
add_hypo(const volm_geo_index_node_sptr & hyp_root,std::vector<volm_img_info> const & infos,std::vector<volm_img_info> const & class_map_infos,vgl_point_2d<double> const & pt,double inc_in_sec_radius,bool search)30 bool add_hypo(const volm_geo_index_node_sptr& hyp_root, std::vector<volm_img_info> const& infos, std::vector<volm_img_info> const& class_map_infos,
31               vgl_point_2d<double> const& pt, double inc_in_sec_radius, bool search)
32 {
33   // find the elevation of the hypotheses
34   float z = 0;
35   bool found_it = false;
36   for (const auto & info : infos) {
37     if (info.contains(pt)) {
38       double u,v;
39       info.cam->global_to_img(pt.x(), pt.y(), 0, u, v);
40       int uu = (int)std::floor(u+0.5);
41       int vv = (int)std::floor(v+0.5);
42       if (info.valid_pixel(uu,vv)) {
43         // check whether this location is building
44         bool not_building = true;
45         for (const auto & class_map_info : class_map_infos)
46         {
47           if (class_map_info.contains(pt)) {
48             double uc,vc;
49             class_map_info.cam->global_to_img(pt.x(), pt.y(), 0, uc, vc);
50             int uuc = (int)std::floor(uc+0.5);
51             int vvc = (int)std::floor(vc+0.5);
52             if (class_map_info.valid_pixel(uuc,vvc)) {
53               vil_image_view<vxl_byte> imgc(class_map_info.img_r);
54               //std::cout << " loc " << std::setprecision(12) << pt << " is " << volm_osm_category_io::volm_land_table[imgc(uuc,vvc)].name_ << " at pixel "
55               //         << " (" << uuc << "x" << vvc << ") in image " << class_map_infos[mc].name << std::endl;
56               if (imgc(uuc,vvc)==volm_osm_category_io::volm_land_table_name["building"].id_ || imgc(uuc,vvc)==volm_osm_category_io::volm_land_table_name["tall_building"].id_)
57               {
58                 not_building = false;
59                 break;
60               }
61             }
62           }
63         }
64         if (not_building)
65         {
66           //vil_image_view<vxl_int_16> img(infos[mm].img_r);
67           vil_image_view<float> img(info.img_r);
68           z = img(uu,vv);
69           found_it = true;
70           break;
71         }
72       }
73     }
74   }
75   if (found_it) { // add the hypo
76     if (search)
77     {
78       if (!volm_geo_index::exists(hyp_root, pt.y(), pt.x(), inc_in_sec_radius)) {
79         if (z > 0)
80           volm_geo_index::add_hypothesis(hyp_root, pt.x(), pt.y(), z);
81         return true;
82       }
83     }
84     else
85     {
86       if (z > 0)
87         volm_geo_index::add_hypothesis(hyp_root, pt.x(), pt.y(), z);
88       return true;
89     }
90   }
91   return false;
92 }
93 
find_land_type(std::vector<volm_img_info> & geo_infos,vgl_point_2d<double> & pt)94 int find_land_type(std::vector<volm_img_info>& geo_infos, vgl_point_2d<double>& pt)
95 {
96   int type = -1;
97   for (auto & geo_info : geo_infos) {
98      if (geo_info.contains(pt)) {
99       double u,v;
100       geo_info.cam->global_to_img(pt.x(), pt.y(), 0, u, v);
101       int uu = (int)std::floor(u+0.5);
102       int vv = (int)std::floor(v+0.5);
103       if (geo_info.valid_pixel(uu,vv)) {
104         vil_image_view<vxl_byte> img(geo_info.img_r);
105         type = img(uu,vv);
106         break;
107       }
108     }
109   }
110   return type;
111 }
112 
loc_inside_region(vgl_polygon<double> const & poly,double const & ptx,double const & pty)113 bool loc_inside_region(vgl_polygon<double> const& poly, double const& ptx, double const& pty)
114 {
115   // when sheets overlap, the poly.contain method will return false for pts located inside the overlapped region
116   // this function will returns true if the given point is inside any single sheet of the polygon
117   unsigned num_sheet = poly.num_sheets();
118   for (unsigned i = 0; i < num_sheet; i++) {
119     vgl_polygon<double> single_sheet_poly(poly[i]);
120     if (single_sheet_poly.contains(ptx, pty))
121       return true;
122   }
123   return false;
124 }
125 
126 // read the tiles of the region, create a geo index and write the hyps
main(int argc,char ** argv)127 int main(int argc,  char** argv)
128 {
129   vul_timer t;
130   t.mark();
131 
132   vul_arg<std::string> in_folder("-in", "input folder to read files as .tif", "");
133   vul_arg<std::string> in_poly("-poly", "region polygon as kml, only the hypos inside this will be added", "");
134   vul_arg<float> inc("-inc", "increments in arcseconds, e.g. 0.1 for ~3m increments", 0.1f);
135   vul_arg<unsigned> nh("-nh", "number of hyps in each direction in one leaf tile, e.g. 100 so that each tile has 100x100 hyps", 100);
136   vul_arg<std::string> out_pre("-out_pre", "output file folder with file separator at the end", "");
137   vul_arg<std::string> add_gt("-addgt", "add known gt locations? pass the name of txt file containing gt locations", ""); // if no -addgt argument then the value is false, if there is one then the value is true
138   vul_arg<bool> only_gt("-onlygt", "add only known gt locations", false);
139   vul_arg<unsigned> tile_id("-tile", "id of the tile", 0);
140   vul_arg<unsigned> utm_zone("-zone", "utm zone to fill", 17);
141   vul_arg<bool> read("-read", "if passed only read the index in the out_pre() folder and report some statistics", false);
142   vul_arg<bool> use_osm_roads("-osm_roads", "if passed, use the osm binary file to read the roads and generate location hypotheses along them", false);
143   vul_arg<bool> non_building("-non-build","if passed, will generate location on all non-building region", false);
144   vul_arg<std::string> osm_bin_file("-osm_bin", "the binary file that has all the objects", "");
145   vul_arg<std::string> osm_tree_folder("-osm_tree", "the geoindex tree folder that has the tree structure with the ids of osm objects at its leaves", "");
146   vul_arg<std::string> region_name("-region_name", "if passed, will generate constant post on the given region, e.g, sane region, pier region","");
147   vul_arg<int> world_id("-world", "the id of the world", -1);
148   vul_arg<bool> p1b("-p1b", "use the p1b tiles", false);
149   vul_arg<bool> p1a("-p1a", "use the p1a tiles", false);
150   vul_arg<bool> use_satellite_img("-sat", "use satellite image resource", false);
151   vul_arg<std::string> land_class_map_folder("-map", "folder for land classification map with building in the image","");
152   vul_arg<std::string> land("-land", "the input folder of the land type .tif files (geo cover images)", "");
153   vul_arg<std::string> urgent_folder("-urgent", "RUGENT data folder","");
154 
155   vul_arg_parse(argc, argv);
156 
157   std::cout << "argc: " << argc << std::endl;
158   double arcsec_to_sec = 1.0f/3600.0f;
159   double inc_in_sec = inc()*arcsec_to_sec;
160   double inc_in_sec_radius_ratio = 3.0/4.0;
161   double inc_in_sec_rad = inc_in_sec*inc_in_sec_radius_ratio; // radius to search for existence of before adding a new one
162 
163 
164   // generate locations along the OSM road for p1b world regions
165   if (p1b()) {
166 
167     if (out_pre().compare("") == 0 || in_poly().compare("") == 0) {
168       vul_arg_display_usage_and_exit();
169       return volm_io::EXE_ARGUMENT_ERROR;
170     }
171     std::cout << "will use increments " << inc() << " arcseconds along north and east directions!\n";
172 
173     vgl_polygon<double> poly = bkml_parser::parse_polygon(in_poly());
174     std::cout << "outer poly  has: " << poly[0].size() << std::endl;
175     poly.print(std::cout);
176     std::vector<volm_tile> tiles;
177     if (!volm_tile::generate_tiles(world_id(),tiles))
178     {
179       std::cerr << "Unknown world id: " << world_id() << std::endl;
180       return volm_io::EXE_ARGUMENT_ERROR;
181     }
182 
183     std::cout << " number of tiles: " << tiles.size() << std::endl;
184     unsigned i = tile_id();
185     if (i >= tiles.size()) {
186       std::cerr << "Unknown tile id: " << i << std::endl;
187       return volm_io::EXE_ARGUMENT_ERROR;
188     }
189 
190     double meter_to_sec = volm_io_tools::meter_to_seconds(tiles[i].lat_, tiles[i].lon_);
191 
192     float size = 0.05f;  // in seconds, set a fixed size for the leaves
193     volm_geo_index_node_sptr hyp_root = volm_geo_index::construct_tree(tiles[i], (float)size, poly);
194     // write the geo index and the hyps
195     if (region_name().compare("") == 0 && !non_building()) {
196       std::stringstream file_name; file_name << out_pre() << "geo_index_tile_" << i << ".txt";
197       volm_geo_index::write(hyp_root, file_name.str(), (float)size);
198       unsigned depth = volm_geo_index::depth(hyp_root);
199       std::stringstream file_name2; file_name2 << out_pre() << "geo_index_tile_" << i << ".kml";
200       volm_geo_index::write_to_kml(hyp_root, 0, file_name2.str());
201       std::stringstream file_name3; file_name3 << out_pre() << "geo_index_tile_" << i << "_depth_" << depth << ".kml";
202       volm_geo_index::write_to_kml(hyp_root, depth, file_name3.str());
203     }
204 
205     if (only_gt() && add_gt().compare("") != 0) {  // only add the ground truth points in the file as they are given (i.e. with the elevs in the file)
206 
207       // load height map resource to get the height for each ground truth location
208       std::vector<volm_img_info> infos;
209       if (use_satellite_img() )
210         // load the satellite resources
211         volm_io_tools::load_satellite_height_imgs(in_folder(), infos, false, "");
212       else
213         // load the DEM tiles
214         volm_io_tools::load_aster_dem_imgs(in_folder(), infos);
215       if (out_pre().compare("") == 0 || world_id() < 0) {
216         vul_arg_display_usage_and_exit();
217         return volm_io::EXE_ARGUMENT_ERROR;
218       }
219 
220       // load satellite classification map to avoid locations on top of the buildings (can be empty if no class map)
221       std::vector<volm_img_info> class_map_infos;
222       volm_io_tools::load_imgs(land_class_map_folder(), class_map_infos, true, true, true);
223 
224       std::vector<std::pair<vgl_point_3d<double>, std::pair<std::pair<std::string, int>, std::string> > > samples;
225       int cnt = volm_io::read_gt_file(add_gt(), samples);
226       std::cout << " adding " << cnt <<" gt locs!\n";
227       std::cout << " height map resources: " << infos.size() << " images are loaded!\n";
228       std::cout << " land classification resources: " << class_map_infos.size() << " images are loaded!\n";
229 
230       for (int j = 0; j < cnt; ++j) {
231         std::cout << samples[j].second.first.first << " adding.. " << samples[j].first.y() << ", " << samples[j].first.x() << ' ';
232         // obtain the height of the height map resources
233         // check whether the gt_loc is inside roi poly
234         if (poly.contains(samples[j].first.x(), samples[j].first.y()))
235         {
236           bool added = add_hypo(hyp_root, infos, class_map_infos, vgl_point_2d<double>(samples[j].first.x(), samples[j].first.y()), inc_in_sec_rad, true);
237           //bool added = volm_geo_index::add_hypothesis(hyp_root, samples[j].first.x(), samples[j].first.y(), samples[j].first.z());
238           if (added) std::cout << " success!\n";
239           else       std::cout << " not found in tree of tile: " << tile_id() << "!\n";
240         }
241         else
242           std::cout << " not inside the ROI polygons!\n";
243       }
244 
245       unsigned r_cnt = volm_geo_index::hypo_size(hyp_root) ;
246       std::cout << " after addition of gt locs, hyp_root " << i << " has total " << r_cnt << " hypotheses in its leaves!\n";
247 
248       // write the hypos
249       std::stringstream file_name4; file_name4 << out_pre() << "geo_index_tile_" << i;
250       std::cout << "writing hyps to: " << file_name4.str() << std::endl;
251       volm_geo_index::write_hyps(hyp_root, file_name4.str());
252 
253       // write the hypos in kml file
254       std::vector<volm_geo_index_node_sptr> leaves;
255       volm_geo_index::get_leaves_with_hyps(hyp_root, leaves);
256       for (auto & leave : leaves) {
257         std::string out_file = vul_file::strip_extension(leave->get_hyp_name(file_name4.str())) + ".kml";
258         leave->hyps_->write_to_kml(out_file, inc_in_sec_rad, true);
259       }
260 
261       return volm_io::SUCCESS;
262     }
263 
264     if (use_osm_roads()) {  // if an osm binary file is specified
265       if (in_folder().compare("") == 0 || out_pre().compare("") == 0 || osm_bin_file().compare("") == 0 || osm_tree_folder().compare("") == 0 || world_id() < 0) {
266         vul_arg_display_usage_and_exit();
267         return volm_io::EXE_ARGUMENT_ERROR;
268       }
269 
270       volm_osm_objects osm_objs;
271       double min_size;
272       std::stringstream file_name_pre;
273       file_name_pre << osm_tree_folder() << "geo_index2_wr" << world_id() << "_tile_" << tile_id();
274       volm_geo_index2_node_sptr root = volm_io_tools::read_osm_data_and_tree(file_name_pre.str(), osm_bin_file(), osm_objs, min_size);
275       if (!root) {
276         std::cerr << "Errors in reading " << file_name_pre.str() << " and/or " << osm_bin_file() << std::endl;
277         return volm_io::EXE_ARGUMENT_ERROR;
278       }
279       std::stringstream kml_roads;
280       kml_roads << out_pre() << "/p1b_wr" << world_id() << "_tile_" << tile_id() << "_osm_roads.kml";
281       osm_objs.write_lines_to_kml(kml_roads.str());
282 
283       std::vector<volm_img_info> infos;
284       if (use_satellite_img() )
285         // load the satellite resources
286         volm_io_tools::load_satellite_height_imgs(in_folder(), infos, false, "height_refined_sat_geo");
287       else
288         // load the DEM tiles
289         volm_io_tools::load_aster_dem_imgs(in_folder(), infos);
290 
291       // load satellite classification map to avoid locations on top of the buildings (can be empty if now class map)
292       std::vector<volm_img_info> class_map_infos;
293       volm_io_tools::load_imgs(land_class_map_folder(), class_map_infos, true, true, true);
294       std::cout << " height map resources: " << infos.size() << " images are loaded!\n";
295       std::cout << " land classification resources: " << class_map_infos.size() << " images are loaded!\n";
296 
297       if (land().compare("") == 0) {  // if not using land types
298 
299         // now go over each road in the osm file and insert into the tree of loc hyps
300         std::vector<volm_osm_object_line_sptr>& roads = osm_objs.loc_lines();
301         for (const auto& r : roads) {
302           std::string name = r->prop().name_;
303           std::vector<vgl_point_2d<double> > points = r->line();
304           for (unsigned kk = 1; kk < points.size(); kk++) {
305             if (loc_inside_region(poly, points[kk-1].x(), points[kk-1].y()))
306                 add_hypo(hyp_root, infos, class_map_infos, points[kk-1], inc_in_sec_rad, true);
307             // now interpolate along a straight line, assume locally planar
308             double dif_dy = points[kk].y() - points[kk-1].y();
309             double dif_dx = points[kk].x() - points[kk-1].x();
310             double ds = std::sqrt(dif_dy*dif_dy + dif_dx*dif_dx);
311             double inc_dy = inc_in_sec*(dif_dy/ds);
312             double inc_dx = inc_in_sec*(dif_dx/ds);
313             int cnt = 0;
314             while (ds > inc_in_sec) {
315               ds = ds-inc_in_sec;
316               cnt++;
317               double x = points[kk-1].x()+inc_dx*cnt;
318               double y = points[kk-1].y()+inc_dy*cnt;
319               if (loc_inside_region(poly, x, y))
320                 add_hypo(hyp_root, infos, class_map_infos, vgl_point_2d<double>(x, y), inc_in_sec_rad, true);
321             }
322 
323           }
324         }
325       } else {
326         std::cout << "Generating hyps along the OSM roads and with increments depending on the Geocover land type!\n";
327         std::vector<volm_img_info> geo_infos;
328         volm_io_tools::load_geocover_imgs(land(), geo_infos);
329 
330         // now go over each road in the osm file and insert into the tree of loc hyps
331         std::vector<volm_osm_object_line_sptr>& roads = osm_objs.loc_lines();
332         std::cout << "will process " << roads.size() << " roads...\n"; std::cout.flush();
333 #if 0
334         double y = 12.587963; double x = 76.822019;
335         std::cout << "testing a location's type: x: " << x << " y: " << y << " (first roads first x: " << roads[0]->line()[0].x() << ")\n";
336         int type_prev = find_land_type(geo_infos, vgl_point_2d<double>(x,y));
337         std::cout << "\t\t type is: " << type_prev << "\n";
338 
339         y = 12.575318; x = 76.835210;
340         std::cout << "testing a location's type: x: " << x << " y: " << y << " (first roads first x: " << roads[0]->line()[0].x() << ")\n";
341         type_prev = find_land_type(geo_infos, vgl_point_2d<double>(x,y));
342         std::cout << "\t\t type is: " << type_prev << "\n";
343 #endif
344 
345         for (unsigned k = 0; k < roads.size(); k++) {
346           if (k%1000 == 0) std::cout << k << "."; std::cout.flush();
347           volm_osm_object_line_sptr r = roads[k];
348           std::string name = r->prop().name_;
349           std::vector<vgl_point_2d<double> > points = r->line();
350           if (!points.size())
351             continue;
352           int type_prev = find_land_type(geo_infos, points[0]);
353           if (type_prev < 0)
354             inc_in_sec = 4 * meter_to_sec;  // if unknown type, use the smallest possible interval to be safe
355           else
356             inc_in_sec = volm_osm_category_io::geo_land_hyp_increments[type_prev] * meter_to_sec;
357           inc_in_sec_rad = inc_in_sec*inc_in_sec_radius_ratio;
358           double remainder = 0.0;
359           if ( loc_inside_region(poly, points[0].x(), points[0].y()) )
360             add_hypo(hyp_root, infos, class_map_infos, points[0], inc_in_sec_rad, true);
361           for (unsigned kk = 1; kk < points.size(); kk++) {
362             double prev_x = points[kk-1].x();
363             double prev_y = points[kk-1].y();
364             int type = find_land_type(geo_infos, points[kk]);
365             if (type > 0 && type != type_prev) {
366               double inc_now = volm_osm_category_io::geo_land_hyp_increments[type] * meter_to_sec;
367               inc_in_sec = inc_in_sec < inc_now ? inc_in_sec : inc_now;  // pick the smaller of the increments if different types
368               inc_in_sec_rad = inc_in_sec*inc_in_sec_radius_ratio;
369             }
370 
371             // interpolate along a straight line, assume locally planar
372             double dif_dy = points[kk].y() - points[kk-1].y();
373             double dif_dx = points[kk].x() - points[kk-1].x();
374             double ds = std::sqrt(dif_dy*dif_dy + dif_dx*dif_dx);
375 
376             if (inc_in_sec > ds) {
377               remainder += ds;
378               type_prev = type;
379               continue;
380             }
381 
382             double cos = dif_dx/ds;
383             double sin = dif_dy/ds;
384 
385             double inc_dy = inc_in_sec*sin;
386             double inc_dx = inc_in_sec*cos;
387 
388             // get rid of remainder first
389             if (remainder < inc_in_sec) {
390               double rem = inc_in_sec-remainder;
391               double inc_dy_rem = rem*sin;
392               double inc_dx_rem = rem*cos;
393               double x = prev_x+inc_dx_rem;
394               double y = prev_y+inc_dy_rem;
395               if ( loc_inside_region(poly, x, y) )
396                 add_hypo(hyp_root, infos, class_map_infos, vgl_point_2d<double>(x, y), inc_in_sec_rad, true);
397               prev_x = x;
398               prev_y = y;
399               ds -= rem;
400             }
401 
402             while (ds > inc_in_sec) {
403               ds -= inc_in_sec;
404               double x = prev_x+inc_dx;
405               double y = prev_y+inc_dy;
406               if (loc_inside_region(poly, x, y))
407                 add_hypo(hyp_root, infos, class_map_infos, vgl_point_2d<double>(x, y), inc_in_sec_rad, false);
408               prev_x = x;
409               prev_y = y;
410             }
411             type_prev = type;
412             remainder = ds;
413           }
414         }
415 
416       }
417 
418       unsigned r_cnt = volm_geo_index::hypo_size(hyp_root) ;
419       std::cout << "\n root " << i << " has total " << r_cnt << " hypotheses in its leaves!\n";
420 
421       // write the hypos
422       std::stringstream file_name4; file_name4 << out_pre() << "geo_index_tile_" << i;
423       std::cout << "writing hyps to: " << file_name4.str() << std::endl;
424       volm_geo_index::write_hyps(hyp_root, file_name4.str());
425 
426       std::vector<volm_geo_index_node_sptr> leaves;
427       volm_geo_index::get_leaves_with_hyps(hyp_root, leaves);
428       for (auto & leave : leaves) {
429         std::string out_file = vul_file::strip_extension(leave->get_hyp_name(file_name4.str())) + ".kml";
430         leave->hyps_->write_to_kml(out_file, inc_in_sec_rad);
431       }
432 
433       return volm_io::SUCCESS;
434     } // end of p1b generating locations along the road
435 
436     // generate locations on all non-building region with constant post
437     if (non_building())
438     {
439       // check input
440       if (in_folder().compare("") == 0 || out_pre().compare("") == 0 || world_id() < 0 ) {
441         vul_arg_display_usage_and_exit();
442         std::cout << "ERROR: missing input for generating phase 1b location database on all non-building region" << std::endl;
443       }
444       // load satellite/dem height images
445       std::vector<volm_img_info> infos;
446       if (use_satellite_img() )
447         volm_io_tools::load_satellite_height_imgs(in_folder(), infos, false, "");
448       else
449         volm_io_tools::load_aster_dem_imgs(in_folder(), infos);
450 
451       // load satellite classification 2d map to avoid locations on top of the buildings
452       std::vector<volm_img_info> class_map_infos;
453       volm_io_tools::load_imgs(land_class_map_folder(),class_map_infos, true, true, true);
454       std::cout << "height map resources: " << infos.size() << " geotiff images are loaded!\n";
455       std::cout << "land classification resources: " << class_map_infos.size() << " images are loaded\n";
456 
457       // load URGENT building footprint
458       std::string glob = urgent_folder() + "/*.csv";
459       std::vector<std::pair<vgl_polygon<double>, vgl_point_2d<double> > > build_polys;
460       std::vector<double> build_heights;
461       for (vul_file_iterator fit = glob; fit; ++fit) {
462         volm_io::read_building_file(fit(), build_polys, build_heights);
463       }
464       std::cout << "URGENT building resources: " << build_polys.size() << " buildings are loaded from " << urgent_folder() << std::endl;
465       // obtain the building polygons that intersect with current tile
466       std::vector<vgl_polygon<double> > build_in_tile;
467       for (auto & build_poly : build_polys) {
468         if (vgl_intersection(tiles[tile_id()].bbox_double(), build_poly.first))
469           build_in_tile.push_back(build_poly.first);
470       }
471       std::cout << build_in_tile.size() << " URGENT buildings are inside given tile" << tiles[tile_id()].bbox() << std::endl;
472 
473       double inc_in_meter = inc_in_sec*21/0.000202;
474       double size = nh() * inc_in_sec;
475       unsigned t_id = tile_id();
476       std::cout << "generation locations with interval " << inc_in_meter << " meter. (" << inc_in_sec << " seconds)" << std::endl;
477       std::cout << "generate geo_index based on location density, each leaf has size: " << size << " seconds in geographic cooridnates..\n";
478       volm_geo_index_node_sptr root = volm_geo_index::construct_tree(tiles[t_id], (float)size, poly);
479       // write the geo index structure
480       std::stringstream file_name;  file_name << out_pre() << "geo_index_tile_" << t_id << ".txt";
481       volm_geo_index::write(root, file_name.str(), (float)size);
482       unsigned depth = volm_geo_index::depth(root);
483       std::stringstream file_name3;  file_name3 << out_pre() << "geo_index_tile_" << t_id << "_depth_" << depth << ".kml";
484       volm_geo_index::write_to_kml(root, depth, file_name3.str());
485 
486       // loop over each leaf to add locations
487       for (auto sat_info : infos) {
488         std::vector<volm_geo_index_node_sptr> leaves;
489         volm_geo_index::get_leaves(root, leaves, sat_info.bbox);
490         if (leaves.empty())
491           continue;
492         // obtain the building polygons that currently intersect with given leaf
493         auto leaf_size = (float)leaves[0]->extent_.width();
494         std::cout << leaves.size() << " leaves (" << leaf_size << " deg) intersects with the height map: " << sat_info.name << std::flush << std::endl;
495         for (auto & leave : leaves) {
496           // obtain buildings that are inside leaf
497           std::vector<vgl_polygon<double> > build_in_leaf;
498           build_in_leaf.clear();
499           for (const auto & b_idx : build_in_tile)
500             if (vgl_intersection(leave->extent_, b_idx))
501               build_in_leaf.push_back(b_idx);
502           std::cout << build_in_leaf.size() << " are in leaf " << leave->extent_ << std::flush << std::endl;
503           if (!leave->hyps_)
504             leave->hyps_ = new volm_loc_hyp();
505           float lower_left_lon = (float)leave->extent_.min_point().x();
506           float lower_left_lat = (float)leave->extent_.min_point().y();
507           auto nhi = (unsigned)std::ceil(leaf_size/inc_in_sec);
508           for (unsigned hi=0; hi<nhi; hi++) {
509             double lon = lower_left_lon + hi*inc_in_sec;
510             for (unsigned hj=0; hj<nhi; hj++) {
511               double lat = lower_left_lat + hj*inc_in_sec;
512               vgl_point_2d<double> pt(lon, lat);
513               // check building from URGENT data
514               bool is_building = false;
515               for (unsigned i = 0; (i < build_in_leaf.size() && !is_building);  i++)
516                 is_building = loc_inside_region(build_in_leaf[i], lon, lat);
517               if (is_building)
518                 continue;
519               // check building from classification map
520               int type = find_land_type(class_map_infos, pt);
521               if ((class_map_infos.size() && type < 0) ||
522                   type == (int)volm_osm_category_io::volm_land_table_name["building"].id_ ||
523                   type == (int)volm_osm_category_io::volm_land_table_name["tall_building"].id_)
524                 continue;
525               double u, v;
526               sat_info.cam->global_to_img(lon, lat, 0.0, u, v);
527               int ii = (int)std::floor(u+0.5);
528               int jj = (int)std::floor(v+0.5);
529               if (sat_info.valid_pixel(ii,jj)) {
530                 vil_image_view<float> img(sat_info.img_r);
531                 float z = img(ii,jj);
532                 unsigned id;
533                 if (z>0 && !(leave->hyps_->exist(lat, lon, inc_in_sec_rad, id)))
534                   leave->hyps_->add(lat, lon, z);
535               }
536             }
537           }
538         }
539         std::cout << "FINISH!!\n";
540       }
541       // write the hypo database
542       std::vector<volm_geo_index_node_sptr> leaves;
543       volm_geo_index::get_leaves_with_hyps(root, leaves);
544       std::cout << leaves.size() << " leaves have location hypotheses" << std::endl;
545       for (unsigned i = 0; i < leaves.size(); i++) {
546         std::cout << "\t leaf: " << i << " has " << leaves[i]->hyps_->locs_.size() << " locations" << std::endl;
547       }
548       std::stringstream file_name4;  file_name4 << out_pre() << "geo_index_tile_" << t_id;
549       std::cout << "\nwriting hypos to: " << file_name4.str() << std::endl;
550       volm_geo_index::write_hyps(root, file_name4.str());
551       for (auto & leave : leaves) {
552         std::string out_file = vul_file::strip_extension(leave->get_hyp_name(file_name4.str())) + ".kml";
553         leave->hyps_->write_to_kml(out_file, inc_in_sec_rad, false);
554       }
555       std::cout << volm_geo_index::hypo_size(root) << " locations are generated in tile " << t_id << std::endl;
556 
557       return volm_io::SUCCESS;
558     }
559 
560     // generate locations with constant interval
561     if (region_name().compare("all") == 0) {
562       // check whether the region is defined
563       if (region_name() != "all" && volm_osm_category_io::volm_land_table_name.find(region_name()) == volm_osm_category_io::volm_land_table_name.end()) {
564         std::cout << "ERROR: unknown region name: " << region_name() << std::endl;
565         return volm_io::EXE_ARGUMENT_ERROR;
566       }
567       // check input
568       if (in_folder().compare("") == 0 || out_pre().compare("") == 0 || world_id() < 0) {
569         vul_arg_display_usage_and_exit();
570         std::cout << "ERROR: missing input for generating phase 1b location database on all non-building region" << std::endl;
571       }
572       // load satellite/dem height images
573       std::vector<volm_img_info> infos;
574       if (use_satellite_img() )
575         volm_io_tools::load_satellite_height_imgs(in_folder(), infos, false, "");
576       else
577         volm_io_tools::load_aster_dem_imgs(in_folder(), infos);
578 
579       // load satellite classification 2d map to avoid locations on top of the buildings
580       std::cout << "height map resources: " << infos.size() << " geotiff images are loaded!\n";
581       double inc_in_meter = inc_in_sec*21/0.000202;
582       double size = nh() * inc_in_sec;
583       unsigned t_id = tile_id();
584       std::cout << "generate location on land category: " << region_name() << std::endl;
585       std::cout << "generation locations with interval " << inc_in_meter << " meter. (" << inc_in_sec << " seconds)" << std::endl;
586       std::cout << "generate geo_index based on location density, each leaf has size: " << size << " seconds in geographic cooridnates..\n";
587       volm_geo_index_node_sptr root = volm_geo_index::construct_tree(tiles[t_id], (float)size, poly);
588       // write the geo index structure
589       std::stringstream file_name;  file_name << out_pre() << "geo_index_tile_" << t_id << ".txt";
590       volm_geo_index::write(root, file_name.str(), (float)size);
591       unsigned depth = volm_geo_index::depth(root);
592       std::stringstream file_name3;  file_name3 << out_pre() << "geo_index_tile_" << t_id << "_depth_" << depth << ".kml";
593       volm_geo_index::write_to_kml(root, depth, file_name3.str());
594       // loop over each leaf to add locations
595       for (auto sat_info : infos) {
596         std::vector<volm_geo_index_node_sptr> leaves;
597         volm_geo_index::get_leaves(root, leaves, sat_info.bbox);
598         if (leaves.empty())
599           continue;
600         auto leaf_size = (float)leaves[0]->extent_.width();
601         std::cout << leaves.size() << " leaves (" << leaf_size << " deg) intersects with the height map: " << sat_info.name << std::endl;
602         for (auto & leave : leaves) {
603           if (!leave->hyps_)
604             leave->hyps_ = new volm_loc_hyp();
605           float lower_left_lon = (float)leave->extent_.min_point().x();
606           float lower_left_lat = (float)leave->extent_.min_point().y();
607           auto nhi = (unsigned)std::ceil(leaf_size/inc_in_sec);
608           for (unsigned hi=0; hi<nhi; hi++) {
609             double lon = lower_left_lon + hi*inc_in_sec;
610             for (unsigned hj=0; hj<nhi; hj++) {
611               double lat = lower_left_lat + hj*inc_in_sec;
612               vgl_point_2d<double> pt(lon, lat);
613               double u, v;
614               sat_info.cam->global_to_img(lon, lat, 0.0, u, v);
615               int ii = (int)std::floor(u+0.5);
616               int jj = (int)std::floor(v+0.5);
617               if (sat_info.valid_pixel(ii,jj)) {
618                 vil_image_view<float> img(sat_info.img_r);
619                 float z = img(ii,jj);
620                 unsigned id;
621                 if (z>0 && !(leave->hyps_->exist(lat, lon, inc_in_sec_rad, id)))
622                   leave->hyps_->add(lat, lon, z);
623               }
624             }
625           }
626         }
627       }
628       // write the hypo database
629       std::vector<volm_geo_index_node_sptr> leaves;
630       volm_geo_index::get_leaves_with_hyps(root, leaves);
631       std::stringstream file_name4;  file_name4 << out_pre() << "geo_index_tile_" << t_id;
632       std::cout << "\nwriting hypos to: " << file_name4.str() << std::endl;
633       volm_geo_index::write_hyps(root, file_name4.str());
634       for (auto & leave : leaves) {
635         std::string out_file = vul_file::strip_extension(leave->get_hyp_name(file_name4.str())) + ".kml";
636         leave->hyps_->write_to_kml(out_file, inc_in_sec_rad, true);
637       }
638       std::cout << volm_geo_index::hypo_size(root) << " locations are generated in tile " << t_id << std::endl;
639 
640       return volm_io::SUCCESS;
641 
642     }
643 
644   } // end of p1b location generation
645 
646   // generate locations along the OSM road network and along the coastline sand region
647   if (p1a())
648   {
649     // input check
650     if (out_pre().compare("") == 0 || in_poly().compare("") == 0) {
651       vul_arg_display_usage_and_exit();  return volm_io::EXE_ARGUMENT_ERROR;
652     }
653     std::cout << "will use increments: " << inc() << " arcseconds along north and east directions!\n";
654     // parse roi polygon
655     vgl_polygon<double> poly = bkml_parser::parse_polygon(in_poly());
656     std::cout << "ROI poly has: " << poly[0].size() << " points" << std::endl;
657     // create volm tile
658     std::vector<volm_tile> tiles;
659     if (world_id() == 1)       tiles = volm_tile::generate_p1_wr1_tiles();
660     else if (world_id() == 2)  tiles = volm_tile::generate_p1_wr2_tiles();
661     else {  std::cout << "ERROR: unknown world id in phase 1a" << std::endl;  return -1;  }
662     std::cout << " number of tiles: " << tiles.size() << std::endl;
663     unsigned t_id = tile_id();
664     if ( t_id >= tiles.size() ) {
665       std::cout << "ERROR: unknown tile id: " << t_id << std::endl;  return volm_io::EXE_ARGUMENT_ERROR;
666     }
667     double meter_to_sec = volm_io_tools::meter_to_seconds(tiles[t_id].lat_, tiles[t_id].lon_);
668     // create and write geo index for locations
669     float size = 0.1f;  // in seconds, set a fixed size for the leaves
670     volm_geo_index_node_sptr hyp_root = volm_geo_index::construct_tree(tiles[t_id], (float)size, poly);
671     if (region_name().compare("") == 0) {
672       std::stringstream file_name;  file_name << out_pre() << "geo_index_tile_" << t_id << ".txt";
673       volm_geo_index::write(hyp_root, file_name.str(), (float)size);
674       unsigned depth = volm_geo_index::depth(hyp_root);
675       //std::stringstream file_name2;  file_name2 << out_pre() << "geo_index_tile" << t_id << ".kml";
676       //volm_geo_index::write_to_kml(hyp_root, 0, file_name2.str());
677       std::stringstream file_name3;  file_name3 << out_pre() << "geo_index_tile_" << t_id << "_depth_" << depth << ".kml";
678       volm_geo_index::write_to_kml(hyp_root, depth, file_name3.str());
679     }
680 
681     // generate ground truth only locations database
682     if (only_gt() && add_gt().compare("") != 0) {
683       // only add the ground truth points in the files as they are given (using the elev in the ground truth file)
684       // load ground truth file
685       std::vector<std::pair<vgl_point_3d<double>, std::pair<std::pair<std::string, int>, std::string> > > samples;
686       int cnt = volm_io::read_gt_file(add_gt(), samples);
687       std::cout << "adding " << cnt << " ground truth locations!\n";
688       // add locations into hypo database
689       for (int i = 0; i < cnt; i++) {
690         if (poly.contains(samples[i].first.x(), samples[i].first.y())) {
691           std::cout << samples[i].second.first.first << " adding.. " << samples[i].first.y() << ", " << samples[i].first.x() << ' ';
692           // obtain the gt elev from ground truth file
693           bool added = volm_geo_index::add_hypothesis(hyp_root, samples[i].first.x() , samples[i].first.y(), samples[i].first.z());
694           if (added) std::cout << " succeed!\n";
695           else       std::cout << " not found in tree of tile: " << tile_id() << "!\n";
696         }
697         else {
698           std::cout << samples[i].second.first.first << " is not inside ROI polygons!\n";
699         }
700       }
701       std::cout << "after addition of ground locations, hyp_root " << t_id << " has total " << volm_geo_index::hypo_size(hyp_root) << " hypotheses in its leaves!\n";
702 
703       // write the hypos
704       std::stringstream file_name4; file_name4 << out_pre() << "geo_index_tile_" << t_id;
705       std::cout << "\nwriting hypos to: " << file_name4.str() << std::endl;
706       volm_geo_index::write_hyps(hyp_root, file_name4.str());
707       // write the hypos into kml file for visualization
708       std::vector<volm_geo_index_node_sptr> leaves;
709       volm_geo_index::get_leaves_with_hyps(hyp_root, leaves);
710       for (auto & leave : leaves) {
711         std::string out_file = vul_file::strip_extension(leave->get_hyp_name(file_name4.str()))+".kml";
712         leave->hyps_->write_to_kml(out_file, inc_in_sec_rad, true);
713       }
714       return volm_io::SUCCESS;
715     }
716 
717     // generate osm road network only locations database
718     if (use_osm_roads())
719     {
720       if (in_folder().compare("") == 0 || out_pre().compare("") == 0 || osm_bin_file().compare("") == 0 || osm_tree_folder().compare("") == 0 || world_id() < 0 ||
721           land_class_map_folder().compare("") == 0) {
722         vul_arg_display_usage_and_exit();
723         std::cout << "ERROR: missing input for generating phase 1a OSM road network " << std::endl;
724         return volm_io::EXE_ARGUMENT_ERROR;
725       }
726       unsigned t_id = tile_id();
727       // load OSM database
728       volm_osm_objects osm_objs;
729       double min_size;
730       std::stringstream file_name_pre;
731       file_name_pre << osm_tree_folder() << "geo_index2_wr" << world_id() << "_tile_" << t_id;
732       volm_geo_index2_node_sptr root = volm_io_tools::read_osm_data_and_tree(file_name_pre.str(), osm_bin_file(), osm_objs, min_size);
733       if (!root) {
734         std::cout << "ERROR: loading osm database failed, check file " << file_name_pre.str() << " or " << osm_bin_file() << std::endl;
735         return volm_io::EXE_ARGUMENT_ERROR;
736       }
737       std::stringstream kml_roads;
738       kml_roads << out_pre() << "/p1b_wr" << world_id() << "_tile_" << tile_id() << "_osm_roads.kml";
739       osm_objs.write_lines_to_kml(kml_roads.str());
740       // load LIDAR height data
741       std::vector<volm_img_info> lidar_infos;
742       volm_io_tools::load_lidar_imgs(in_folder(), lidar_infos);
743       // load land classification data
744       std::vector<volm_img_info> class_infos;
745       volm_io_tools::load_imgs(land_class_map_folder(), class_infos, true, true, true);
746       std::cout << lidar_infos.size() << " LIDAR images are successfully loaded!" << std::endl;
747       std::cout << class_infos.size() << " 2d class map are successfully loaded!" << std::endl;
748 
749       // add locations along the road network
750       if (land().compare("") == 0) {
751         std::cout << "generate locations along the roads with constant density" << std::endl;
752         std::vector<volm_osm_object_line_sptr>& roads = osm_objs.loc_lines();
753         for (const auto& r : roads) {
754           if (r->prop().name_ == "water_river" || r->prop().name_ == "water_stream" || r->prop().name_ == "water_large_river"  || r->prop().name_ == "water_canal")
755             continue;
756           std::vector<vgl_point_2d<double> > points = r->line();
757           for (unsigned kk = 1; kk < points.size(); kk++) {
758             if (loc_inside_region(poly, points[kk-1].x(), points[kk-1].y()))
759               add_hypo(hyp_root, lidar_infos, class_infos, points[kk-1], inc_in_sec_rad, true);
760             // now interpolate along a straight line, assume locally planar
761             double dif_dy = points[kk].y() - points[kk-1].y();
762             double dif_dx = points[kk].x() - points[kk-1].x();
763             double ds = std::sqrt(dif_dy*dif_dy + dif_dx*dif_dx);
764             double inc_dy = inc_in_sec*(dif_dy/ds);
765             double inc_dx = inc_in_sec*(dif_dx/ds);
766             unsigned cnt = 0;
767             while (ds > inc_in_sec) {
768               ds = ds-inc_in_sec;  cnt++;
769               double x = points[kk-1].x() + inc_dx*cnt;  double y = points[kk-1].y() + inc_dy*cnt;
770               if (loc_inside_region(poly, x, y))
771                 add_hypo(hyp_root, lidar_infos, class_infos, vgl_point_2d<double>(x, y), inc_in_sec_rad, true);
772             }
773           }
774         }
775       } else {
776         std::cout << "generate locations along the roads and location density is defined by NLCD land category" << std::endl;
777         std::vector<volm_img_info> info_tmp;
778         volm_io_tools::load_nlcd_imgs(land(), info_tmp);
779         if (info_tmp.size() != tiles.size()) {
780           std::cout << "ERROR: mismatch in create tiles " << tiles.size() << " and loaded " << info_tmp.size() << " nlcd images, check input nlcd folder\n";
781           return -1;
782         }
783         std::vector<volm_img_info> nlcd_infos;
784         for (unsigned i = 0; i < tiles.size(); i++) {
785           for (unsigned j = 0; j < tiles.size(); j++) {
786             double diff_lon = tiles[i].bbox_double().min_x() - info_tmp[j].bbox.min_x();
787             double diff_lat = tiles[i].bbox_double().min_y() - info_tmp[j].bbox.min_y();
788             double diff = std::sqrt(diff_lon*diff_lon + diff_lat*diff_lat);
789             if (diff < 0.25) {
790               nlcd_infos.push_back(info_tmp[j]);
791               break;
792             }
793           }
794         }
795         std::cout << nlcd_infos.size() << " NLCD images are successfully loaded!\n";
796         // start to add locations along all roads
797         std::vector<volm_osm_object_line_sptr>& roads = osm_objs.loc_lines();
798         std::cout << "will process " << roads.size() << " roads...\n" << std::flush;
799         for (unsigned r_idx = 0; r_idx < roads.size(); r_idx++) {
800           if (r_idx%1000 == 0)  std::cout << r_idx << '.' << std::flush;
801           volm_osm_object_line_sptr r = roads[r_idx];
802           if (r->prop().name_ == "water_river" || r->prop().name_ == "water_stream" || r->prop().name_ == "water_large_river"  || r->prop().name_ == "water_canal")
803             continue;
804           // obtain the road network that is inside the tile
805           std::vector<vgl_point_2d<double> > pts = r->line() ;
806           std::string name = r->prop().name_;
807           if (pts.empty())
808             continue;
809           int type_prev = find_land_type(nlcd_infos, pts[0]);
810           if (type_prev < 0)
811             inc_in_sec = 4*meter_to_sec;  // if the type is unknown, use the smallest possible interval to be safe
812           else
813             inc_in_sec = volm_osm_category_io::geo_land_hyp_increments[type_prev]*meter_to_sec;
814           inc_in_sec_rad = inc_in_sec*inc_in_sec_radius_ratio;
815           double remainder = 0.0;
816           if (loc_inside_region(poly, pts[0].x(), pts[0].y()))
817             add_hypo(hyp_root, lidar_infos, class_infos, pts[0], inc_in_sec_rad, true);
818           for (unsigned kk = 1; kk < pts.size(); kk++) {
819             double prev_x = pts[kk-1].x();  double prev_y = pts[kk-1].y();
820             int type = find_land_type(nlcd_infos, pts[kk]);
821             if (type>0 && type != type_prev) {
822               double inc_now = volm_osm_category_io::geo_land_hyp_increments[type]*meter_to_sec;
823               inc_in_sec = inc_in_sec < inc_now ? inc_in_sec : inc_now;  // pick the smaller of the increments if different types
824               inc_in_sec_rad = inc_in_sec*inc_in_sec_radius_ratio;
825             }
826             // interpolate along a straight line, assume locally planar
827             double dif_dy = pts[kk].y() - pts[kk-1].y();
828             double dif_dx = pts[kk].x() - pts[kk-1].x();
829             double ds = std::sqrt(dif_dy*dif_dy + dif_dx*dif_dx);
830             if (inc_in_sec > ds) {
831               remainder += ds;
832               type_prev = type;
833               continue;
834             }
835             double cos = dif_dx/ds;          double sin = dif_dy/ds;
836             double inc_dy = inc_in_sec*sin;  double inc_dx = inc_in_sec*cos;
837             // get rid of remainder first
838             if (remainder < inc_in_sec) {
839               double rem = inc_in_sec-remainder;
840               double inc_dy_rem = rem*sin;
841               double inc_dx_rem = rem*cos;
842               double x = prev_x+inc_dx_rem;
843               double y = prev_y+inc_dy_rem;
844               if ( loc_inside_region(poly, x, y) )
845                 add_hypo(hyp_root, lidar_infos, class_infos, vgl_point_2d<double>(x, y), inc_in_sec_rad, true);
846               prev_x = x;
847               prev_y = y;
848               ds -= rem;
849             }
850             while (ds > inc_in_sec) {
851               ds -= inc_in_sec;
852               double x= prev_x+inc_dx;  double y = prev_y+inc_dy;
853               if (loc_inside_region(poly, x, y))
854                 add_hypo(hyp_root, lidar_infos, class_infos, vgl_point_2d<double>(x, y), inc_in_sec_rad, false);
855               prev_x = x;
856               prev_y = y;
857             }
858             type_prev = type;
859             remainder = ds;
860           }
861         } // end of road loop
862       } // end of if-else statement where NLCD is used or not
863 
864       // write the hypos
865       std::stringstream file_name4;  file_name4 << out_pre() << "geo_index_tile_" << t_id;
866       std::cout << "\nwriting hypos to: " << file_name4.str() << std::endl;
867       volm_geo_index::write_hyps(hyp_root, file_name4.str());
868       std::cout << volm_geo_index::hypo_size(hyp_root) << " locations are generated in tile " << t_id << std::endl;
869 
870       std::vector<volm_geo_index_node_sptr> leaves;
871       volm_geo_index::get_leaves_with_hyps(hyp_root, leaves);
872       for (auto & leave : leaves) {
873         std::string out_file = vul_file::strip_extension(leave->get_hyp_name(file_name4.str())) + ".kml";
874         leave->hyps_->write_to_kml(out_file, inc_in_sec_rad);
875       }
876       return volm_io::SUCCESS;
877     } // end of road locations generation
878 
879     // generate locations only on certain types of locations with a given density
880     if (region_name().compare("") != 0)
881     {
882       //check whether the region is defined
883       if (volm_osm_category_io::volm_land_table_name.find(region_name()) == volm_osm_category_io::volm_land_table_name.end()) {
884         std::cout << "ERROR: unknown region name: " << region_name() << std::endl;
885         return volm_io::EXE_ARGUMENT_ERROR;
886       }
887       // check input
888       if (in_folder().compare("") == 0 || out_pre().compare("") == 0 || world_id() < 0 || land_class_map_folder().compare("") == 0) {
889         vul_arg_display_usage_and_exit();
890         std::cout << "ERROR: missing input for generating phase 1a region based location network" << std::endl;
891         return volm_io::EXE_ARGUMENT_ERROR;
892       }
893 
894       double inc_in_meter = inc_in_sec*21/0.000202;
895       std::cout << "generate locations on land category: " << region_name()
896                << " with location distance " << inc_in_meter << " meter. (" << inc_in_sec << " seconds)" << std::endl;
897       // determine depth of the geo index depending on inc
898       double size = nh()*inc_in_sec;
899       std::cout << "generate geo_index based on location density, each leaf has size: " << size << " seconds in geographic coords..\n";
900 
901       volm_geo_index_node_sptr root = volm_geo_index::construct_tree(tiles[t_id], (float)size, poly);
902       // write the geo index structure
903       std::stringstream file_name;  file_name << out_pre() << "geo_index_tile_" << t_id << ".txt";
904       volm_geo_index::write(root, file_name.str(), (float)size);
905       unsigned depth = volm_geo_index::depth(root);
906       std::stringstream file_name3;  file_name3 << out_pre() << "geo_index_tile_" << t_id << "_depth_" << depth << ".kml";
907       volm_geo_index::write_to_kml(root, depth, file_name3.str());
908       // load LIDAR
909       std::vector<volm_img_info> lidar_infos;
910       volm_io_tools::load_lidar_imgs(in_folder(), lidar_infos);
911       // load land class map
912       std::vector<volm_img_info> class_infos;
913       volm_io_tools::load_imgs(land_class_map_folder(), class_infos, true, true, true);
914       std::cout << lidar_infos.size() << " LIDAR images are successfully loaded!" << std::endl;
915       std::cout << class_infos.size() << " 2d class map are successfully loaded!" << std::endl;
916 
917       // loop over each leaf to add locations
918       for (auto lidar_info : lidar_infos) {
919         std::vector<volm_geo_index_node_sptr> leaves;
920         volm_geo_index::get_leaves(root, leaves, lidar_info.bbox);
921         if (leaves.empty())
922           continue;
923         auto leaf_size = (float)leaves[0]->extent_.width();
924         std::cout << leaves.size() << " leaves (" << leaf_size << " deg) intersects with lidar image: " << lidar_info.name << std::endl;
925         for (auto & leave : leaves) {
926           if (!leave->hyps_)
927             leave->hyps_ = new volm_loc_hyp();
928           float lower_left_lon = (float)leave->extent_.min_point().x();
929           float lower_left_lat = (float)leave->extent_.min_point().y();
930           auto nhi = (unsigned)std::ceil(leaf_size/inc_in_sec);
931           for (unsigned hi=0; hi < nhi; hi++) {
932             double lon = lower_left_lon + hi*inc_in_sec;
933             for (unsigned hj = 0; hj < nhi; hj++) {
934               double lat = lower_left_lat + hj*inc_in_sec;
935               vgl_point_2d<double> pt(lon, lat);
936               int type = find_land_type(class_infos, pt);
937               if (type > 0) {
938                 if (volm_osm_category_io::volm_land_table[(unsigned char)type].name_ == region_name()) {
939                   double u,v;
940                   lidar_info.cam->global_to_img(lon, lat, 0.0, u, v);
941                   int ii = (int)std::floor(u+0.5);
942                   int jj = (int)std::floor(v+0.5);
943                   if (lidar_info.valid_pixel(ii,jj)) {
944                     vil_image_view<float> img(lidar_info.img_r);
945                     float z = img(ii,jj);
946                     unsigned id;
947                     if (z > 0 && !(leave->hyps_->exist(lat, lon, inc_in_sec_rad, id)))
948                       leave->hyps_->add(lat, lon, z);
949                   }
950                 }
951                 else if (region_name() == "all") {  // generate constant post locations everywhere
952                   double u, v;
953                   lidar_info.cam->global_to_img(lon,lat,0.0,u,v);
954                   int ii = (int)std::floor(u+0.5);
955                   int jj = (int)std::floor(v+0.5);
956                   if (lidar_info.valid_pixel(ii,jj)) {
957                     vil_image_view<float> img(lidar_info.img_r);
958                     float z = img(ii,jj);
959                     unsigned id;
960                     if (z > 0 && !(leave->hyps_->exist(lat, lon, inc_in_sec_rad, id)))
961                       leave->hyps_->add(lat, lon, z);
962                   }
963                 }
964               }
965             }
966           }
967         } // end of loop over current lidar image
968       }  // end of loop over all lidar images
969 
970       // write the hypo database
971       std::vector<volm_geo_index_node_sptr> leaves;
972       volm_geo_index::get_leaves_with_hyps(root, leaves);
973       std::stringstream file_name4; file_name4 << out_pre() << "geo_index_tile_" << t_id;
974       std::cout << "\nwriting hypos to: " << file_name4.str() << std::endl;
975       volm_geo_index::write_hyps(root, file_name4.str());
976       for (auto & leave : leaves) {
977         std::string out_file = vul_file::strip_extension(leave->get_hyp_name(file_name4.str())) + ".kml";
978         leave->hyps_->write_to_kml(out_file, inc_in_sec_rad, true);
979       }
980       std::cout << volm_geo_index::hypo_size(root) << " locations are generated in tile " << t_id << std::endl;
981       return volm_io::SUCCESS;
982     }  // end of region generation
983 
984   }  // end of p1A options
985 
986 #if 0
987   // for P1A and P1B regions reading (world_id 6 and 7 becomes desert and coast)
988   // change to wr1 tiles for desert
989   std::vector<volm_tile> tiles;
990   if (world_id() == 1)      tiles = volm_tile::generate_p1b_wr1_tiles();
991   else if (world_id() == 2) tiles = volm_tile::generate_p1b_wr2_tiles();
992   else if (world_id() == 3) tiles = volm_tile::generate_p1b_wr3_tiles();
993   else if (world_id() == 4) tiles = volm_tile::generate_p1b_wr4_tiles();
994   else if (world_id() == 5) tiles = volm_tile::generate_p1b_wr5_tiles();
995   else if (world_id() == 6) tiles = volm_tile::generate_p1_wr1_tiles();
996   else if (world_id() == 7) tiles = volm_tile::generate_p1_wr2_tiles();
997   std::cout << " number of tiles: " << tiles.size() << std::endl;
998   unsigned i = tile_id();
999   if (i >= tiles.size()) {
1000     std::cerr << "tile id: " << i << " is greater than number of tiles: " << tiles.size() << "!\n";
1001     vul_arg_display_usage_and_exit();
1002     return volm_io::EXE_ARGUMENT_ERROR;
1003   }
1004 #endif
1005 
1006   if (read()) {
1007     if (out_pre().compare("") == 0) {
1008       vul_arg_display_usage_and_exit();
1009       return volm_io::EXE_ARGUMENT_ERROR;
1010     }
1011     unsigned t_id = tile_id();
1012     float min_s;
1013     std::stringstream file_name; file_name << out_pre() << "geo_index_tile_" << t_id;
1014     volm_geo_index_node_sptr root = volm_geo_index::read_and_construct(file_name.str() + ".txt", min_s);
1015     std::vector<volm_geo_index_node_sptr> leaves2;
1016     volm_geo_index::get_leaves(root, leaves2);
1017     std::cout << "\t number of leaves: " << leaves2.size() << std::endl;
1018     volm_geo_index::read_hyps(root, file_name.str());
1019     std::cout << " read hyps!\n";
1020     std::vector<volm_geo_index_node_sptr> leaves;
1021     volm_geo_index::get_leaves_with_hyps(root, leaves);
1022     std::cout << "Geo index for tile: " << t_id << " stored in: " << file_name.str() << '\n'
1023              << "\t number of leaves with hyps: " << leaves.size() << std::endl;
1024     unsigned size = volm_geo_index::hypo_size(root);
1025     std::cout << "\t total number of hypos: " << size << std::endl;
1026     for (unsigned l = 0; l < leaves.size(); l++) {
1027       std::cout << " leaf_id = " << l << " leaf bbox = " << leaves[l]->extent_ << std::endl;
1028       unsigned num_hyps = leaves[l]->hyps_->size();
1029       for (unsigned h = 0; h < num_hyps; h++)
1030       {
1031         /*vgl_point_3d<double> h_pt = */
1032              leaves[l]->hyps_->locs_[h];
1033       }
1034     }
1035     return volm_io::SUCCESS;
1036   }
1037 
1038 #if 0
1039   if (in_folder().compare("") == 0 || out_pre().compare("") == 0 || in_poly().compare("") == 0) {
1040     vul_arg_display_usage_and_exit();
1041     return volm_io::EXE_ARGUMENT_ERROR;
1042   }
1043   std::cout << "will use increments " << inc() << " arcseconds along north and east directions!\n";
1044 
1045   vgl_polygon<double> poly = bkml_parser::parse_polygon(in_poly());
1046   std::cout << "outer poly  has: " << poly[0].size() << std::endl;
1047 
1048   // determine depth of the geo index depending on inc, if we want to have 100x100 = 10K hyps in each leaf
1049   double size = nh()*inc_in_sec; // inc() is given in arcseconds, convert it to seconds;
1050   std::cout << " each leaf has size: " << size << " seconds in geographic coords..\n"
1051            << " increments in seconds: " << inc_in_sec << '\n'
1052            << " increments in meters: " << (inc_in_sec*21/0.000202) << '\n'
1053            << " only putting hyps in UTM zone: " << utm_zone() << '\n';
1054 
1055 
1056   volm_geo_index_node_sptr root = volm_geo_index::construct_tree(tiles[i], (float)size, poly);
1057 
1058   // write the geo index and the hyps
1059   std::stringstream file_name; file_name << out_pre() << "geo_index_tile_" << i << ".txt";
1060   volm_geo_index::write(root, file_name.str(), (float)size);
1061   unsigned depth = volm_geo_index::depth(root);
1062   std::stringstream file_name2; file_name2 << out_pre() << "geo_index_tile_" << i << ".kml";
1063   volm_geo_index::write_to_kml(root, 0, file_name2.str());
1064   std::stringstream file_name3; file_name3 << out_pre() << "geo_index_tile_" << i << "_depth_" << depth << ".kml";
1065   volm_geo_index::write_to_kml(root, depth, file_name3.str());
1066 
1067   // prune the out of zone leaves
1068   if (!volm_geo_index::prune_by_zone(root, utm_zone())) {
1069     std::cout << " root " << i << " is not in zone: " << utm_zone() << "! no hypotheses in its leaves!\n";
1070     return 0;
1071   }
1072   if (!only_gt()) {
1073   std::string file_glob = in_folder() + "/*.tif";
1074   unsigned cnt = 0;
1075   for (vul_file_iterator fn=file_glob; fn; ++fn, ++cnt) {
1076     std::string tiff_fname = fn();
1077 
1078     vil_image_view_base_sptr img_sptr = vil_load(tiff_fname.c_str());
1079     vil_image_view<float> img(img_sptr);
1080     unsigned ni = img.ni(); unsigned nj = img.nj();
1081     volm_tile t(tiff_fname, ni, nj);
1082     //t.write_kml(out_pre() + t.get_string() + ".kml", 0);
1083 
1084     // write the geo index and the hyps
1085     std::vector<volm_geo_index_node_sptr> leaves;
1086     vgl_box_2d<double> leaf_box = t.bbox_double();
1087     volm_geo_index::get_leaves(root, leaves, leaf_box);
1088     if (!leaves.size())
1089       continue;
1090     float size_leaf = (float)leaves[0]->extent_.width();
1091 #if 0
1092     volm_index_node_sptr dummy_root = new volm_index_node(t.bbox());
1093     dummy_root->children_ = leaves;
1094     std::stringstream file_name; file_name << out_pre() << "geo_index_tile_" << i << "_intersection_" << cnt << ".kml";
1095     volm_index::write_to_kml(dummy_root, 1, file_name.str());
1096 #endif
1097     // generate the hyps and find heights from LIDAR
1098     for (unsigned j = 0; j < leaves.size(); ++j) {
1099       if (!leaves[j]->hyps_)
1100         leaves[j]->hyps_ = new volm_loc_hyp();
1101       float lower_left_lon = (float)leaves[j]->extent_.min_point().x();
1102       float lower_left_lat = (float)leaves[j]->extent_.min_point().y();
1103       unsigned nhi = (unsigned)std::ceil(size_leaf/inc_in_sec);
1104       for (unsigned hi = 0; hi < nhi; ++hi) {
1105         double lon = lower_left_lon + hi*inc_in_sec;
1106         for (unsigned hj = 0; hj < nhi; ++hj) {
1107           double lat = lower_left_lat + hj*inc_in_sec;
1108           vpgl_utm u; int zone;  double x, y;
1109           u.transform(lat, lon, x, y, zone);
1110           if (zone != (int)utm_zone())
1111             continue;
1112           unsigned ii, jj;
1113           bool contains = t.global_to_img(lon, lat, ii, jj);
1114           if (contains) {
1115             float z = img(ii, jj);
1116             unsigned id;
1117             if (z > 0  && !(leaves[j]->hyps_->exist(lat, lon, inc_in_sec_rad, id)))
1118               leaves[j]->hyps_->add(lat, lon, z);
1119           }
1120         }
1121       }
1122     }
1123 
1124     //if (cnt > 0)
1125     //  break;
1126   }
1127   }
1128   unsigned r_cnt = volm_geo_index::hypo_size(root) ;
1129   std::cout << " root " << i << " has total " << r_cnt << " hypotheses in its leaves!\n";
1130 
1131   if (add_gt().compare("") != 0) {  // user passed the path to a text file with the gt locations
1132 
1133     // load the images
1134     std::string file_glob = in_folder() + "/*.tif";
1135     std::vector<std::pair<vil_image_view_base_sptr,  volm_tile > > tiles;
1136     for (vul_file_iterator fn=file_glob; fn; ++fn) {
1137       std::string tiff_fname = fn();
1138 
1139       vil_image_view_base_sptr img_sptr = vil_load(tiff_fname.c_str());
1140       unsigned ni = img_sptr->ni(); unsigned nj = img_sptr->nj();
1141       volm_tile t(tiff_fname, ni, nj);
1142       tiles.push_back(std::pair<vil_image_view_base_sptr, volm_tile>(img_sptr, t));
1143     }
1144 
1145 
1146 #if 0    //: add any gt positions if any
1147     if (volm_geo_index::add_hypothesis(root, -79.857689, 32.759063, 1.60))
1148       std::cout << " added p1a_test1_06-GROUNDTRUTH\n";
1149 
1150     if (volm_geo_index::add_hypothesis(root, -79.813014, 32.775959, 4.41))
1151       std::cout << " added p1a_test1_28-GROUNDTRUTH\n";
1152     if (volm_geo_index::add_hypothesis(root, -78.282153, 33.911997, 1.60))
1153       std::cout << " added p1a_test1_08-GROUNDTRUTH\n";
1154     if (volm_geo_index::add_hypothesis(root, -78.281430, 33.912397, 1.60))
1155       std::cout << " added p1a_test1_46-GROUNDTRUTH\n";
1156     if (volm_geo_index::add_hypothesis(root, -81.550366, 30.720336, 1.60))
1157       std::cout << " added p1a_test1_18-GROUNDTRUTH\n";
1158     if (volm_geo_index::add_hypothesis(root, -79.951930, 32.648980, 1.60))
1159       std::cout << " added p1a_test1_38-GROUNDTRUTH\n";
1160     if (volm_geo_index::add_hypothesis(root, -79.268871, 33.365799, 1.60))
1161       std::cout << " added p1a_test1_34-GROUNDTRUTH\n";
1162 #endif
1163 
1164     std::vector<std::pair<vgl_point_3d<double>, std::pair<std::pair<std::string, int>, std::string> > > samples;
1165     int cnt = volm_io::read_gt_file(add_gt(), samples);
1166     std::cout << " adding " << cnt <<" gt locs!\n";
1167     for (int j = 0; j < cnt; ++j) {
1168       vpgl_utm u; int zone;  double x, y;
1169       u.transform(samples[j].first.y(), samples[j].first.x(), x, y, zone);
1170       if (zone != (int)utm_zone()) {
1171         std::cout << samples[j].second.first.first << " is in zone: " << zone <<" not in " << utm_zone() << " skipping!\n";
1172         continue;
1173       }
1174       std::cout << samples[j].second.first.first << " adding.. " << samples[j].first.y() << ", " << samples[j].first.x() << ' ';
1175 
1176       // find which box contains it
1177       bool added = false;
1178       for (unsigned kk = 0; kk < tiles.size(); kk++) {
1179          unsigned ii, jj;
1180          bool contains = tiles[kk].second.global_to_img(samples[j].first.x(), samples[j].first.y(), ii, jj);
1181          if (contains) {
1182           vil_image_view<float> img(tiles[kk].first);
1183           float z = img(ii, jj);
1184           // check the neighborhood
1185           for (int ii2 = ii - 1; ii2 <= (int)(ii+1); ii2++)
1186             for (int jj2 = jj - 1; jj2 <= (int)(jj+1); jj2++) {
1187               if (ii2 >= 0 && jj2 >= 0 && ii2 < (int)img.ni() && jj2 < (int)img.nj()) {
1188                 if (z < img(ii2, jj2)) z = img(ii2, jj2);
1189               }
1190             }
1191           if (z > 0.0f) {
1192             std::cout << " corrected height from: " << samples[j].first.z() << " to: " << z+1.6 << '\n';
1193             added = volm_geo_index::add_hypothesis(root, samples[j].first.x(), samples[j].first.y(), z+1.6);
1194           } else {
1195             std::cout << " height from LIDAR is: " << z << " writing original height: " << samples[j].first.z() << '\n';
1196             added = volm_geo_index::add_hypothesis(root, samples[j].first.x(), samples[j].first.y(), samples[j].first.z());
1197           }
1198           break;
1199          }
1200       }
1201       //bool added = volm_geo_index::add_hypothesis(root, samples[j].first.x(), samples[j].first.y(), samples[j].first.z());
1202       if (added) std::cout << " success!\n";
1203       else       std::cout <<" not found in tree of tile: " << tile_id() << "!\n";
1204     }
1205 
1206     unsigned r_cnt = volm_geo_index::hypo_size(root) ;
1207     std::cout << " after addition of gt locs, root " << i << " has total " << r_cnt << " hypotheses in its leaves!\n";
1208   }
1209 
1210   // write the hypos
1211   std::stringstream file_name4; file_name4 << out_pre() << "geo_index_tile_" << i;
1212   std::cout << "writing hyps to: " << file_name4.str() << std::endl;
1213   volm_geo_index::write_hyps(root, file_name4.str());
1214 //#if DEBUG
1215   std::vector<volm_geo_index_node_sptr> leaves;
1216   volm_geo_index::get_leaves_with_hyps(root, leaves);
1217   std::stringstream file_name5; file_name5 << out_pre() << "geo_index_tile_" << i << "_hyps.kml";
1218   leaves[0]->hyps_->write_to_kml(file_name5.str(), inc_in_sec);
1219 //#endif
1220 
1221   std::cout << "total time: " << t.all()/1000 << " seconds = " << t.all()/(1000*60) << " mins.\n";
1222   return volm_io::SUCCESS;
1223 #endif
1224 
1225 }
1226