GeoFrame.h
1 /*
2 
3 MIT License
4 
5 Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25 */
26 /*
27 Part of Rack development has been done in the BALTRAD projects part-financed
28 by the European Union (European Regional Development Fund and European
29 Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30 */
31 #ifndef GEOFRAME_H_
32 #define GEOFRAME_H_
33 
34 #include "Geometry.h"
35 #include "drain/util/BoundingBox.h"
36 #include "drain/util/Geo.h"
37 #include "drain/util/Proj6.h" // for geographical projection of radar data bins
38 
39 
40 namespace drain
41 {
42 
43 
44 
45 namespace image
46 {
47 
48 class GeoInfo {
49 
50 public:
51  // TODO: CartesianODIM compatibility
52 };
53 
55 
58 class GeoFrame { // See also
59 
60 public:
61 
62  //typedef unsigned int icoord_t;
63 
65  GeoFrame(unsigned int width = 0, unsigned int height = 0);
66 
67  virtual inline
68  ~GeoFrame(){
69  };
70 
71  inline
72  void reset(){
73  setGeometry(0, 0);
74  setBoundingBoxD(0,0,0,0);
75  //bBoxD.set(+180.0, +90.0, -180.0, -90.0);
76  }
77 
79 
82  inline
83  bool projectionIsSet() const {
84  return projGeo2Native.isSet();
85  };
86 
88  inline
89  bool geometryIsSet() const {
90  return ((frameWidth > 0) && (frameHeight > 0));
91  };
92 
94 
98  inline
99  bool bboxIsSet() const {
100  return (bBoxD.getArea() > 0.0);
101  };
102 
104 
108  inline
109  bool isDefined() const {
110  return geometryIsSet() && projectionIsSet() && bboxIsSet();
111  };
112 
113 
114  // Notice that someday this does NOT allocate memory. @see allocate();
115  virtual
116  void setGeometry(unsigned int width, unsigned int height);
117 
119  inline
120  int getFrameWidth() const {return frameWidth; };
121 
123  inline
124  int getFrameHeight() const {return frameHeight; };
125 
126 
128 
134  void setBoundingBox(double lonLL, double latLL, double lonUR, double latUR);
135 
137  inline
139  setBoundingBox(bbox.lowerLeft.x, bbox.lowerLeft.y, bbox.upperRight.x, bbox.upperRight.y);
140  }
141 
142 
144  //inline
145  void setBoundingBoxD(double lonLL, double latLL, double lonUR, double latUR);
146  // { setBoundingBoxR(DEG2RAD*lonLL, DEG2RAD*latLL, DEG2RAD*lonUR, DEG2RAD*latUR);}
147 
149  inline
151  setBoundingBoxD(bboxD.lowerLeft.x, bboxD.lowerLeft.y, bboxD.upperRight.x, bboxD.upperRight.y);
152  }
153 
154 
156  inline
157  void setBoundingBoxR(double lonLL, double latLL, double lonUR, double latUR){
158  setBoundingBoxD(RAD2DEG*lonLL, RAD2DEG*latLL, RAD2DEG*lonUR, RAD2DEG*latUR);
159  }
160 
162  inline
164  setBoundingBoxR(bboxR.lowerLeft.x, bboxR.lowerLeft.y, bboxR.upperRight.x, bboxR.upperRight.y);
165  }
166 
167 
169  void setBoundingBoxM(double xLL, double yLL, double xUR, double yUR);
170 
172  inline
173  /*
174  void setBoundingBoxM(const drain::Rectangle<double> & bboxM) {
175  setBoundingBoxM(bboxM.lowerLeft.x, bboxM.lowerLeft.y, bboxM.upperRight.x, bboxM.upperRight.y);
176  }*/
178  setBoundingBoxM(bboxM[0], bboxM[1], bboxM[2], bboxM[3]);
179  }
180 
181 protected:
182 
184  void updateBoundingBoxR(); //double lonLL, double latLL, double lonUR, double latUR);
185 
187  void updateBoundingBoxD(); //double lonLL, double latLL, double lonUR, double latUR);
188 
190  void updateBoundingBoxM(); // double lonLL, double latLL, double lonUR, double latUR);
191 
192 
194  void updateScaling();
195 
196 public:
197 
198 
199 
201  inline
202  const drain::Rectangle<double> & getBoundingBoxDeg() const { return bBoxD; };
203 
205  inline
207 
209  inline
210  const drain::Rectangle<double> & getBoundingBoxRad() const { return bBoxR; };
211 
212  void getCenterPixel(drain::Rectangle<double> & pixelD) const;
213 
215  inline
216  double getXScale() const {
217  return xScale;
218  }
219 
221  inline
222  double getYScale() const {
223  return yScale;
224  }
225 
226 
227 
229  /*
230  * \par bboxM - bounding box (xLowerLeft, yLowerLeft, xUpperRight, yUpperRight).
231  */
232  inline
234  cropWithM(bboxM.lowerLeft.x, bboxM.lowerLeft.y, bboxM.upperRight.x, bboxM.upperRight.y);
235  }
236 
238  void cropWithM(double xLL, double yLL, double xUR, double yUR);
239 
240 
242  virtual inline
243  void deg2pix(double lon, double lat, int & i, int & j) const {
244  projGeo2Native.projectFwd(lon, lat, lon, lat); // PROJ6-CRS uses degrees
245  // ...Now (lon,lat) are metric
246  // projGeo2Native.projectFwd(lon*DEG2RAD, lat*DEG2RAD, lon, lat); // PROJ6-CRS uses degrees
247  m2pix(lon, lat, i,j);
248  }
249 
251  inline
252  void deg2pix(const drain::Point2D<double> & loc, drain::Point2D<int> & pix) const {
253  deg2pix(loc.x, loc.y, pix.x, pix.y);
254  }
255 
257 
259  virtual inline
260  void deg2m(double lon, double lat, double &x, double &y) const {
261  projGeo2Native.projectFwd(lon, lat, x, y); // PROJ6-CRS uses degrees
262  }
263 
264  inline
265  void deg2m(const drain::Point2D<double> & pDeg, drain::Point2D<double> & pMet) const {
266  deg2m(pDeg.x, pDeg.y, pMet.x, pMet.y);
267  }
268 
270  virtual inline
271  void pix2deg(int i, int j, double & lon, double & lat) const {
272  pix2m(i,j, lon,lat); //pix2m(i,j, x,y);
273  projGeo2Native.projectInv(lon,lat, lon,lat);
274  // lon *= RAD2DEG; // PROJ6-CRS uses degrees
275  // lat *= RAD2DEG; // PROJ6-CRS uses degrees
276  }
277 
279  virtual inline
280  void pix2rad(int i, int j, double & lon, double & lat) const {
281  pix2m(i,j, lon,lat); //pix2m(i,j, x,y);
282  //projGeo2Native.projectInv(lon,lat, lon,lat);
283  projGeo2Native.projectInv(lon,lat, lon,lat);
284  // PROJ6-CRS uses degrees:
285  lon *= DEG2RAD;
286  lat *= DEG2RAD;
287  }
288 
289 
291  inline
292  void pix2deg(const drain::Point2D<int> & pix, drain::Point2D<double> & loc) const {
293  pix2deg(pix.x,pix.y, loc.x,loc.y);
294  }
295 
297  virtual inline
298  void pix2LLdeg(int i,int j, double & lon, double & lat) const {
299  double x, y; // metric
300  pix2LLm(i,j, x,y);
301  projGeo2Native.projectInv(x,y, lon,lat);
302  // lon *= RAD2DEG; // PROJ6-CRS uses degrees
303  // lat *= RAD2DEG; // PROJ6-CRS uses degrees
304  }
305 
307 
309  virtual inline
310  void m2deg(double x, double y, double & lon, double & lat) const {
311  projGeo2Native.projectInv(x,y, lon,lat);
312  // lon *= RAD2DEG; // PROJ6-CRS uses degrees
313  // lat *= RAD2DEG; // PROJ6-CRS uses degrees
314  }
315 
317 
319  virtual inline
320  void m2deg(const drain::Point2D<double> & pMetric, drain::Point2D<double> & pDeg) const {
321  projGeo2Native.projectInv(pMetric.x, pMetric.y, pDeg.x, pDeg.y); // lon, lat);
322  //pDeg.x *= RAD2DEG; // PROJ6-CRS uses degrees
323  //pDeg.y *= RAD2DEG; // PROJ6-CRS uses degrees
324  }
325 
326  /* UNTESTED
327  inline virtual
328  void LLdeg2m(const double & lon, const double & lat, double & x, double & y) const {
329  projGeo2Native.projectFwd(lon,lat, x,y);
330  }
331  */
332 
333 
335 
342  inline virtual
343  void m2pix(double x, double y, int & i, int & j) const {
344  // i = static_cast<int>(0.5+ (x - bBoxNative.lowerLeft.x) / xScale); // xOffset
345  // j = frameHeight-1 - static_cast<int>(0.5+ (y - bBoxNative.lowerLeft.y) / yScale);
346  // j = frameHeight-1 - static_cast<int>(0.5+ (y - bBoxNative.lowerLeft.y) / yScale);
347  i = ::lround((x - bBoxNative.lowerLeft.x) / xScale); // xOffset
348  j = frameHeight-1 - ::lround((y - bBoxNative.lowerLeft.y) / yScale);
349  }
350 
351  inline virtual
352  void m2pix(const drain::Point2D<double> & pMetric, drain::Point2D<int> & pImage) const {
353  // pImage.x = static_cast<int>(0.5+ (pMetric.x - bBoxNative.lowerLeft.x) / xScale); // xOffset
354  // pImage.y = frameHeight-1 - static_cast<int>(0.5+ (pMetric.y - bBoxNative.lowerLeft.y) / yScale);
355  // pImage.y = frameHeight-1 - static_cast<int>(0.5+ (pMetric.y - bBoxNative.lowerLeft.y) / yScale);
356  pImage.x = ::lround( (pMetric.x - bBoxNative.lowerLeft.x) / xScale); // xOffset
357  pImage.y = frameHeight-1 - ::lround((pMetric.y - bBoxNative.lowerLeft.y) / yScale);
358  }
359 
360 
362 
371  inline
372  virtual
373  void pix2m(int i, int j, double & x, double & y) const {
374  x = (static_cast<double>(i)+0.5)*xScale + bBoxNative.lowerLeft.x;
375  y = (static_cast<double>(frameHeight-1 - j)+0.5)*yScale + bBoxNative.lowerLeft.y;
376  }
377 
378  inline
379  virtual
380  void pix2m(const drain::Point2D<int> & pImage, drain::Point2D<double> & pMetric) const {
381  pMetric.x = (static_cast<double>(pImage.x)+0.5)*xScale + bBoxNative.lowerLeft.x;
382  pMetric.y = (static_cast<double>(frameHeight-1 - pImage.y)+0.5)*yScale + bBoxNative.lowerLeft.y;
383  }
384 
386 
393  inline
394  virtual
395  void pix2LLm(int i, int j, double & x, double & y) const {
396  x = (static_cast<double>(i))*xScale + bBoxNative.lowerLeft.x;
397  y = (static_cast<double>(frameHeight-1 - j))*yScale + bBoxNative.lowerLeft.y;
398  // y = (static_cast<double>(frameHeight-1 - j))*yScale + bBoxNative.lowerLeft.y;
399  }
400 
401 
402 
403 
404 
406  inline
407  void setProjection(const std::string & projDef){
410  }
411 
413  inline
414  void setProjectionEPSG(int epsg){
415  projGeo2Native.dst.setProjection(epsg);
417  }
418 
420  void updateProjection();
421 
423  inline
424  const std::string & getProjection() const { return projGeo2Native.getProjectionDst();};
425 
427  // Consider: rename src/input projection coord system
428  inline
429  const std::string & getCoordinateSystem() const { return projGeo2Native.getProjectionSrc();};
430 
431 
433  inline
434  //const drain::Rectangle<double> & getDataBBoxD() const { return dataBBoxD; };
436 
438  inline
439  //const drain::Rectangle<double> & getDataOverlapD() const { return dataOverlapD; };
441 
443 
446  void updateDataExtentDeg(const drain::Rectangle<double> &inputBBoxDeg);
447 
451  void updateDataExtentNat(const drain::Rectangle<double> &inputBBoxNat);
452 
453 
454  inline
455  bool isLongLat() const {
456  return projGeo2Native.isLongLat();
457  }
458 
461 
462  //std::ostream & toOStr(std::ostream & ostr) const ;
463  virtual
464  std::ostream & toStream(std::ostream & ostr) const;
465 
466 
467 
468 protected:
469 
470  // drain::image::AreaGeometry frameGeometry; would be fine, but unsigned caused conversion/cast underflows
471  int frameWidth;
472  int frameHeight;
473 
476 
479 
482 
483 
484  // experimental
487 
488  // experimental
491 
493  // drain::Rectangle<double> dataBBoxD;
494 
496  //drain::Rectangle<double> dataOverlapD;
497 
498  // ... needed in mapping...
499  double xScale = 0.0;
500  double yScale = 0.0;
501 
502 };
503 
504 inline
505 std::ostream & operator<<(std::ostream & ostr, const GeoFrame & frame){
506  frame.GeoFrame::toStream(ostr);
507  ostr << '\n';
508  return ostr;
509 }
510 
511 
512 } // ::image
513 } // ::drain
514 
515 #endif /*COMPOSITOR_R_H_*/
516 
517 // Drain
Definition: Proj6.h:257
void setProjectionDst(const std::string &projDef, Projector::CRS_mode crs=Projector::FORCE_CRS)
Sets destination projection.
Definition: Proj6.h:286
bool isLongLat() const
Check if destination projection is longitude-latitude degrees.
Definition: Proj6.h:482
void projectFwd(double &x, double &y) const
Forward projection (in-place)
Definition: Proj6.h:359
const std::string & getProjectionSrc() const
Returns the projection std::string applied by the last setProjection call.
Definition: Proj6.h:310
void setProjection(const std::string &str, CRS_mode crs=FORCE_CRS)
Sets projection defined as Proj string.
Definition: Proj6.cpp:70
Array with georeferencing support.
Definition: GeoFrame.h:58
drain::Rectangle< double > bBoxR
Geographical scope in Radians.
Definition: GeoFrame.h:475
void setBoundingBox(const drain::Rectangle< double > &bbox)
Sets bounding box in degrees in the target coordinate system.
Definition: GeoFrame.h:138
virtual void m2pix(double x, double y, int &i, int &j) const
Scales geographic map coordinates to image coordinates.
Definition: GeoFrame.h:343
void setProjectionEPSG(int epsg)
Sets the projection of the image as a EPSG code.
Definition: GeoFrame.h:414
void updateScaling()
Geometric scaling.
Definition: GeoFrame.cpp:331
virtual void pix2rad(int i, int j, double &lon, double &lat) const
Calculates the geographic coordinates [rad] of the center of a pixel at (i,j).
Definition: GeoFrame.h:280
virtual void m2deg(const drain::Point2D< double > &pMetric, drain::Point2D< double > &pDeg) const
Convert metric coordinates to degrees.
Definition: GeoFrame.h:320
void updateProjection()
Updates bboxex, if needed.
Definition: GeoFrame.cpp:289
void pix2deg(const drain::Point2D< int > &pix, drain::Point2D< double > &loc) const
Calculates the geographic coordinates of the center of a pixel at (i,j).
Definition: GeoFrame.h:292
const drain::Rectangle< double > & getDataBBoxNat() const
Return the actual geographical boundingBox suggested by implied by input data.
Definition: GeoFrame.h:435
void updateDataExtentNat(const drain::Rectangle< double > &inputBBoxNat)
Definition: GeoFrame.cpp:428
const drain::Rectangle< double > & getBoundingBoxNat() const
Returns the geographical scope in Meters.
Definition: GeoFrame.h:206
void setBoundingBoxR(double lonLL, double latLL, double lonUR, double latUR)
Sets bounding box in radians in the target coordinate system.
Definition: GeoFrame.h:157
double xScale
Utility for deriving extent (degrees) required by input data.
Definition: GeoFrame.h:499
void updateBoundingBoxR()
Proj 6 NEW: Given BBox in geo coords [deg], adjust geo coords [rad].
Definition: GeoFrame.cpp:242
void updateBoundingBoxD()
Given BBox in geo coords [rad], adjust geo coords [deg].
Definition: GeoFrame.cpp:234
bool bboxIsSet() const
Returns true, if the bounding box (geographical extent) has been set.
Definition: GeoFrame.h:99
const drain::Rectangle< double > & getDataOverlapBBoxNat() const
Return the common overlapping geographical area (intersection) implied by input data.
Definition: GeoFrame.h:440
int getFrameHeight() const
Return the nominal height, not the height of the memory array which does not have to be allocated.
Definition: GeoFrame.h:124
GeoFrame(unsigned int width=0, unsigned int height=0)
Default constructor.
Definition: GeoFrame.cpp:60
void updateDataExtentDeg(const drain::Rectangle< double > &inputBBoxDeg)
Extend to include this input.
Definition: GeoFrame.cpp:414
virtual void pix2LLdeg(int i, int j, double &lon, double &lat) const
Calculates the geographic coordinates of the lower left corner of a pixel at (i,j).
Definition: GeoFrame.h:298
drain::Rectangle< double > dataBBoxNat
For deriving extent (~union) of input data.
Definition: GeoFrame.h:486
bool geometryIsSet() const
Return true, if array area is greater than zero.
Definition: GeoFrame.h:89
bool projectionIsSet() const
Returns true, if the geographical extent has been set.
Definition: GeoFrame.h:83
void setBoundingBox(double lonLL, double latLL, double lonUR, double latUR)
Sets bounding box in degrees OR in metres in the target coordinate system.
Definition: GeoFrame.cpp:90
const drain::Rectangle< double > & getBoundingBoxRad() const
Returns the geographical scope in Radians.
Definition: GeoFrame.h:210
void setBoundingBoxM(double xLL, double yLL, double xUR, double yUR)
Sets bounding box in meters in the target coordinate system.
Definition: GeoFrame.cpp:168
virtual void pix2LLm(int i, int j, double &x, double &y) const
Scales image coordinates (i,j) to geographic map coordinates (x,y) of the lower left corner pixel.
Definition: GeoFrame.h:395
void setBoundingBoxM(const drain::UniTuple< double, 4 > &bboxM)
Sets bounding box in meters in the target coordinate system.
Definition: GeoFrame.h:177
drain::Rectangle< double > bBoxNative
Geographical scope in meters, or degrees in case of long/lat.
Definition: GeoFrame.h:481
virtual void pix2m(int i, int j, double &x, double &y) const
Scales image coordinates (i,j) to geographic map coordinates (x,y) in the pixel centres.
Definition: GeoFrame.h:373
virtual void deg2m(double lon, double lat, double &x, double &y) const
Convert metric coordinates to degrees.
Definition: GeoFrame.h:260
void setBoundingBoxR(const drain::Rectangle< double > &bboxR)
Sets bounding box in radians in the target coordinate system.
Definition: GeoFrame.h:163
void updateBoundingBoxM()
Given BBox in geo coords [rad], adjust metric bounding box. Do not update xScale or yScale.
Definition: GeoFrame.cpp:250
virtual void m2deg(double x, double y, double &lon, double &lat) const
Convert metric coordinates to degrees.
Definition: GeoFrame.h:310
bool isDefined() const
Returns true, if the projection, array area and geographical extent bounding box has been set.
Definition: GeoFrame.h:109
virtual void deg2pix(double lon, double lat, int &i, int &j) const
Projects geographic coordinates to image coordinates.
Definition: GeoFrame.h:243
drain::Rectangle< double > dataOverlapBBoxNat
For deriving common extent (~intersection) of input data.
Definition: GeoFrame.h:490
virtual void pix2deg(int i, int j, double &lon, double &lat) const
Calculates the geographic coordinates of the center of a pixel at (i,j).
Definition: GeoFrame.h:271
void setProjection(const std::string &projDef)
Sets the projection of the image as a Proj string.
Definition: GeoFrame.h:407
double getYScale() const
Return vertical resolution of a pixel in meters (if metric) or degrees (if unprojected,...
Definition: GeoFrame.h:222
drain::Proj6 projGeo2Native
Radial to metric.
Definition: GeoFrame.h:460
const std::string & getProjection() const
Returns the projection of the composite image as a proj4 std::string.
Definition: GeoFrame.h:424
void deg2pix(const drain::Point2D< double > &loc, drain::Point2D< int > &pix) const
Project geographic coordinates to image coordinates.
Definition: GeoFrame.h:252
void setBoundingBoxD(const drain::Rectangle< double > &bboxD)
Sets bounding box in degrees in the target coordinate system.
Definition: GeoFrame.h:150
void cropWithM(drain::Rectangle< double > &bboxM)
Crops the initial bounding box with a given bounding box.
Definition: GeoFrame.h:233
const drain::Rectangle< double > & getBoundingBoxDeg() const
Returns the geographical scope in Degrees.
Definition: GeoFrame.h:202
const std::string & getCoordinateSystem() const
Returns the source projection (should be radian array, lon & lat).
Definition: GeoFrame.h:429
double getXScale() const
Return horizontal resolution of a pixel in meters (if metric) or degrees (if unprojected,...
Definition: GeoFrame.h:216
void setBoundingBoxD(double lonLL, double latLL, double lonUR, double latUR)
Sets bounding box in degrees in the target coordinate system.
Definition: GeoFrame.cpp:149
drain::Rectangle< double > bBoxD
Geographical scope in Degrees.
Definition: GeoFrame.h:478
int getFrameWidth() const
Return the nominal width, not the width of the memory array which does not have to be allocated.
Definition: GeoFrame.h:120
Definition: GeoFrame.h:48
Definition: DataSelector.cpp:1277