1 // This is brl/bseg/boxm2/pro/processes/boxm2_dem_to_xyz_process.cxx
2 #include <fstream>
3 #include <iostream>
4 #include <algorithm>
5 #include <bprb/bprb_func_process.h>
6 //:
7 // \file
8 // \brief  A process to prepare x y z images from a geotiff dem, the resolutions are adjusted with respect to scene resolution.
9 // E.g. if DEM is a 10 m resolution and scene is of 1m resolution, then the output images are resampled versions of DEM
10 // given x,y,z images use ingest_dem process to initialize model
11 //
12 //  If a camera is passed as input (e.g. given by a previous process that reads it from tfw file then use it,
13 //  otherwise try reading it from geotiff header
14 //
15 // \author Ozge C. Ozcanli
16 // \date May 02, 2012
17 
18 #include <boxm2/boxm2_scene.h>
19 
20 #include <vpgl/file_formats/vpgl_geo_camera.h>
21 #include "vpgl/vpgl_generic_camera.h"
22 #include "vil/vil_image_view.h"
23 #include "vil/vil_image_resource.h"
24 #include "vil/vil_load.h"
25 #ifdef _MSC_VER
26 #  include "vcl_msvc_warnings.h"
27 #endif
28 //#include "vil/vil_resample_bilin.h"
29 #include "vil/vil_convert.h"
30 #include "vil/vil_new.h"
31 
32 #include <vsol/vsol_box_2d_sptr.h>
33 #include <vsol/vsol_box_2d.h>
34 
35 #include <brip/brip_roi.h>
36 
37 
38 namespace boxm2_dem_to_xyz_process_globals
39 {
40   constexpr unsigned n_inputs_ = 5;
41   constexpr unsigned n_outputs_ = 3;
42 }
43 
boxm2_dem_to_xyz_process_cons(bprb_func_process & pro)44 bool boxm2_dem_to_xyz_process_cons(bprb_func_process& pro)
45 {
46   using namespace boxm2_dem_to_xyz_process_globals;
47 
48   //process takes 1 input
49   std::vector<std::string> input_types_(n_inputs_);
50   input_types_[0] = "boxm2_scene_sptr";
51   input_types_[1] = "vcl_string";  // geotiff image of DEM
52   input_types_[2] = "double"; // lvcs is using wgs84 so wrt ellipsoid, however some DEMs are using geoid,
53                               // in that case pass the distance between ellipsoid and geoid in the region
54                               // to convert DEM heights to heights wrt to ellipsoid
55   input_types_[3] = "vpgl_camera_double_sptr";  // geocam if available, otherwise pass 0, camera will be constructed using info in geotiff header
56   input_types_[4] = "float";  // some DEMs have gaps or invalid regions, pass the value in the DEM imagery that is used to fill those areas.
57 
58   // process has 1 outputs:
59   std::vector<std::string>  output_types_(n_outputs_);
60   output_types_[0] = "vil_image_view_base_sptr";  // x image
61   output_types_[1] = "vil_image_view_base_sptr";  // y image
62   output_types_[2] = "vil_image_view_base_sptr";  // z image
63 
64   bool good = pro.set_input_types(input_types_) && pro.set_output_types(output_types_);
65 
66   // set input defaults
67   brdb_value_sptr geocam = new brdb_value_t<vpgl_camera_double_sptr>;
68   pro.set_input(3, geocam);
69 
70   return good;
71 }
72 
upsample_dem(vil_image_resource_sptr & out_img_res,unsigned ni,unsigned nj,vil_image_view<float> * dem_view,unsigned dem_ni,unsigned dem_nj)73 bool upsample_dem(vil_image_resource_sptr& out_img_res, unsigned ni, unsigned nj, vil_image_view<float>* dem_view, unsigned dem_ni, unsigned dem_nj)
74 {
75   if (dem_ni == ni && dem_nj == nj) {
76     out_img_res->put_view(*dem_view, 0,0);
77     return true;
78   }
79   else if (dem_ni > ni || dem_nj > nj) {
80     std::cerr << "in upsample_dem() -- cannot downsample image, image sizes are incompatible!\n";
81     return false;
82   }
83   int block_ni = ni/dem_ni;
84   int block_nj = nj/dem_nj;
85   std::cout << "ni: " << ni << " dem_ni: " << dem_ni << " block_ni: " << block_ni << '\n'
86            << "nj: " << nj << " dem_nj: " << dem_nj << " block_nj: " << block_nj << std::endl;
87   for (unsigned i = 0; i < dem_ni; i++) {
88     for (unsigned j = 0; j < dem_nj; j++) {
89       vil_image_view<float> block(block_ni, block_nj, 1);
90       block.fill((*dem_view)(i,j));
91       out_img_res->put_view(block, i*block_ni, j*block_nj);
92     }
93   }
94   return true;
95 }
96 
boxm2_dem_to_xyz_process(bprb_func_process & pro)97 bool boxm2_dem_to_xyz_process(bprb_func_process& pro)
98 {
99   using namespace boxm2_dem_to_xyz_process_globals;
100 
101   if ( pro.n_inputs() < n_inputs_ ){
102     std::cout << pro.name() << ": The input number should be " << n_inputs_<< std::endl;
103     return false;
104   }
105   //get the inputs
106   boxm2_scene_sptr scene = pro.get_input<boxm2_scene_sptr>(0);
107   vpgl_lvcs_sptr lvcs = new vpgl_lvcs(scene->lvcs());
108   std::string geotiff_fname = pro.get_input<std::string>(1);
109   auto geoid_height = pro.get_input<double>(2);
110   vpgl_camera_double_sptr cam = pro.get_input<vpgl_camera_double_sptr>(3);
111   auto fill_in_value = pro.get_input<float>(4);
112 
113   vil_image_resource_sptr dem_res = vil_load_image_resource(geotiff_fname.c_str());
114 
115   vpgl_geo_camera* geocam = nullptr;
116   if (cam && (geocam = dynamic_cast<vpgl_geo_camera*> (cam.ptr())) ) {
117     std::cout << "Using the loaded camera!\n";
118   }
119   else {
120     vpgl_geo_camera::init_geo_camera(dem_res, lvcs, geocam); // FIXME leaking geocam. This pattern is everywhere...
121   }
122 
123   if (!geocam) {
124     std::cerr << "In boxm2_dem_to_xyz_process() - the geocam could not be initialized!\n";
125     return false;
126   }
127 
128   vgl_box_3d<double> scene_bbox = scene->bounding_box();
129   std::vector<boxm2_block_id> blks = scene->get_block_ids();
130   if (blks.size() < 1)
131     return false;
132 
133   unsigned orig_dem_ni = dem_res->ni(); unsigned orig_dem_nj = dem_res->nj();
134   std::cout << "original dem resolution: " << orig_dem_ni << ' ' << orig_dem_nj << std::endl;
135   brip_roi broi(orig_dem_ni, orig_dem_nj);
136   vsol_box_2d_sptr bb = new vsol_box_2d();
137 
138   double u,v;
139   geocam->project(scene_bbox.min_x(), scene_bbox.min_y(), scene_bbox.min_z(), u, v);
140   bb->add_point(u,v);
141   geocam->project(scene_bbox.max_x(), scene_bbox.max_y(), scene_bbox.max_z(), u, v);
142   bb->add_point(u,v);
143   bb = broi.clip_to_image_bounds(bb);
144   if (bb->width() <= 0 || bb->height() <= 0) {
145     std::cout << "In boxm2_dem_to_xyz_process() -- " << geotiff_fname << " does not overlap the scene!\n";
146     return false;
147   }
148 
149   vil_image_view_base_sptr dem_view_float;
150   vil_image_view_base_sptr dem_view_base = dem_res->get_view(0, orig_dem_ni, 0, orig_dem_nj);
151   auto* dem_view = dynamic_cast<vil_image_view<float>*>(dem_view_base.ptr());
152   if (!dem_view) {
153     vil_image_view<float> temp(dem_view_base->ni(), dem_view_base->nj(), 1);
154 
155     auto* dem_view_int = dynamic_cast<vil_image_view<vxl_int_16>*>(dem_view_base.ptr());
156     if (!dem_view_int) {
157       auto* dem_view_byte = dynamic_cast<vil_image_view<vxl_byte>*>(dem_view_base.ptr());
158       if (!dem_view_byte) {
159         std::cerr << "Error: boxm2_dem_to_xyz_process: The image pixel format: " << dem_view_base->pixel_format() << " is not supported!\n";
160         return false;
161       }
162       else {
163         vil_convert_cast(*dem_view_byte, temp);
164       }
165     }
166     else {
167       vil_convert_cast(*dem_view_int, temp);
168     }
169 
170     dem_view_float = new vil_image_view<float>(temp); // shallow copy
171     dem_view = dynamic_cast<vil_image_view<float>*>(dem_view_float.ptr());
172     //assert(dev_view);
173   }
174 
175   boxm2_scene_info* info = scene->get_blk_metadata(blks[0]);
176   float sb_length = info->block_len;
177   float vox_length = sb_length/8.0f;
178 
179   // prepare an image for the finest resolution
180   int ni = (int)std::ceil((scene_bbox.max_x()-scene_bbox.min_x())/vox_length);
181   int nj = (int)std::ceil((scene_bbox.max_y()-scene_bbox.min_y())/vox_length);
182 
183   std::cout <<"ni: " << ni << " nj: " << nj << std::endl;
184 
185   // create x y z images
186   auto* out_img_x = new vil_image_view<float>(ni, nj, 1);
187   auto* out_img_y = new vil_image_view<float>(ni, nj, 1);
188   auto* out_img_z = new vil_image_view<float>(ni, nj, 1);
189   // initialize the image by scene origin
190   out_img_x->fill(-10.0f);
191   out_img_y->fill(-10.0f);
192   out_img_z->fill(-1.0f);
193   //out_img_x->fill((float)(scene_bbox.min_x())-10.0f); // local coord system min z
194   //out_img_y->fill((float)(scene_bbox.min_y())-10.0f); // local coord system min z
195   //out_img_z->fill((float)(scene_bbox.min_z())-1.0f);  // local coord system min z
196   std::cout <<   "out img x(0,0): " << ((*out_img_x)(0,0))
197            << "\nout img y(0,0): " << ((*out_img_y)(0,0))
198            << "\nout img z(0,0): " << ((*out_img_z)(0,0)) << std::endl;
199 
200   double lon,lat,gz;
201   lvcs->local_to_global(0,0,0,vpgl_lvcs::wgs84,lon, lat, gz);
202   std::cout << "lvcs origin height: " << gz << std::endl;
203   gz += geoid_height;  // correct for the difference to geoid if necessary, geoid_height should have been passed 0 if that is not necessary
204   //gz += scene_bbox.min_z();
205 
206   std::cout << " scene min z: " << scene_bbox.min_z() << " gz: " << gz << std::endl;
207   if (fill_in_value <= 0)
208     fill_in_value = std::numeric_limits<float>::max();
209 
210   for (int i = 0; i < ni; ++i)
211     for (int j = 0; j < nj; ++j) {
212       auto local_x = (float)(i*vox_length+scene_bbox.min_x()+vox_length/2.0);
213       auto local_y = (float)(scene_bbox.max_y()-j*vox_length+vox_length/2.0);
214       (*out_img_x)(i,j) = local_x;
215       (*out_img_y)(i,j) = local_y;
216 
217       double u,v;
218       geocam->project(local_x, local_y, scene_bbox.min_z(), u, v);
219       // for now just cast to the nearest pixel in DEM, might want to sample bilinearly
220       int uu = (int)std::floor(u+0.5);
221       int vv = (int)std::floor(v+0.5);
222       if (uu >= 0 && vv >= 0 && uu < (int)orig_dem_ni && vv < (int)orig_dem_nj) {
223         if ((*dem_view)(uu,vv) < fill_in_value)  // otherwise it remains at local min z
224           (*out_img_z)(i,j) = (*dem_view)(uu,vv)-(float)gz;  // we need local height
225       }
226     }
227 
228   pro.set_output_val<vil_image_view_base_sptr>(0, out_img_x);
229   pro.set_output_val<vil_image_view_base_sptr>(1, out_img_y);
230   pro.set_output_val<vil_image_view_base_sptr>(2, out_img_z);
231 
232   return true;
233 }
234 
235 //: Craete x y z images from the shadow height map of an ortho aerial image
236 
237 namespace boxm2_shadow_heights_to_xyz_process_globals
238 {
239   constexpr unsigned n_inputs_ = 5;
240   constexpr unsigned n_outputs_ = 3;
241 }
242 
boxm2_shadow_heights_to_xyz_process_cons(bprb_func_process & pro)243 bool boxm2_shadow_heights_to_xyz_process_cons(bprb_func_process& pro)
244 {
245   using namespace boxm2_shadow_heights_to_xyz_process_globals;
246 
247   //process takes 5 inputs
248   std::vector<std::string> input_types_(n_inputs_);
249   input_types_[0] = "boxm2_scene_sptr";
250   input_types_[1] = "vil_image_view_base_sptr";  // shadow height map image of the cropped ortho aerial image, height values are in pixels with respect to a horizontal surface at the base of the object
251   input_types_[2] = "vpgl_camera_double_sptr"; // generic cam of cropped ortho aerial image (to be given by roi_init_geotiff_process)
252   input_types_[3] = "vcl_string"; // dem_fname, height image values will be added to these values
253   input_types_[4] = "double"; // pixel scaling
254 
255   // process has 3 outputs:
256   std::vector<std::string>  output_types_(n_outputs_);
257   output_types_[0] = "vil_image_view_base_sptr";  // x image
258   output_types_[1] = "vil_image_view_base_sptr";  // y image
259   output_types_[2] = "vil_image_view_base_sptr";  // z image
260 
261   return pro.set_input_types(input_types_) && pro.set_output_types(output_types_);
262 }
263 
boxm2_shadow_heights_to_xyz_process(bprb_func_process & pro)264 bool boxm2_shadow_heights_to_xyz_process(bprb_func_process& pro)
265 {
266   using namespace boxm2_shadow_heights_to_xyz_process_globals;
267 
268   if ( pro.n_inputs() < n_inputs_ ){
269     std::cout << pro.name() << ": The input number should be " << n_inputs_<< std::endl;
270     return false;
271   }
272   //get the inputs
273   boxm2_scene_sptr scene = pro.get_input<boxm2_scene_sptr>(0);
274   vpgl_lvcs_sptr lvcs = new vpgl_lvcs(scene->lvcs());
275   vil_image_view_base_sptr img = pro.get_input<vil_image_view_base_sptr>(1);
276   vpgl_camera_double_sptr cam = pro.get_input<vpgl_camera_double_sptr>(2);
277   std::string geotiff_fname = pro.get_input<std::string>(3);
278   auto scale = pro.get_input<double>(4);
279 
280   auto* gcam=dynamic_cast<vpgl_generic_camera<double>*> (cam.ptr());
281 
282   vil_image_view<float> height_img(img);
283   unsigned ni = height_img.ni();
284   unsigned nj = height_img.nj();
285 
286   if (gcam->rows() != nj || gcam->cols() !=ni) {
287     std::cerr << "In boxm2_shadow_heights_to_xyz_process() - generic cam and height image sizes are not compatible!\n"
288              << "gcam rows: " << gcam->rows() << " cols: " << gcam->cols() << " ni: " << ni << " nj: " << nj << '\n';
289     return false;
290   }
291 
292   vil_image_resource_sptr dem_res = vil_load_image_resource(geotiff_fname.c_str());
293   vpgl_geo_camera* geocam = nullptr;
294   vpgl_geo_camera::init_geo_camera(dem_res, lvcs, geocam);
295 
296   vgl_box_3d<double> scene_bbox = scene->bounding_box();
297   std::vector<boxm2_block_id> blks = scene->get_block_ids();
298   if (blks.size() < 1)
299     return false;
300 
301   // crop the image from the DEM
302   vgl_box_2d<double> proj_bbox;
303   double u,v;
304   geocam->project(scene_bbox.min_x(), scene_bbox.min_y(), scene_bbox.min_z(), u, v);
305   proj_bbox.add(vgl_point_2d<double>(u,v));
306   geocam->project(scene_bbox.max_x(), scene_bbox.max_y(), scene_bbox.max_z(), u, v);
307   proj_bbox.add(vgl_point_2d<double>(u,v));
308   int min_i = int(std::max(0.0, std::floor(proj_bbox.min_x())));
309   int min_j = int(std::max(0.0, std::floor(proj_bbox.min_y())));
310   int max_i = int(std::min(dem_res->ni()-1.0, std::ceil(proj_bbox.max_x())));
311   int max_j = int(std::min(dem_res->nj()-1.0, std::ceil(proj_bbox.max_y())));
312   std::cout << "scene projected in the image mini: " << min_i << " minj: " << min_j << " maxi: " << max_i << " maxj: " << max_j << std::endl;
313   unsigned int dem_ni = max_i - min_i + 1;
314   unsigned int dem_nj = max_j - min_j + 1;
315   vil_image_view_base_sptr dem_view_base = dem_res->get_view((unsigned int)min_i, dem_ni, (unsigned int)min_j, dem_nj);
316   auto* dem_view = dynamic_cast<vil_image_view<float>*>(dem_view_base.ptr());
317   if (!dem_view) {
318       std::cerr << "Error: boxm2_dem_to_xyz_process: could not cast first return image to a vil_image_view<float>\n";
319       return false;
320   }
321   std::cout << "dem resolution is: " << dem_ni << " by " << dem_nj << std::endl;
322 
323   auto* out_img = new vil_image_view<float>(ni, nj, 1);
324   vil_image_resource_sptr out_img_res = vil_new_image_resource_of_view(*out_img);
325 
326   if (!upsample_dem(out_img_res, ni, nj, dem_view, dem_ni, dem_nj))
327     return false;
328 
329   // create x y z images
330   auto* out_img_x = new vil_image_view<float>(ni, nj, 1);
331   auto* out_img_y = new vil_image_view<float>(ni, nj, 1);
332   auto* out_img_z = new vil_image_view<float>(ni, nj, 1);
333 
334   double lon,lat,gz;
335   lvcs->local_to_global(0,0,0,vpgl_lvcs::wgs84,lon, lat, gz);
336 
337   for (unsigned i = 0; i < ni; i++)
338     for (unsigned j = 0; j < nj; j++) {
339       vgl_ray_3d<double> ray = gcam->ray(i,j);
340       vgl_point_3d<double> o = ray.origin();
341       (*out_img_x)(i,j) = (float)o.x();
342       (*out_img_y)(i,j) = (float)o.y();
343       if (height_img(i,j) >= 0) {
344         (*out_img_z)(i,j) = (float)(height_img(i,j)*scale+(*out_img)(i,j)-gz);  // we need local height
345       }
346       else
347         (*out_img_z)(i,j) = (*out_img)(i,j)-(float)gz;  // we need local height
348     }
349 
350   pro.set_output_val<vil_image_view_base_sptr>(0, out_img_x);
351   pro.set_output_val<vil_image_view_base_sptr>(1, out_img_y);
352   pro.set_output_val<vil_image_view_base_sptr>(2, out_img_z);
353   return true;
354 }
355 
356 namespace boxm2_dem_to_xyz_process2_globals
357 {
358   constexpr unsigned n_inputs_ = 5;
359   constexpr unsigned n_outputs_ = 3;
360 }
361 
boxm2_dem_to_xyz_process2_cons(bprb_func_process & pro)362 bool boxm2_dem_to_xyz_process2_cons(bprb_func_process& pro)
363 {
364   using namespace boxm2_dem_to_xyz_process2_globals;
365 
366   //process takes 1 input
367   std::vector<std::string> input_types_(n_inputs_);
368   input_types_[0] = "boxm2_scene_sptr";
369   input_types_[1] = "vcl_string";  // geotiff image of DEM
370   input_types_[2] = "double"; // lvcs is using wgs84 so wrt ellipsoid, however some DEMs are using geoid,
371                               // in that case pass the distance between ellipsoid and geoid in the region
372                               // to convert DEM heights to heights wrt to ellipsoid
373   input_types_[3] = "vpgl_camera_double_sptr";  // geocam if available, otherwise pass 0, camera will be constructed using info in geotiff header
374   input_types_[4] = "float";  // some DEMs have gaps or invalid regions, pass the value in the DEM imagery that is used to fill those areas.
375 
376   // process has 1 outputs:
377   std::vector<std::string>  output_types_(n_outputs_);
378   output_types_[0] = "vil_image_view_base_sptr";  // x image
379   output_types_[1] = "vil_image_view_base_sptr";  // y image
380   output_types_[2] = "vil_image_view_base_sptr";  // z image
381 
382   bool good = pro.set_input_types(input_types_) && pro.set_output_types(output_types_);
383 
384   // set input defaults
385   brdb_value_sptr geocam = new brdb_value_t<vpgl_camera_double_sptr>;
386   pro.set_input(3, geocam);
387 
388   return good;
389 }
390 
boxm2_dem_to_xyz_process2(bprb_func_process & pro)391 bool boxm2_dem_to_xyz_process2(bprb_func_process& pro)
392 {
393   using namespace boxm2_dem_to_xyz_process2_globals;
394 
395   if ( pro.n_inputs() < n_inputs_ ){
396     std::cout << pro.name() << ": The input number should be " << n_inputs_<< std::endl;
397     return false;
398   }
399   //get the inputs
400   boxm2_scene_sptr scene = pro.get_input<boxm2_scene_sptr>(0);
401   vpgl_lvcs_sptr lvcs = new vpgl_lvcs(scene->lvcs());
402   std::string geotiff_fname = pro.get_input<std::string>(1);
403   //double geoid_height = pro.get_input<double>(2); // TODO: unused!
404   vpgl_camera_double_sptr cam = pro.get_input<vpgl_camera_double_sptr>(3);
405   //float fill_in_value = pro.get_input<float>(4); // TODO: unused!
406 
407   vil_image_resource_sptr dem_res = vil_load_image_resource(geotiff_fname.c_str());
408 
409   vpgl_geo_camera* geocam = nullptr;
410   if ( cam && (geocam = dynamic_cast<vpgl_geo_camera*> (cam.ptr())) ) {
411     std::cout << "Using the loaded camera!\n";
412   }
413   else {
414     vpgl_geo_camera::init_geo_camera(dem_res, lvcs, geocam);
415   }
416 
417   if (!geocam) {
418     std::cerr << "In boxm2_dem_to_xyz_process() - the geocam could not be initialized!\n";
419     return false;
420   }
421 
422   vgl_box_3d<double> scene_bbox = scene->bounding_box();
423   std::vector<boxm2_block_id> blks = scene->get_block_ids();
424   if (blks.size() < 1)
425     return false;
426 
427   unsigned orig_dem_ni = dem_res->ni(),
428            orig_dem_nj = dem_res->nj();
429   std::cout << "original dem resolution: " << orig_dem_ni << ' ' << orig_dem_nj << std::endl;
430   brip_roi broi(orig_dem_ni, orig_dem_nj);
431   vsol_box_2d_sptr bb = new vsol_box_2d();
432 
433   double min_uu, min_vv, max_uu, max_vv;
434   geocam->project(scene_bbox.min_x(), scene_bbox.min_y(), scene_bbox.min_z(), min_uu, min_vv);
435   bb->add_point(min_uu,min_vv);
436   geocam->project(scene_bbox.max_x(), scene_bbox.max_y(), scene_bbox.max_z(), max_uu, max_vv);
437   bb->add_point(max_uu,max_vv);
438   bb = broi.clip_to_image_bounds(bb);
439   if (bb->width() <= 0 || bb->height() <= 0) {
440     std::cout << "In boxm2_dem_to_xyz_process() -- " << geotiff_fname << " does not overlap the scene!\n";
441     return false;
442   }
443   std::cout <<"projected scene bbox: " << *bb << std::endl;
444 
445   unsigned int min_i = min_uu > 0 ? (unsigned int)min_uu : 0;
446   unsigned int min_j = max_vv > 0 ? (unsigned int)max_vv : 0;  // scene box min projects to lower left corner of the scene in the image
447 
448   //int ni = max_uu-min_uu+1 < orig_dem_ni ? (int)(max_uu-min_uu+1) : orig_dem_ni;
449   //int nj = min_vv-max_vv+1 < orig_dem_nj ? (int)(min_vv-max_vv+1) : orig_dem_nj;
450   int ni = bb->get_max_x() - bb->get_min_x() + 1;
451   int nj = bb->get_max_y() - bb->get_min_y() + 1;
452 
453   std::cout <<  "min_uu: " << min_uu << " min_vv: " << min_vv
454            <<"\nmax_uu: " << max_uu << " max_vv: " << max_vv
455            <<"\nmin_i: " << min_i << " min_j: " << min_j << " ni: " << ni << " nj: " << nj << std::endl;
456 
457   vil_image_view_base_sptr dem_view_base = dem_res->get_view(min_i, ni, min_j, nj);
458   auto* dem_view = dynamic_cast<vil_image_view<float>*>(dem_view_base.ptr());
459   if (!dem_view) {
460     vil_image_view<float> temp(dem_view_base->ni(), dem_view_base->nj(), 1);
461 
462     auto* dem_view_int = dynamic_cast<vil_image_view<vxl_int_16>*>(dem_view_base.ptr());
463     if (!dem_view_int) {
464       auto* dem_view_byte = dynamic_cast<vil_image_view<vxl_byte>*>(dem_view_base.ptr());
465       if (!dem_view_byte) {
466         std::cerr << "Error: boxm2_dem_to_xyz_process: The image pixel format: " << dem_view_base->pixel_format() << " is not supported!\n";
467         return false;
468       }
469       else
470         vil_convert_cast(*dem_view_byte, temp);
471     }
472     else
473       vil_convert_cast(*dem_view_int, temp);
474     dem_view = new vil_image_view<float>(temp);
475   }
476   std::cout <<"got dem image!" << std::endl;
477 
478   boxm2_scene_info* info = scene->get_blk_metadata(blks[0]);
479   float sb_length = info->block_len;
480   std::cout <<"sb_length: " << sb_length << "!\n\n"
481            <<"ni: " << ni << " nj: " << nj << std::endl;
482 
483   // create x y z images
484   auto* out_img_x = new vil_image_view<float>(ni, nj, 1);
485   auto* out_img_y = new vil_image_view<float>(ni, nj, 1);
486   auto* out_img_z = new vil_image_view<float>(ni, nj, 1);
487   out_img_x->fill((float)(scene_bbox.min_x())-10.0f);  // local coord system min z
488   out_img_y->fill((float)(scene_bbox.min_y())-10.0f);  // local coord system min z
489   out_img_z->fill((float)(scene_bbox.min_z())-1.0f);  // local coord system min z
490   std::cout <<   "out img x(0,0): " << ((*out_img_x)(0,0))
491            << "\nout img y(0,0): " << ((*out_img_y)(0,0))
492            << "\nout img z(0,0): " << ((*out_img_z)(0,0)) << std::endl;
493 
494   for (int i = 0; i < ni; ++i)
495     for (int j = 0; j < nj; ++j) {
496       // find the global coord of this pixel
497       double lon, lat;
498       geocam->img_to_global(i+min_i, j+min_j, lon, lat);
499       // find the local coord of this global position
500       double lx, ly, lz;
501       lvcs->global_to_local(lon, lat, (*dem_view)(i,j), vpgl_lvcs::wgs84, lx, ly, lz);
502       vgl_point_3d<double> pt(lx, ly, lz);
503       (*out_img_x)(i,j) = (float)lx;
504       (*out_img_y)(i,j) = (float)ly;
505       (*out_img_z)(i,j) = (float)lz;
506     }
507 
508   pro.set_output_val<vil_image_view_base_sptr>(0, out_img_x);
509   pro.set_output_val<vil_image_view_base_sptr>(1, out_img_y);
510   pro.set_output_val<vil_image_view_base_sptr>(2, out_img_z);
511 
512   return true;
513 }
514 
515 
516 namespace boxm2_initialize_ground_xyz_process_globals
517 {
518   constexpr unsigned n_inputs_ = 2;
519   constexpr unsigned n_outputs_ = 3;
520 }
521 
boxm2_initialize_ground_xyz_process_cons(bprb_func_process & pro)522 bool boxm2_initialize_ground_xyz_process_cons(bprb_func_process& pro)
523 {
524   using namespace boxm2_initialize_ground_xyz_process_globals;
525 
526   std::vector<std::string> input_types_(n_inputs_);
527   input_types_[0] = "boxm2_scene_sptr";
528   input_types_[1] = "int";   // desired height of ground plane in global coords, e.g. for ground plane to be at global z = 0, pass 0
529 
530   std::vector<std::string>  output_types_(n_outputs_);
531   output_types_[0] = "vil_image_view_base_sptr";  // x image
532   output_types_[1] = "vil_image_view_base_sptr";  // y image
533   output_types_[2] = "vil_image_view_base_sptr";  // z image
534 
535   return pro.set_input_types(input_types_) && pro.set_output_types(output_types_);
536 }
537 
boxm2_initialize_ground_xyz_process(bprb_func_process & pro)538 bool boxm2_initialize_ground_xyz_process(bprb_func_process& pro)
539 {
540   using namespace boxm2_initialize_ground_xyz_process_globals;
541 
542   if ( pro.n_inputs() < n_inputs_ ){
543     std::cout << pro.name() << ": The input number should be " << n_inputs_<< std::endl;
544     return false;
545   }
546   //get the inputs
547   boxm2_scene_sptr scene = pro.get_input<boxm2_scene_sptr>(0);
548   int ground_height = pro.get_input<int>(1);
549 
550   vgl_box_3d<double> scene_bbox = scene->bounding_box();
551 
552   std::vector<boxm2_block_id> blks = scene->get_block_ids();
553   boxm2_scene_info* info = scene->get_blk_metadata(blks[0]);
554   float sb_length = info->block_len;
555   float vox_length = sb_length/8.0f;
556   std::cout << "voxel length: " << vox_length;
557 
558   // prepare an image for the finest resolution
559   int ni = (int)std::ceil((scene_bbox.max_x()-scene_bbox.min_x()+1.0)/vox_length);
560   int nj = (int)std::ceil((scene_bbox.max_y()-scene_bbox.min_y()+1.0)/vox_length);
561   std::cout <<"image size needs ni: " << ni << " nj: " << nj << " to support voxel res: " << vox_length << std::endl;
562 
563   // create x y z images
564   auto* out_img_x = new vil_image_view<float>(ni, nj, 1);
565   auto* out_img_y = new vil_image_view<float>(ni, nj, 1);
566   auto* out_img_z = new vil_image_view<float>(ni, nj, 1);
567   out_img_x->fill((float)(scene_bbox.min_x())-10.0f);  // local coord system min z
568   out_img_y->fill((float)(scene_bbox.min_y())-10.0f);  // local coord system min z
569   out_img_z->fill((float)(scene_bbox.min_z())-1.0f);  // local coord system min z
570 
571   double lat, lon, elev; scene->lvcs().get_origin(lat, lon, elev);
572   std::cout << "global scene origin is at z = " << elev << std::endl;
573   double g_h = ground_height - elev;
574   std::cout << " in global coords, ground plane is at z = " << ground_height << '\n'
575            << " in local coords, this corresponds to z = " << scene_bbox.min_z()+g_h  << '\n';
576 
577   for (int i = 0; i < ni; i++)
578     for (int j = 0; j < nj; j++) {
579       (*out_img_x)(i,j) = i + 0.5;
580       (*out_img_y)(i,j) = nj - j + 0.5;
581       (*out_img_z)(i,j) = scene_bbox.min_z() + g_h;  // g_h meters higher than min z
582     }
583   pro.set_output_val<vil_image_view_base_sptr>(0, out_img_x);
584   pro.set_output_val<vil_image_view_base_sptr>(1, out_img_y);
585   pro.set_output_val<vil_image_view_base_sptr>(2, out_img_z);
586   return true;
587 }
588