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