Rev 391 | Rev 439 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | |||
15 | Werner | 2 | #ifndef GRID_H |
3 | #define GRID_H |
||
4 | |||
22 | Werner | 5 | #include <QtCore> |
15 | Werner | 6 | |
7 | |||
8 | #include <stdexcept> |
||
145 | Werner | 9 | #include <limits> |
150 | iland | 10 | #include <cstring> |
15 | Werner | 11 | |
373 | werner | 12 | #include "global.h" |
13 | |||
247 | werner | 14 | /** Grid class (template). |
15 | Werner | 15 | |
74 | Werner | 16 | Orientation |
17 | The grid is oriented as typically coordinates on the northern hemisphere: greater y-values -> north, greater x-values-> east. |
||
18 | The projection is reversed for drawing on screen (Viewport): |
||
19 | N |
||
20 | (2/0) (2/1) (2/2) |
||
21 | E (1/0) (1/1) (2/1) W |
||
22 | (0/0) (1/0) (2/0) |
||
23 | S |
||
24 | */ |
||
15 | Werner | 25 | template <class T> |
26 | class Grid { |
||
27 | public: |
||
28 | |||
29 | Grid(); |
||
30 | Grid(int cellsize, int sizex, int sizey) { mData=0; setup(cellsize, sizex, sizey); } |
||
58 | Werner | 31 | Grid(const QRectF rect_metric, const float cellsize) { mData=0; setup(rect_metric,cellsize); } |
33 | Werner | 32 | // copy ctor |
33 | Grid(const Grid<T>& toCopy); |
||
105 | Werner | 34 | ~Grid() { clear(); } |
35 | void clear() { if (mData) delete[] mData; mData=0; } |
||
15 | Werner | 36 | |
18 | Werner | 37 | bool setup(const float cellsize, const int sizex, const int sizey); |
22 | Werner | 38 | bool setup(const QRectF& rect, const double cellsize); |
75 | Werner | 39 | void initialize(const T& value) {for( T *p = begin();p!=end(); ++p) *p=value; } |
150 | iland | 40 | void wipe(); ///< write 0-bytes with memcpy to the whole area |
154 | werner | 41 | void wipe(const T value); ///< overwrite the whole area with "value" size of T must be the size of "int" ERRORNOUS!!! |
15 | Werner | 42 | |
145 | Werner | 43 | int sizeX() const { return mSizeX; } |
44 | int sizeY() const { return mSizeY; } |
||
45 | float metricSizeX() const { return mSizeX*mCellsize; } |
||
46 | float metricSizeY() const { return mSizeY*mCellsize; } |
||
49 | Werner | 47 | QRectF metricRect() const { return mRect; } |
145 | Werner | 48 | float cellsize() const { return mCellsize; } |
373 | werner | 49 | int count() const { return mCount; } ///< returns the number of elements of the grid |
50 | bool isEmpty() const { return mData==NULL; } ///< returns false if the grid was not setup |
||
32 | Werner | 51 | // operations |
15 | Werner | 52 | // query |
33 | Werner | 53 | /// access (const) with index variables. use int. |
54 | inline const T& operator()(const int ix, const int iy) const { return constValueAtIndex(ix, iy); } |
||
55 | /// access (const) using metric variables. use float. |
||
56 | inline const T& operator()(const float x, const float y) const { return constValueAt(x, y); } |
||
48 | Werner | 57 | inline const T& operator[] (const QPointF &p) const { return constValueAt(p); } |
33 | Werner | 58 | |
77 | Werner | 59 | inline T& valueAtIndex(const QPoint& pos); ///< value at position defined by indices (x,y) |
33 | Werner | 60 | T& valueAtIndex(const int ix, const int iy) { return valueAtIndex(QPoint(ix,iy)); } ///< const value at position defined by indices (x,y) |
285 | werner | 61 | T& valueAtIndex(const int index) {return mData[index]; } ///< get a ref ot value at (one-dimensional) index 'index'. |
33 | Werner | 62 | |
63 | const T& constValueAtIndex(const QPoint& pos) const; ///< value at position defined by a (integer) QPoint |
||
32 | Werner | 64 | const T& constValueAtIndex(const int ix, const int iy) const { return constValueAtIndex(QPoint(ix,iy)); } |
33 | Werner | 65 | |
66 | T& valueAt(const QPointF& posf); ///< value at position defined by metric coordinates (QPointF) |
||
67 | const T& constValueAt(const QPointF& posf) const; ///< value at position defined by metric coordinates (QPointF) |
||
68 | |||
69 | T& valueAt(const float x, const float y); ///< value at position defined by metric coordinates (x,y) |
||
70 | const T& constValueAt(const float x, const float y) const; ///< value at position defined by metric coordinates (x,y) |
||
71 | |||
105 | Werner | 72 | bool coordValid(const float x, const float y) const { return x>=mRect.left() && x<mRect.right() && y>=mRect.top() && y<mRect.bottom(); } |
49 | Werner | 73 | bool coordValid(const QPointF &pos) const { return coordValid(pos.x(), pos.y()); } |
75 | Werner | 74 | |
55 | Werner | 75 | QPoint indexAt(const QPointF& pos) const { return QPoint(int((pos.x()-mRect.left()) / mCellsize), int((pos.y()-mRect.top())/mCellsize)); } ///< get index of value at position pos (metric) |
373 | werner | 76 | bool isIndexValid(const QPoint& pos) const { return (pos.x()>=0 && pos.x()<mSizeX && pos.y()>=0 && pos.y()<mSizeY); } ///< return true, if position is within the grid |
77 | bool isIndexValid(const int x, const int y) const {return (x>=0 && x<mSizeX && y>=0 && y<mSizeY); } ///< return true, if index is within the grid |
||
75 | Werner | 78 | /// force @param pos to contain valid indices with respect to this grid. |
55 | Werner | 79 | void validate(QPoint &pos) const{ pos.setX( qMax(qMin(pos.x(), mSizeX-1), 0) ); pos.setY( qMax(qMin(pos.y(), mSizeY-1), 0) );} ///< ensure that "pos" is a valid key. if out of range, pos is set to minimum/maximum values. |
105 | Werner | 80 | /// get the (metric) centerpoint of cell with index @p pos |
81 | QPointF cellCenterPoint(const QPoint &pos) { return QPointF( (pos.x()+0.5)*mCellsize+mRect.left(), (pos.y()+0.5)*mCellsize + mRect.top());} ///< get metric coordinates of the cells center |
||
82 | /// get the metric rectangle of the cell with index @pos |
||
55 | Werner | 83 | QRectF cellRect(const QPoint &pos) { QRectF r( QPointF(mRect.left() + mCellsize*pos.x(), mRect.top() + pos.y()*mCellsize), |
84 | QSizeF(mCellsize, mCellsize)); return r; } ///< return coordinates of rect given by @param pos. |
||
105 | Werner | 85 | |
27 | Werner | 86 | inline T* begin() const { return mData; } ///< get "iterator" pointer |
37 | Werner | 87 | inline T* end() const { return mEnd; } ///< get iterator end-pointer |
27 | Werner | 88 | QPoint indexOf(T* element) const; ///< retrieve index (x/y) of the pointer element. returns -1/-1 if element is not valid. |
89 | // special queries |
||
33 | Werner | 90 | T max() const; ///< retrieve the maximum value of a grid |
91 | T sum() const; ///< retrieve the sum of the grid |
||
92 | T avg() const; ///< retrieve the average value of a grid |
||
391 | werner | 93 | // modifying operations |
94 | void add(const T& summand); |
||
95 | void multiply(const T& factor); |
||
33 | Werner | 96 | /// creates a grid with lower resolution and averaged cell values. |
97 | /// @param factor factor by which grid size is reduced (e.g. 3 -> 3x3=9 pixels are averaged to 1 result pixel) |
||
98 | /// @param offsetx, offsety: start averaging with an offset from 0/0 (e.g.: x=1, y=2, factor=3: -> 1/2-3/4 -> 0/0) |
||
99 | /// @return Grid with size sizeX()/factor x sizeY()/factor |
||
100 | Grid<T> averaged(const int factor, const int offsetx=0, const int offsety=0) const; |
||
101 | /// normalized returns a normalized grid, in a way that the sum() = @param targetvalue. |
||
102 | /// if the grid is empty or the sum is 0, no modifications are performed. |
||
103 | Grid<T> normalized(const T targetvalue) const; |
||
373 | werner | 104 | T* ptr(int x, int y) { return &(mData[y*mSizeX + x]); } ///< get a pointer to the element denoted by "x" and "y" |
105 | inline double distance(const QPoint &p1, const QPoint &p2); ///< distance (metric) between p1 and p2 |
||
106 | const QPoint randomPosition() const; ///< returns a (valid) random position within the grid |
||
15 | Werner | 107 | private: |
77 | Werner | 108 | |
15 | Werner | 109 | T* mData; |
37 | Werner | 110 | T* mEnd; ///< pointer to 1 element behind the last |
49 | Werner | 111 | QRectF mRect; |
36 | Werner | 112 | float mCellsize; ///< size of a cell in meter |
113 | int mSizeX; ///< count of cells in x-direction |
||
114 | int mSizeY; ///< count of cells in y-direction |
||
115 | int mCount; ///< total number of cells in the grid |
||
15 | Werner | 116 | }; |
117 | |||
118 | typedef Grid<float> FloatGrid; |
||
119 | |||
438 | werner | 120 | /** @class GridRunner is a helper class to iterate over a rectangular fraction of a grid |
121 | */ |
||
122 | template <class T> |
||
123 | class GridRunner { |
||
124 | public: |
||
125 | GridRunner(Grid<T> &target_grid, const QRectF &rectangle); |
||
126 | T* next(); ///< to to next element, return NULL if finished |
||
127 | private: |
||
128 | T* mLast; |
||
129 | T* mCurrent; |
||
130 | size_t mLineLength; |
||
131 | size_t mCols; |
||
132 | size_t mCurrentCol; |
||
133 | }; |
||
134 | |||
135 | |||
33 | Werner | 136 | // copy constructor |
137 | template <class T> |
||
138 | Grid<T>::Grid(const Grid<T>& toCopy) |
||
139 | { |
||
40 | Werner | 140 | mData = 0; |
50 | Werner | 141 | mRect = toCopy.mRect; |
33 | Werner | 142 | setup(toCopy.cellsize(), toCopy.sizeX(), toCopy.sizeY()); |
143 | const T* end = toCopy.end(); |
||
144 | T* ptr = begin(); |
||
145 | for (T* i= toCopy.begin(); i!=end; ++i, ++ptr) |
||
146 | *ptr = *i; |
||
147 | } |
||
22 | Werner | 148 | |
33 | Werner | 149 | // normalize function |
32 | Werner | 150 | template <class T> |
33 | Werner | 151 | Grid<T> Grid<T>::normalized(const T targetvalue) const |
32 | Werner | 152 | { |
33 | Werner | 153 | Grid<T> target(*this); |
154 | T total = sum(); |
||
155 | T multiplier; |
||
156 | if (total) |
||
157 | multiplier = targetvalue / total; |
||
158 | else |
||
159 | return target; |
||
160 | for (T* p=target.begin();p!=target.end();++p) |
||
161 | *p *= multiplier; |
||
40 | Werner | 162 | return target; |
33 | Werner | 163 | } |
164 | |||
165 | |||
166 | template <class T> |
||
167 | Grid<T> Grid<T>::averaged(const int factor, const int offsetx, const int offsety) const |
||
168 | { |
||
32 | Werner | 169 | Grid<T> target; |
170 | target.setup(cellsize()*factor, sizeX()/factor, sizeY()/factor); |
||
171 | int x,y; |
||
172 | T sum=0; |
||
173 | target.initialize(sum); |
||
174 | // sum over array of 2x2, 3x3, 4x4, ... |
||
175 | for (x=offsetx;x<mSizeX;x++) |
||
176 | for (y=offsety;y<mSizeY;y++) { |
||
177 | target.valueAtIndex((x-offsetx)/factor, (y-offsety)/factor) += constValueAtIndex(x,y); |
||
178 | } |
||
179 | // divide |
||
180 | double fsquare = factor*factor; |
||
181 | for (T* p=target.begin();p!=target.end();++p) |
||
182 | *p /= fsquare; |
||
183 | return target; |
||
184 | } |
||
22 | Werner | 185 | |
15 | Werner | 186 | template <class T> |
22 | Werner | 187 | T& Grid<T>::valueAtIndex(const QPoint& pos) |
188 | { |
||
75 | Werner | 189 | //if (isIndexValid(pos)) { |
77 | Werner | 190 | return mData[pos.y()*mSizeX + pos.x()]; |
75 | Werner | 191 | //} |
192 | //qCritical("Grid::valueAtIndex. invalid: %d/%d", pos.x(), pos.y()); |
||
193 | //return mData[0]; |
||
22 | Werner | 194 | } |
36 | Werner | 195 | |
27 | Werner | 196 | template <class T> |
197 | const T& Grid<T>::constValueAtIndex(const QPoint& pos) const |
||
198 | { |
||
75 | Werner | 199 | //if (isIndexValid(pos)) { |
77 | Werner | 200 | return mData[pos.y()*mSizeX + pos.x()]; |
75 | Werner | 201 | //} |
202 | //qCritical("Grid::constValueAtIndex. invalid: %d/%d", pos.x(), pos.y()); |
||
203 | //return mData[0]; |
||
27 | Werner | 204 | } |
22 | Werner | 205 | |
206 | template <class T> |
||
33 | Werner | 207 | T& Grid<T>::valueAt(const float x, const float y) |
208 | { |
||
209 | return valueAtIndex( indexAt(QPointF(x,y)) ); |
||
210 | } |
||
36 | Werner | 211 | |
33 | Werner | 212 | template <class T> |
213 | const T& Grid<T>::constValueAt(const float x, const float y) const |
||
214 | { |
||
215 | return constValueAtIndex( indexAt(QPointF(x,y)) ); |
||
216 | } |
||
36 | Werner | 217 | |
33 | Werner | 218 | template <class T> |
22 | Werner | 219 | T& Grid<T>::valueAt(const QPointF& posf) |
220 | { |
||
221 | return valueAtIndex( indexAt(posf) ); |
||
222 | } |
||
36 | Werner | 223 | |
33 | Werner | 224 | template <class T> |
225 | const T& Grid<T>::constValueAt(const QPointF& posf) const |
||
226 | { |
||
227 | return constValueAtIndex( indexAt(posf) ); |
||
228 | } |
||
22 | Werner | 229 | |
230 | template <class T> |
||
15 | Werner | 231 | Grid<T>::Grid() |
232 | { |
||
37 | Werner | 233 | mData = 0; mCellsize=0.f; |
234 | mEnd = 0; |
||
15 | Werner | 235 | } |
236 | |||
237 | template <class T> |
||
18 | Werner | 238 | bool Grid<T>::setup(const float cellsize, const int sizex, const int sizey) |
15 | Werner | 239 | { |
37 | Werner | 240 | mSizeX=sizex; mSizeY=sizey; mCellsize=cellsize; |
50 | Werner | 241 | if (mRect.isNull()) // only set rect if not set before |
242 | mRect.setCoords(0., 0., cellsize*sizex, cellsize*sizey); |
||
15 | Werner | 243 | mCount = mSizeX*mSizeY; |
37 | Werner | 244 | if (mData) { |
245 | delete[] mData; mData=NULL; |
||
246 | } |
||
15 | Werner | 247 | if (mCount>0) |
37 | Werner | 248 | mData = new T[mCount]; |
249 | mEnd = &(mData[mCount]); |
||
15 | Werner | 250 | return true; |
251 | } |
||
252 | |||
253 | template <class T> |
||
22 | Werner | 254 | bool Grid<T>::setup(const QRectF& rect, const double cellsize) |
15 | Werner | 255 | { |
49 | Werner | 256 | mRect = rect; |
22 | Werner | 257 | int dx = int(rect.width()/cellsize); |
49 | Werner | 258 | if (mRect.left()+cellsize*dx<rect.right()) |
22 | Werner | 259 | dx++; |
260 | int dy = int(rect.height()/cellsize); |
||
49 | Werner | 261 | if (mRect.top()+cellsize*dy<rect.bottom()) |
22 | Werner | 262 | dy++; |
263 | return setup(cellsize, dx, dy); |
||
15 | Werner | 264 | } |
265 | |||
261 | werner | 266 | /** retrieve from the index from an element reversely from a pointer to that element. |
267 | The internal memory layout is (for dimx=6, dimy=3): |
||
268 | |||
269 | 6 7 8 9 10 11 |
||
270 | 12 13 14 15 16 17 |
||
271 | Note: north and south are reversed, thus the item with index 0 is located in the south-western edge of the grid! */ |
||
25 | Werner | 272 | template <class T> |
27 | Werner | 273 | QPoint Grid<T>::indexOf(T* element) const |
25 | Werner | 274 | { |
275 | QPoint result(-1,-1); |
||
276 | if (element==NULL || element<mData || element>=end()) |
||
277 | return result; |
||
278 | int idx = element - mData; |
||
105 | Werner | 279 | result.setX( idx % mSizeX); |
280 | result.setY( idx / mSizeX); |
||
25 | Werner | 281 | return result; |
282 | } |
||
22 | Werner | 283 | |
27 | Werner | 284 | template <class T> |
285 | T Grid<T>::max() const |
||
286 | { |
||
143 | Werner | 287 | T maxv = -std::numeric_limits<T>::max(); |
27 | Werner | 288 | T* p; |
289 | T* pend = end(); |
||
290 | for (p=begin(); p!=pend;++p) |
||
291 | maxv = std::max(maxv, *p); |
||
292 | return maxv; |
||
293 | } |
||
294 | |||
33 | Werner | 295 | template <class T> |
296 | T Grid<T>::sum() const |
||
297 | { |
||
298 | T* pend = end(); |
||
299 | T total = 0; |
||
300 | for (T *p=begin(); p!=pend;++p) |
||
301 | total += *p; |
||
302 | return total; |
||
303 | } |
||
304 | |||
305 | template <class T> |
||
306 | T Grid<T>::avg() const |
||
307 | { |
||
308 | if (count()) |
||
309 | return sum() / T(count()); |
||
310 | else return 0; |
||
311 | } |
||
312 | |||
150 | iland | 313 | template <class T> |
391 | werner | 314 | void Grid<T>::add(const T& summand) |
315 | { |
||
316 | T* pend = end(); |
||
317 | for (T *p=begin(); p!=pend;*p+=summand,++p) |
||
318 | ; |
||
319 | } |
||
320 | |||
321 | template <class T> |
||
322 | void Grid<T>::multiply(const T& factor) |
||
323 | { |
||
324 | T* pend = end(); |
||
325 | for (T *p=begin(); p!=pend;*p*=factor,++p) |
||
326 | ; |
||
327 | } |
||
328 | |||
329 | |||
330 | |||
331 | template <class T> |
||
150 | iland | 332 | void Grid<T>::wipe() |
333 | { |
||
334 | memset(mData, 0, mCount*sizeof(T)); |
||
335 | } |
||
336 | template <class T> |
||
337 | void Grid<T>::wipe(const T value) |
||
338 | { |
||
154 | werner | 339 | /* this does not work properly !!! */ |
153 | werner | 340 | if (sizeof(T)==sizeof(int)) { |
341 | float temp = value; |
||
342 | float *pf = &temp; |
||
343 | |||
344 | memset(mData, *((int*)pf), mCount*sizeof(T)); |
||
345 | } else |
||
150 | iland | 346 | initialize(value); |
347 | } |
||
348 | |||
373 | werner | 349 | template <class T> |
350 | double Grid<T>::distance(const QPoint &p1, const QPoint &p2) |
||
351 | { |
||
352 | QPointF fp1=cellCenterPoint(p1); |
||
353 | QPointF fp2=cellCenterPoint(p2); |
||
354 | double distance = sqrt( (fp1.x()-fp2.x())*(fp1.x()-fp2.x()) + (fp1.y()-fp2.y())*(fp1.y()-fp2.y())); |
||
355 | return distance; |
||
356 | } |
||
357 | |||
358 | template <class T> |
||
359 | const QPoint Grid<T>::randomPosition() const |
||
360 | { |
||
361 | return QPoint(irandom(0,mSizeX-1), irandom(0, mSizeY-1)); |
||
362 | } |
||
438 | werner | 363 | |
373 | werner | 364 | //////////////////////////////////////////////////////////// |
438 | werner | 365 | // grid runner |
366 | //////////////////////////////////////////////////////////// |
||
367 | template <class T> |
||
368 | GridRunner<T>::GridRunner(Grid<T> &target_grid, const QRectF &rectangle) |
||
369 | { |
||
370 | QPoint upper_left = target_grid.indexAt(rectangle.topLeft()); |
||
371 | QPoint lower_right = target_grid.indexAt(rectangle.bottomRight()); |
||
372 | mCurrent = target_grid.ptr(upper_left.x(), upper_left.y()); |
||
373 | mLast = target_grid.ptr(lower_right.x()-1, lower_right.y()-1); |
||
374 | mCols = lower_right.x() - upper_left.x(); // |
||
375 | mLineLength = target_grid.sizeX() - mCols; |
||
376 | mCurrentCol = 0; |
||
377 | } |
||
378 | |||
379 | template <class T> |
||
380 | T* GridRunner<T>::next() |
||
381 | { |
||
382 | if (mCurrent>mLast) |
||
383 | return NULL; |
||
384 | T* t = mCurrent; |
||
385 | mCurrent++; |
||
386 | mCurrentCol++; |
||
387 | if (mCurrentCol >= mCols) { |
||
388 | mCurrent += mLineLength; // skip to next line |
||
389 | mCurrentCol = 0; |
||
390 | } |
||
391 | return t; |
||
392 | } |
||
393 | |||
394 | //////////////////////////////////////////////////////////// |
||
36 | Werner | 395 | // global functions |
373 | werner | 396 | //////////////////////////////////////////////////////////// |
36 | Werner | 397 | |
398 | /// dumps a FloatGrid to a String. |
||
46 | Werner | 399 | /// rows will be y-lines, columns x-values. (see grid.cpp) |
36 | Werner | 400 | QString gridToString(const FloatGrid &grid); |
401 | |||
402 | /// creates and return a QImage from Grid-Data. |
||
403 | /// @param black_white true: max_value = white, min_value = black, false: color-mode: uses a HSV-color model from blue (min_value) to red (max_value), default: color mode (false) |
||
404 | /// @param min_value, max_value min/max bounds for color calcuations. values outside bounds are limited to these values. defaults: min=0, max=1 |
||
405 | /// @param reverse if true, color ramps are inversed (to: min_value = white (black and white mode) or red (color mode). default = false. |
||
406 | /// @return a QImage with the Grids size of pixels. Pixel coordinates relate to the index values of the grid. |
||
407 | QImage gridToImage(const FloatGrid &grid, |
||
408 | bool black_white=false, |
||
409 | double min_value=0., double max_value=1., |
||
410 | bool reverse=false); |
||
411 | |||
285 | werner | 412 | /** load into 'rGrid' the content of the image pointed at by 'fileName'. |
413 | Pixels are converted to grey-scale and then transformend to a value ranging from 0..1 (black..white). |
||
414 | */ |
||
415 | bool loadGridFromImage(const QString &fileName, FloatGrid &rGrid); |
||
416 | |||
46 | Werner | 417 | /// template version for non-float grids (see also version for FloatGrid) |
36 | Werner | 418 | template <class T> |
46 | Werner | 419 | QString gridToString(const Grid<T> &grid) |
36 | Werner | 420 | { |
421 | QString res; |
||
422 | QTextStream ts(&res); |
||
423 | |||
46 | Werner | 424 | for (int y=0;y<grid.sizeY();y++){ |
425 | for (int x=0;x<grid.sizeX();x++){ |
||
36 | Werner | 426 | ts << grid.constValueAtIndex(x,y) << ";"; |
427 | } |
||
428 | ts << "\r\n"; |
||
429 | } |
||
430 | |||
431 | return res; |
||
432 | } |
||
46 | Werner | 433 | |
15 | Werner | 434 | #endif // GRID_H |