pops-core  0.9
PoPS (Pest or Pathogen Spread) Model Core C++ library
raster.hpp
Go to the documentation of this file.
1 #ifndef POPS_RASTER_HPP
2 #define POPS_RASTER_HPP
3 
4 /*
5  * PoPS model - native raster manipulation
6  *
7  * Copyright (C) 2015-2020 by the authors.
8  *
9  * Authors: Vaclav Petras <wenzeslaus gmail com>
10  * Completely rewritten by Vaclav Petras based on
11  * version by Zexi Chen <zchen22 ncsu edu>.
12  *
13  * The code contained herein is licensed under the GNU General Public
14  * License. You may obtain a copy of the GNU General Public License
15  * Version 2 or later at the following locations:
16  *
17  * http://www.opensource.org/licenses/gpl-license.html
18  * http://www.gnu.org/copyleft/gpl.html
19  */
20 
21 #include <iostream>
22 #include <cmath>
23 #include <algorithm>
24 #include <stdexcept>
25 #include <initializer_list>
26 #include <type_traits>
27 
28 namespace pops {
29 
33 template<class InputIt1, class InputIt2, class BinaryOperation>
34 BinaryOperation
35 for_each_zip(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryOperation f)
36 {
37  for (; first1 != last1; ++first1, ++first2) {
38  f(*first1, *first2);
39  }
40  return f;
41 }
42 
96 template<typename Number, typename Index = int>
97 class Raster
98 {
99 protected:
100  Index rows_;
101  Index cols_;
102  Number* data_;
103  // owning is true for any state which is not using someone's data
104  bool owns_;
105 
106 public:
107  typedef Number NumberType;
108  typedef Index IndexType;
109 
110  Raster() : owns_(true)
111  {
112  cols_ = 0;
113  rows_ = 0;
114  data_ = NULL;
115  }
116 
117  Raster(const Raster& other) : owns_(true)
118  {
119  cols_ = other.cols_;
120  rows_ = other.rows_;
121  data_ = new Number[cols_ * rows_];
122  std::copy(other.data_, other.data_ + (cols_ * rows_), data_);
123  }
124 
129  Raster(const Raster& other, Number value) : owns_(true)
130  {
131  cols_ = other.cols_;
132  rows_ = other.rows_;
133  data_ = new Number[cols_ * rows_]{value};
134  }
135 
136  Raster(Raster&& other) : owns_(other.owns_)
137  {
138  cols_ = other.cols_;
139  rows_ = other.rows_;
140  data_ = other.data_;
141  other.data_ = nullptr;
142  }
143 
144  Raster(Index rows, Index cols) : owns_(true)
145  {
146  this->cols_ = cols;
147  this->rows_ = rows;
148  this->data_ = new Number[cols_ * rows_];
149  }
150 
151  Raster(Index rows, Index cols, Number value) : owns_(true)
152  {
153  this->cols_ = cols;
154  this->rows_ = rows;
155  this->data_ = new Number[cols_ * rows_]{value};
156  }
157 
165  Raster(Number* data, Index rows, Index cols)
166  : rows_(rows), cols_(cols), data_(data), owns_(false)
167  {}
168 
169  // maybe remove from the class, or make it optional together with
170  // a reference
171  Raster(std::initializer_list<std::initializer_list<Number>> l)
172  : Raster(l.size(), l.begin()->size())
173  {
174  Index i = 0;
175  Index j = 0;
176  for (const auto& subl : l) {
177  for (const auto& value : subl) {
178  data_[cols_ * i + j] = value;
179  ++j;
180  }
181  j = 0;
182  ++i;
183  }
184  }
185 
187  {
188  if (data_ && owns_) {
189  delete[] data_;
190  }
191  }
192 
193  Index cols() const
194  {
195  return cols_;
196  }
197 
198  Index rows() const
199  {
200  return rows_;
201  }
202 
208  Number* data() noexcept
209  {
210  return data_;
211  }
212 
217  const Number* data() const noexcept
218  {
219  return data_;
220  }
221 
222  void fill(Number value)
223  {
224  std::fill(data_, data_ + (cols_ * rows_), value);
225  }
226 
227  void zero()
228  {
229  std::fill(data_, data_ + (cols_ * rows_), 0);
230  }
231 
232  template<class UnaryOperation>
233  void for_each(UnaryOperation op) const
234  {
235  std::for_each(data_, data_ + (cols_ * rows_), op);
236  }
237 
238  const Number& operator()(Index row, Index col) const
239  {
240  return data_[row * cols_ + col];
241  }
242 
243  Number& operator()(Index row, Index col)
244  {
245  return data_[row * cols_ + col];
246  }
247 
248  Raster& operator=(const Raster& other)
249  {
250  if (this != &other) {
251  if (data_ && owns_)
252  delete[] data_;
253  cols_ = other.cols_;
254  rows_ = other.rows_;
255  data_ = new Number[cols_ * rows_];
256  std::copy(other.data_, other.data_ + (cols_ * rows_), data_);
257  }
258  return *this;
259  }
260 
262  {
263  if (this != &other) {
264  if (data_ && owns_)
265  delete[] data_;
266  cols_ = other.cols_;
267  rows_ = other.rows_;
268  data_ = other.data_;
269  owns_ = other.owns_;
270  other.data_ = nullptr;
271  }
272  return *this;
273  }
274 
275  template<typename OtherNumber>
276  Raster& operator+=(OtherNumber value)
277  {
278  std::for_each(
279  data_, data_ + (cols_ * rows_), [&value](Number& a) { a += value; });
280  return *this;
281  }
282 
283  template<typename OtherNumber>
284  Raster& operator-=(OtherNumber value)
285  {
286  std::for_each(
287  data_, data_ + (cols_ * rows_), [&value](Number& a) { a -= value; });
288  return *this;
289  }
290 
291  template<typename OtherNumber>
292  Raster& operator*=(OtherNumber value)
293  {
294  std::for_each(
295  data_, data_ + (cols_ * rows_), [&value](Number& a) { a *= value; });
296  return *this;
297  }
298 
299  template<typename OtherNumber>
300  Raster& operator/=(OtherNumber value)
301  {
302  std::for_each(
303  data_, data_ + (cols_ * rows_), [&value](Number& a) { a /= value; });
304  return *this;
305  }
306 
307  template<typename OtherNumber>
308  typename std::enable_if<
309  std::is_floating_point<Number>::value
310  || std::is_same<Number, OtherNumber>::value,
311  Raster&>::type
313  {
314  for_each_zip(
315  data_,
316  data_ + (cols_ * rows_),
317  image.data(),
318  [](Number& a, const OtherNumber& b) { a += b; });
319  return *this;
320  }
321 
322  template<typename OtherNumber>
323  typename std::enable_if<
324  std::is_floating_point<Number>::value
325  || std::is_same<Number, OtherNumber>::value,
326  Raster&>::type
328  {
329  for_each_zip(
330  data_,
331  data_ + (cols_ * rows_),
332  image.data(),
333  [](Number& a, const OtherNumber& b) { a -= b; });
334  return *this;
335  }
336 
337  template<typename OtherNumber>
338  typename std::enable_if<
339  std::is_floating_point<Number>::value
340  || std::is_same<Number, OtherNumber>::value,
341  Raster&>::type
343  {
344  for_each_zip(
345  data_,
346  data_ + (cols_ * rows_),
347  image.data(),
348  [](Number& a, const OtherNumber& b) { a *= b; });
349  return *this;
350  }
351 
352  template<typename OtherNumber>
353  typename std::enable_if<
354  std::is_floating_point<Number>::value
355  || std::is_same<Number, OtherNumber>::value,
356  Raster&>::type
358  {
359  for_each_zip(
360  data_,
361  data_ + (cols_ * rows_),
362  image.data(),
363  [](Number& a, const OtherNumber& b) { a /= b; });
364  return *this;
365  }
366 
367  bool operator==(const Raster& other) const
368  {
369  // TODO: assumes same sizes
370  for (Index i = 0; i < cols_; i++) {
371  for (Index j = 0; j < cols_; j++) {
372  if (this->data_[i * cols_ + j] != other.data_[i * cols_ + j])
373  return false;
374  }
375  }
376  return true;
377  }
378 
379  bool operator!=(const Raster& other) const
380  {
381  // TODO: assumes same sizes
382  for (Index i = 0; i < cols_; i++) {
383  for (Index j = 0; j < cols_; j++) {
384  if (this->data_[i * cols_ + j] != other.data_[i * cols_ + j])
385  return true;
386  }
387  }
388  return false;
389  }
390 
391  template<typename OtherNumber>
392  friend inline
393  typename std::enable_if<std::is_arithmetic<OtherNumber>::value, Raster>::type
394  operator+(const Raster& raster, OtherNumber value)
395  {
396  auto out = Raster(raster.rows(), raster.cols());
397 
398  std::transform(
399  raster.data(),
400  raster.data() + (raster.cols() * raster.rows()),
401  out.data(),
402  [&value](const Number& a) { return a + value; });
403  return out;
404  }
405 
406  template<typename OtherNumber>
407  friend inline
408  typename std::enable_if<std::is_arithmetic<OtherNumber>::value, Raster>::type
409  operator-(const Raster& raster, OtherNumber value)
410  {
411  auto out = Raster(raster.rows(), raster.cols());
412 
413  std::transform(
414  raster.data(),
415  raster.data() + (raster.cols() * raster.rows()),
416  out.data(),
417  [&value](const Number& a) { return a - value; });
418  return out;
419  }
420 
421  template<typename OtherNumber>
422  friend inline
423  typename std::enable_if<std::is_arithmetic<OtherNumber>::value, Raster>::type
424  operator*(const Raster& raster, OtherNumber value)
425  {
426  auto out = Raster(raster.rows(), raster.cols());
427 
428  std::transform(
429  raster.data(),
430  raster.data() + (raster.cols() * raster.rows()),
431  out.data(),
432  [&value](const Number& a) { return a * value; });
433  return out;
434  }
435 
436  template<typename OtherNumber>
437  friend inline
438  typename std::enable_if<std::is_arithmetic<OtherNumber>::value, Raster>::type
439  operator/(const Raster& raster, OtherNumber value)
440  {
441  auto out = Raster(raster.rows(), raster.cols());
442 
443  std::transform(
444  raster.data(),
445  raster.data() + (raster.cols() * raster.rows()),
446  out.data(),
447  [&value](const Number& a) { return a / value; });
448  return out;
449  }
450 
451  template<typename OtherNumber>
452  friend inline
453  typename std::enable_if<std::is_arithmetic<OtherNumber>::value, Raster>::type
454  operator+(OtherNumber value, const Raster& raster)
455  {
456  return raster + value;
457  }
458 
459  template<typename OtherNumber>
460  friend inline
461  typename std::enable_if<std::is_arithmetic<OtherNumber>::value, Raster>::type
462  operator-(OtherNumber value, const Raster& raster)
463  {
464  auto out = Raster(raster.rows(), raster.cols());
465 
466  std::transform(
467  raster.data(),
468  raster.data() + (raster.cols() * raster.rows()),
469  out.data(),
470  [&value](const Number& a) { return value - a; });
471  return out;
472  }
473 
474  template<typename OtherNumber>
475  friend inline
476  typename std::enable_if<std::is_arithmetic<OtherNumber>::value, Raster>::type
477  operator*(OtherNumber value, const Raster& raster)
478  {
479  return raster * value;
480  }
481 
482  template<typename OtherNumber>
483  friend inline
484  typename std::enable_if<std::is_arithmetic<OtherNumber>::value, Raster>::type
485  operator/(OtherNumber value, const Raster& raster)
486  {
487  auto out = Raster(raster.rows(), raster.cols());
488 
489  std::transform(
490  raster.data(),
491  raster.data() + (raster.cols() * raster.rows()),
492  out.data(),
493  [&value](const Number& a) { return value / a; });
494  return out;
495  }
496 
497  friend inline Raster pow(Raster image, double value)
498  {
499  image.for_each([value](Number& a) { a = std::pow(a, value); });
500  return image;
501  }
502  friend inline Raster sqrt(Raster image)
503  {
504  image.for_each([](Number& a) { a = std::sqrt(a); });
505  return image;
506  }
507 
508  friend inline std::ostream& operator<<(std::ostream& stream, const Raster& image)
509  {
510  stream << "[[";
511  for (Index i = 0; i < image.rows_; i++) {
512  if (i != 0)
513  stream << "],\n [";
514  for (Index j = 0; j < image.cols_; j++) {
515  if (j != 0)
516  stream << ", ";
517  stream << image.data_[i * image.cols_ + j];
518  }
519  }
520  stream << "]]\n";
521  return stream;
522  }
523 };
524 
525 template<
526  typename LeftNumber,
527  typename RightNumber,
528  typename ResultNumber = typename std::common_type<LeftNumber, RightNumber>::type>
529 Raster<ResultNumber>
531 {
532  if (lhs.cols() != rhs.cols() || lhs.rows() != rhs.rows()) {
533  throw std::invalid_argument(
534  "Raster::operator+: The number of rows or columns does not match");
535  }
536  auto out = Raster<ResultNumber>(lhs.rows(), lhs.cols());
537 
538  std::transform(
539  lhs.data(),
540  lhs.data() + (lhs.cols() * lhs.rows()),
541  rhs.data(),
542  out.data(),
543  [](const LeftNumber& a, const RightNumber& b) { return a + b; });
544  return out;
545 }
546 
547 template<
548  typename LeftNumber,
549  typename RightNumber,
550  typename ResultNumber = typename std::common_type<LeftNumber, RightNumber>::type>
551 Raster<ResultNumber>
553 {
554  if (lhs.cols() != rhs.cols() || lhs.rows() != rhs.rows()) {
555  throw std::invalid_argument(
556  "Raster::operator-: The number of rows or columns does not match");
557  }
558  auto out = Raster<ResultNumber>(lhs.rows(), lhs.cols());
559 
560  std::transform(
561  lhs.data(),
562  lhs.data() + (lhs.cols() * lhs.rows()),
563  rhs.data(),
564  out.data(),
565  [](const LeftNumber& a, const RightNumber& b) { return a - b; });
566  return out;
567 }
568 
569 template<
570  typename LeftNumber,
571  typename RightNumber,
572  typename ResultNumber = typename std::common_type<LeftNumber, RightNumber>::type>
573 Raster<ResultNumber>
575 {
576  if (lhs.cols() != rhs.cols() || lhs.rows() != rhs.rows()) {
577  throw std::invalid_argument(
578  "Raster::operator*: The number of rows or columns does not match");
579  }
580  auto out = Raster<ResultNumber>(lhs.rows(), lhs.cols());
581 
582  std::transform(
583  lhs.data(),
584  lhs.data() + (lhs.cols() * lhs.rows()),
585  rhs.data(),
586  out.data(),
587  [](const LeftNumber& a, const RightNumber& b) { return a * b; });
588  return out;
589 }
590 
591 template<
592  typename LeftNumber,
593  typename RightNumber,
594  typename ResultNumber = typename std::common_type<LeftNumber, RightNumber>::type>
595 Raster<ResultNumber>
597 {
598  if (lhs.cols() != rhs.cols() || lhs.rows() != rhs.rows()) {
599  throw std::invalid_argument(
600  "Raster::operator/: The number of rows or columns does not match");
601  }
602  auto out = Raster<ResultNumber>(lhs.rows(), lhs.cols());
603 
604  std::transform(
605  lhs.data(),
606  lhs.data() + (lhs.cols() * lhs.rows()),
607  rhs.data(),
608  out.data(),
609  [](const LeftNumber& a, const RightNumber& b) { return a / b; });
610  return out;
611 }
612 
613 } // namespace pops
614 
615 #endif // POPS_RASTER_HPP
pops::Raster::Raster
Raster(Raster &&other)
Definition: raster.hpp:136
pops::Raster::fill
void fill(Number value)
Definition: raster.hpp:222
pops::operator+
Raster< ResultNumber > operator+(const Raster< LeftNumber > &lhs, const Raster< RightNumber > &rhs)
Definition: raster.hpp:530
pops::Raster::~Raster
~Raster()
Definition: raster.hpp:186
pops::Raster::operator-
friend std::enable_if< std::is_arithmetic< OtherNumber >::value, Raster >::type operator-(const Raster &raster, OtherNumber value)
Definition: raster.hpp:409
pops::Raster::Raster
Raster(std::initializer_list< std::initializer_list< Number >> l)
Definition: raster.hpp:171
pops::Raster::operator=
Raster & operator=(const Raster &other)
Definition: raster.hpp:248
pops::Raster::operator+=
Raster & operator+=(OtherNumber value)
Definition: raster.hpp:276
pops::Raster::Raster
Raster(Index rows, Index cols, Number value)
Definition: raster.hpp:151
pops::Raster::Raster
Raster(Index rows, Index cols)
Definition: raster.hpp:144
pops::Raster::rows_
Index rows_
Definition: raster.hpp:100
pops::Raster::cols_
Index cols_
Definition: raster.hpp:101
pops::operator-
Raster< ResultNumber > operator-(const Raster< LeftNumber > &lhs, const Raster< RightNumber > &rhs)
Definition: raster.hpp:552
pops::Raster::zero
void zero()
Definition: raster.hpp:227
pops::Raster::data
const Number * data() const noexcept
Returns pointer for direct access the underlying array.
Definition: raster.hpp:217
pops::Raster::operator/=
std::enable_if< std::is_floating_point< Number >::value||std::is_same< Number, OtherNumber >::value, Raster & >::type operator/=(const Raster< OtherNumber > &image)
Definition: raster.hpp:357
pops::Raster::operator/=
Raster & operator/=(OtherNumber value)
Definition: raster.hpp:300
pops::Raster::operator/
friend std::enable_if< std::is_arithmetic< OtherNumber >::value, Raster >::type operator/(OtherNumber value, const Raster &raster)
Definition: raster.hpp:485
pops::Raster::NumberType
Number NumberType
Definition: raster.hpp:107
pops::Raster::sqrt
friend Raster sqrt(Raster image)
Definition: raster.hpp:502
pops::Raster::operator*
friend std::enable_if< std::is_arithmetic< OtherNumber >::value, Raster >::type operator*(const Raster &raster, OtherNumber value)
Definition: raster.hpp:424
pops::Raster::cols
Index cols() const
Definition: raster.hpp:193
pops::Raster::operator()
Number & operator()(Index row, Index col)
Definition: raster.hpp:243
pops::Raster::operator()
const Number & operator()(Index row, Index col) const
Definition: raster.hpp:238
pops::Raster::operator!=
bool operator!=(const Raster &other) const
Definition: raster.hpp:379
pops::Raster::data
Number * data() noexcept
Returns pointer for direct access the underlying array.
Definition: raster.hpp:208
pops::operator/
Raster< ResultNumber > operator/(const Raster< LeftNumber > &lhs, const Raster< RightNumber > &rhs)
Definition: raster.hpp:596
pops::Raster::operator+
friend std::enable_if< std::is_arithmetic< OtherNumber >::value, Raster >::type operator+(const Raster &raster, OtherNumber value)
Definition: raster.hpp:394
pops::Raster::operator=
Raster & operator=(Raster &&other)
Definition: raster.hpp:261
pops
Definition: cauchy_kernel.hpp:25
pops::Raster::operator==
bool operator==(const Raster &other) const
Definition: raster.hpp:367
pops::Raster::operator-=
Raster & operator-=(OtherNumber value)
Definition: raster.hpp:284
pops::Raster
Representation of a raster image.
Definition: raster.hpp:97
pops::Raster::operator+
friend std::enable_if< std::is_arithmetic< OtherNumber >::value, Raster >::type operator+(OtherNumber value, const Raster &raster)
Definition: raster.hpp:454
pops::Raster::operator*
friend std::enable_if< std::is_arithmetic< OtherNumber >::value, Raster >::type operator*(OtherNumber value, const Raster &raster)
Definition: raster.hpp:477
pops::Raster::operator*=
std::enable_if< std::is_floating_point< Number >::value||std::is_same< Number, OtherNumber >::value, Raster & >::type operator*=(const Raster< OtherNumber > &image)
Definition: raster.hpp:342
pops::Raster::pow
friend Raster pow(Raster image, double value)
Definition: raster.hpp:497
pops::Raster::operator-=
std::enable_if< std::is_floating_point< Number >::value||std::is_same< Number, OtherNumber >::value, Raster & >::type operator-=(const Raster< OtherNumber > &image)
Definition: raster.hpp:327
pops::for_each_zip
BinaryOperation for_each_zip(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryOperation f)
Iterate over two ranges and apply a binary function which modifies the first parameter.
Definition: raster.hpp:35
pops::Raster::operator*=
Raster & operator*=(OtherNumber value)
Definition: raster.hpp:292
pops::operator*
Raster< ResultNumber > operator*(const Raster< LeftNumber > &lhs, const Raster< RightNumber > &rhs)
Definition: raster.hpp:574
pops::Raster::for_each
void for_each(UnaryOperation op) const
Definition: raster.hpp:233
pops::Raster::Raster
Raster(Number *data, Index rows, Index cols)
Use existing data storage.
Definition: raster.hpp:165
pops::Raster::Raster
Raster(const Raster &other, Number value)
Initialize size using another raster, but use given value.
Definition: raster.hpp:129
pops::Raster::operator<<
friend std::ostream & operator<<(std::ostream &stream, const Raster &image)
Definition: raster.hpp:508
pops::Raster::Raster
Raster()
Definition: raster.hpp:110
pops::Raster::data_
Number * data_
Definition: raster.hpp:102
pops::Raster::operator-
friend std::enable_if< std::is_arithmetic< OtherNumber >::value, Raster >::type operator-(OtherNumber value, const Raster &raster)
Definition: raster.hpp:462
pops::Raster::Raster
Raster(const Raster &other)
Definition: raster.hpp:117
pops::Raster::operator/
friend std::enable_if< std::is_arithmetic< OtherNumber >::value, Raster >::type operator/(const Raster &raster, OtherNumber value)
Definition: raster.hpp:439
pops::Raster::operator+=
std::enable_if< std::is_floating_point< Number >::value||std::is_same< Number, OtherNumber >::value, Raster & >::type operator+=(const Raster< OtherNumber > &image)
Definition: raster.hpp:312
pops::Raster::owns_
bool owns_
Definition: raster.hpp:104
pops::Raster::IndexType
Index IndexType
Definition: raster.hpp:108
pops::Raster::rows
Index rows() const
Definition: raster.hpp:198