1 // Copyright (c) 2018-2021 Robert J. Hijmans
2 //
3 // This file is part of the "spat" library.
4 //
5 // spat is free software: you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // spat is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with spat. If not, see <http://www.gnu.org/licenses/>.
17
18 #include "spatRaster.h"
19 #include "distance.h"
20 #include <limits>
21 #include <cmath>
22 #include "geodesic.h"
23 #include "recycle.h"
24 #include "math_utils.h"
25 #include "vecmath.h"
26
shortDistPoints(std::vector<double> & d,const std::vector<double> & x,const std::vector<double> & y,const std::vector<double> & px,const std::vector<double> & py,const bool & lonlat,const double & lindist)27 void shortDistPoints(std::vector<double> &d, const std::vector<double> &x, const std::vector<double> &y, const std::vector<double> &px, const std::vector<double> &py, const bool& lonlat, const double &lindist) {
28 if (lonlat) {
29 distanceToNearest_lonlat(d, x, y, px, py);
30 } else {
31 distanceToNearest_plane(d, x, y, px, py, lindist);
32 }
33 }
34
35
36
distance_vector_rasterize(SpatVector p,bool align_points,SpatOptions & opt)37 SpatRaster SpatRaster::distance_vector_rasterize(SpatVector p, bool align_points, SpatOptions &opt) {
38
39 SpatRaster out = geometry();
40 if (source[0].srs.wkt == "") {
41 out.setError("CRS not defined");
42 return(out);
43 }
44
45 double m = source[0].srs.to_meter();
46 m = std::isnan(m) ? 1 : m;
47
48 SpatRaster x;
49 std::string gtype = p.type();
50 std::vector<std::vector<double>> pxy;
51 if (gtype == "points") {
52 pxy = p.coordinates();
53 if (align_points) {
54 std::vector<double> cells = cellFromXY(pxy[0], pxy[1]);
55 cells.erase(std::unique(cells.begin(), cells.end()), cells.end());
56 pxy = xyFromCell(cells);
57 }
58 } else {
59 SpatOptions ops;
60 std::vector<double> feats(p.size(), 1) ;
61 x = out.rasterize(p, "", feats, NAN, false, false, false, false, false, ops);
62 if (gtype == "polygons") {
63 std::string etype = "inner";
64 x = x.edges(false, etype, 8, 0, ops);
65 }
66 p = x.as_points(false, true, opt);
67 pxy = p.coordinates();
68 }
69
70 if (pxy.size() == 0) {
71 out.setError("no locations to compute distance from");
72 return(out);
73 }
74
75 bool lonlat = is_lonlat(); // m == 0
76 //double torad = 0.0174532925199433;
77 //if (lonlat) {
78 // for (size_t i=0; i<pxy[0].size(); i++) {
79 // pxy[0][i] *= torad;
80 // pxy[1][i] *= torad;
81 // }
82 //}
83
84 unsigned nc = ncol();
85 if (!readStart()) {
86 out.setError(getError());
87 return(out);
88 }
89
90 if (!out.writeStart(opt)) {
91 readStop();
92 return out;
93 }
94 std::vector<double> v, cells;
95
96 for (size_t i = 0; i < out.bs.n; i++) {
97 double s = out.bs.row[i] * nc;
98 cells.resize(out.bs.nrows[i] * nc) ;
99 std::iota(cells.begin(), cells.end(), s);
100
101 if (gtype != "points") {
102 v = x.readBlock(out.bs, i);
103 for (size_t j=0; j<v.size(); j++) {
104 if (!std::isnan(v[j])) {
105 cells[j] = -1;
106 }
107 }
108 }
109 std::vector<std::vector<double>> xy = xyFromCell(cells);
110 std::vector<double> d(cells.size(), 0);
111 //if (lonlat) {
112 // for (size_t i=0; i<xy[0].size(); i++) {
113 // xy[0][i] *= torad;
114 // xy[1][i] *= torad;
115 // }
116 //}
117 shortDistPoints(d, xy[0], xy[1], pxy[0], pxy[1], lonlat, m);
118 if (!out.writeValues(d, out.bs.row[i], out.bs.nrows[i], 0, nc)) return out;
119 }
120 out.writeStop();
121 readStop();
122 return(out);
123 }
124
125
distance_vector(SpatVector p,SpatOptions & opt)126 SpatRaster SpatRaster::distance_vector(SpatVector p, SpatOptions &opt) {
127
128 SpatRaster out = geometry();
129 if (source[0].srs.wkt == "") {
130 out.setError("CRS not defined");
131 return(out);
132 }
133
134 double m = source[0].srs.to_meter();
135 m = std::isnan(m) ? 1 : m;
136
137
138 if (p.size() == 0) {
139 out.setError("no locations to compute distance from");
140 return(out);
141 }
142 p = p.aggregate(false);
143
144 // bool lonlat = is_lonlat(); // m == 0
145 unsigned nc = ncol();
146
147 if (!out.writeStart(opt)) {
148 readStop();
149 return out;
150 }
151 std::vector<double> cells;
152
153 for (size_t i = 0; i < out.bs.n; i++) {
154 double s = out.bs.row[i] * nc;
155 cells.resize(out.bs.nrows[i] * nc) ;
156 std::iota(cells.begin(), cells.end(), s);
157 std::vector<std::vector<double>> xy = xyFromCell(cells);
158 SpatVector pv(xy[0], xy[1], points, "");
159 pv.srs = p.srs;
160 std::vector<double> d = p.distance(pv, false);
161 if (p.hasError()) {
162 out.setError(p.getError());
163 out.writeStop();
164 return(out);
165 }
166 if (!out.writeValues(d, out.bs.row[i], out.bs.nrows[i], 0, nc)) return out;
167 }
168 out.writeStop();
169 return(out);
170 }
171
172
distance(SpatOptions & opt)173 SpatRaster SpatRaster::distance(SpatOptions &opt) {
174 SpatRaster out = geometry(1);
175 if (!hasValues()) {
176 out.setError("SpatRaster has no values");
177 return out;
178 }
179
180 SpatOptions ops(opt);
181 bool warn = false;
182 std::string msg;
183 if (nlyr() > 1) {
184 warn = true;
185 msg = "distance computations are only done for the first input layer";
186 std::vector<unsigned> lyr = {0};
187 *this = subset(lyr, ops);
188 }
189
190 out = edges(false, "inner", 8, 0, ops);
191 SpatVector p = out.as_points(false, true, opt);
192 out = out.distance_vector_rasterize(p, false, opt);
193 if (warn) {
194 out.addWarning(msg);
195 }
196 return out;
197 }
198
199
200
201
distance(bool sequential)202 std::vector<double> SpatVector::distance(bool sequential) {
203 std::vector<double> d;
204 if (srs.is_empty()) {
205 setError("crs not defined");
206 return(d);
207 }
208 double m = srs.to_meter();
209 m = std::isnan(m) ? 1 : m;
210 bool lonlat = is_lonlat(); // m == 0
211
212 // if ((!lonlat) || (gtype != "points")) {
213 std::string gtype = type();
214 if (gtype != "points") {
215 d = geos_distance(sequential);
216 if ((!lonlat) && (m != 1)) {
217 for (double &i : d) i *= m;
218 }
219 return d;
220 } else {
221 if (sequential) {
222 std::vector<std::vector<double>> p = coordinates();
223 size_t n = p[0].size();
224 d.reserve(n);
225 d.push_back(0);
226 n -= 1;
227 if (lonlat) {
228 for (size_t i=0; i<n; i++) {
229 d.push_back(
230 distance_lonlat(p[0][i], p[1][i], p[0][i+1], p[1][i+1])
231 );
232 }
233 } else {
234 for (size_t i=0; i<n; i++) {
235 d.push_back(
236 distance_plane(p[0][i], p[1][i], p[0][i+1], p[1][i+1]) * m
237 );
238 }
239 }
240
241 } else {
242 size_t s = size();
243 size_t n = ((s-1) * s)/2;
244 d.reserve(n);
245 std::vector<std::vector<double>> p = coordinates();
246 if (lonlat) {
247 for (size_t i=0; i<(s-1); i++) {
248 for (size_t j=(i+1); j<s; j++) {
249 d.push_back(
250 distance_lonlat(p[0][i], p[1][i], p[0][j], p[1][j])
251 );
252 }
253 }
254 } else {
255 for (size_t i=0; i<(s-1); i++) {
256 for (size_t j=(i+1); j<s; j++) {
257 d.push_back(
258 distance_plane(p[0][i], p[1][i], p[0][j], p[1][j]) * m
259 );
260 }
261 }
262 }
263 }
264 }
265
266 return d;
267 }
268
269
distance(SpatVector x,bool pairwise)270 std::vector<double> SpatVector::distance(SpatVector x, bool pairwise) {
271
272 std::vector<double> d;
273
274 if (srs.is_empty() || x.srs.is_empty()) {
275 setError("SRS not defined");
276 return(d);
277 }
278 if (! srs.is_same(x.srs, false) ) {
279 setError("SRS do not match");
280 return(d);
281 }
282
283 size_t s = size();
284 size_t sx = x.size();
285 if (pairwise && (s != sx ) && (s > 1) && (sx > 1)) {
286 setError("Can only do pairwise distance if geometries match, or if one is a single geometry");
287 return(d);
288 }
289
290 double m = srs.to_meter();
291 m = std::isnan(m) ? 1 : m;
292 bool lonlat = is_lonlat();
293
294 std::string gtype = type();
295 std::string xtype = x.type();
296 if ((!lonlat) || (gtype != "points") || (xtype != "points")) {
297 d = geos_distance(x, pairwise);
298 if ((!lonlat) && (m != 1)) {
299 for (double &i : d) i *= m;
300 }
301 return d;
302 }
303 std::vector<std::vector<double>> p = coordinates();
304 std::vector<std::vector<double>> px = x.coordinates();
305
306 /* recycling, not a good idea.
307 if (pairwise) {
308 if (s < sx) {
309 recycle(p[0], sx);
310 recycle(p[1], sx);
311 s = sx;
312 } else if (s > sx) {
313 recycle(px[0], s);
314 recycle(px[1], s);
315 sx = s;
316 }
317 }
318 */
319
320
321 size_t n = pairwise ? std::max(s,sx) : s*sx;
322 d.resize(n);
323
324 if (pairwise) {
325 if (s == sx) {
326 if (lonlat) {
327 for (size_t i = 0; i < s; i++) {
328 d[i] = distance_lonlat(p[0][i], p[1][i], px[0][i], px[1][i]);
329 }
330 } else { // not reached
331 for (size_t i = 0; i < s; i++) {
332 d[i] = distance_plane(p[0][i], p[1][i], px[0][i], px[1][i]) * m;
333 }
334 }
335 } else if (s == 1) { // to avoid recycling.
336 if (lonlat) {
337 for (size_t i = 0; i < sx; i++) {
338 d[i] = distance_lonlat(p[0][0], p[1][0], px[0][i], px[1][i]);
339 }
340 } else { // not reached
341 for (size_t i = 0; i < sx; i++) {
342 d[i] = distance_plane(p[0][0], p[1][0], px[0][i], px[1][i]) * m;
343 }
344 }
345 } else { // if (sx == 1) {
346 if (lonlat) {
347 for (size_t i = 0; i < s; i++) {
348 d[i] = distance_lonlat(p[0][i], p[1][i], px[0][0], px[1][0]);
349 }
350 } else { // not reached
351 for (size_t i = 0; i < s; i++) {
352 d[i] = distance_plane(p[0][i], p[1][i], px[0][0], px[1][0]) * m;
353 }
354 }
355 }
356 } else {
357 if (lonlat) {
358 for (size_t i=0; i<s; i++) {
359 size_t k = i * sx;
360 for (size_t j=0; j<sx; j++) {
361 d[k+j] = distance_lonlat(p[0][i], p[1][i], px[0][j], px[1][j]);
362 }
363 }
364 } else { // not reached
365 for (size_t i=0; i<s; i++) {
366 size_t k = i * sx;
367 for (size_t j=0; j<sx; j++) {
368 d[k+j] = distance_plane(p[0][i], p[1][i], px[0][j], px[1][j]) * m;
369 }
370 }
371 }
372 }
373
374 return d;
375 }
376
377
378
379
broom_dist_planar(std::vector<double> & v,std::vector<double> & above,std::vector<double> res,size_t nr,size_t nc,double lindist)380 std::vector<double> broom_dist_planar(std::vector<double> &v, std::vector<double> &above, std::vector<double> res, size_t nr, size_t nc, double lindist) {
381
382 double dx = res[0] * lindist;
383 double dy = res[1] * lindist;
384 double dxy = sqrt(dx * dx + dy *dy);
385
386
387 std::vector<double> dist(v.size(), 0);
388
389 //top to bottom
390 //left to right
391
392 if ( std::isnan(v[0]) ) { //first cell, no cell left of it
393 dist[0] = above[0] + dy;
394 }
395 for (size_t i=1; i<nc; i++) { //first row, no row above it, use "above"
396 if (std::isnan(v[i])) {
397 dist[i] = std::min(std::min(above[i] + dy, above[i-1] + dxy), dist[i-1] + dx);
398 }
399 }
400
401 for (size_t r=1; r<nr; r++) { //other rows
402 size_t start=r*nc;
403
404 if (std::isnan(v[start])) {
405 dist[start] = dist[start-nc] + dy;
406 }
407
408 size_t end = start+nc;
409 for (size_t i=(start+1); i<end; i++) {
410 if (std::isnan(v[i])) {
411 dist[i] = std::min(std::min(dist[i-1] + dx, dist[i-nc] + dy), dist[i-nc-1] + dxy);
412 }
413 }
414 }
415
416 //right to left
417 if ( std::isnan(v[nc-1])) { //first cell
418 dist[nc-1] = std::min(dist[nc-1], above[nc-1] + dy);
419 }
420
421 for (int i=(nc-2); i > -1; i--) { // other cells on first row
422 if (std::isnan(v[i])) {
423 dist[i] = std::min(std::min(std::min(dist[i+1] + dx, above[i+1] + dxy), above[i] + dy), dist[i]);
424 }
425 }
426
427 for (size_t r=1; r<nr; r++) { // other rows
428 size_t start=(r+1)*nc-1;
429 if (std::isnan(v[start])) {
430 dist[start] = std::min(dist[start], dist[start-nc] + dy);
431 }
432 for (size_t i=start-1; i>=(r*nc); i--) {
433 if (std::isnan(v[i])) {
434 dist[i] = std::min(std::min(std::min(dist[i], dist[i+1] + dx), dist[i-nc] + dy), dist[i-nc+1] + dxy);
435 }
436 }
437 }
438
439 size_t off = (nr-1) * nc;
440 above = std::vector<double>(dist.begin()+off, dist.end());
441
442 return dist;
443 }
444
445
DxDxy(const double & lat,const int & row,double xres,double yres,const int & dir,double & dx,double & dxy)446 void DxDxy(const double &lat, const int &row, double xres, double yres, const int &dir, double &dx, double &dxy) {
447 double thislat = lat + row * yres * dir;
448 xres /= 2;
449 yres /= 2;
450 dx = distance_lonlat(-xres, thislat , xres, thislat);
451 dxy = distance_lonlat(-xres, thislat-yres, xres, thislat+yres);
452 //double dy = distance_lonlat(0, 0, -yres, yres, a, f);
453 }
454
455
broom_dist_geo(std::vector<double> & v,std::vector<double> & above,std::vector<double> res,size_t nr,size_t nc,double lat,double latdir)456 std::vector<double> broom_dist_geo(std::vector<double> &v, std::vector<double> &above, std::vector<double> res, size_t nr, size_t nc, double lat, double latdir) {
457
458 double dy = distance_lonlat(0, 0, 0, res[0]);
459 double dx, dxy;
460
461 std::vector<double> dist(v.size(), 0);
462
463 //top to bottom
464 //left to right
465 DxDxy(lat, 0, res[0], res[1], latdir, dx, dxy);
466 if ( std::isnan(v[0]) ) { //first cell, no cell left of it
467 dist[0] = above[0] + dy;
468 }
469 for (size_t i=1; i<nc; i++) { //first row, no row above it, use "above"
470 if (std::isnan(v[i])) {
471 dist[i] = std::min(std::min(above[i] + dy, above[i-1] + dxy), dist[i-1] + dx);
472 }
473 }
474
475
476 for (size_t r=1; r<nr; r++) { //other rows
477 DxDxy(lat, r, res[0], res[1], latdir, dx, dxy);
478 size_t start=r*nc;
479
480 if (std::isnan(v[start])) {
481 dist[start] = dist[start-nc] + dy;
482 }
483
484 size_t end = start+nc;
485 for (size_t i=(start+1); i<end; i++) {
486 if (std::isnan(v[i])) {
487 dist[i] = std::min(std::min(dist[i-1] + dx, dist[i-nc] + dy), dist[i-nc-1] + dxy);
488 }
489 }
490 }
491
492 //right to left
493 DxDxy(lat, 0, res[0], res[1], latdir, dx, dxy);
494 if ( std::isnan(v[nc-1])) { //first cell
495 dist[nc-1] = std::min(dist[nc-1], above[nc-1] + dy);
496 }
497
498 for (int i=(nc-2); i > -1; i--) { // other cells on first row
499 if (std::isnan(v[i])) {
500 dist[i] = std::min(std::min(std::min(dist[i+1] + dx, above[i+1] + dxy), above[i] + dy), dist[i]);
501 }
502 }
503
504 for (size_t r=1; r<nr; r++) { // other rows
505 DxDxy(lat, r, res[0], res[1], latdir, dx, dxy);
506
507 size_t start=(r+1)*nc-1;
508 if (std::isnan(v[start])) {
509 dist[start] = std::min(dist[start], dist[start-nc] + dy);
510 }
511 for (size_t i=start-1; i>=(r*nc); i--) {
512 if (std::isnan(v[i])) {
513 dist[i] = std::min(std::min(std::min(dist[i], dist[i+1] + dx), dist[i-nc] + dy), dist[i-nc+1] + dxy);
514 }
515 }
516 }
517
518 size_t off = (nr-1) * nc;
519 above = std::vector<double>(dist.begin()+off, dist.end());
520
521 return dist;
522 }
523
524
525
gridDistance(SpatOptions & opt)526 SpatRaster SpatRaster::gridDistance(SpatOptions &opt) {
527
528 SpatRaster out = geometry(1);
529 SpatOptions ops(opt);
530 bool warn = false;
531 std::string msg;
532 if (nlyr() > 1) {
533 warn = true;
534 msg = "distance computations are only done for the first input layer";
535 std::vector<unsigned> lyr = {0};
536 SpatOptions opt(ops);
537 SpatRaster x = subset(lyr, opt);
538 return x.gridDistance(ops);
539 }
540
541 if (!hasValues()) {
542 out.setError("cannot compute distance for a raster with no values");
543 return out;
544 }
545
546 //bool isgeo = out.islonlat
547
548 double m = source[0].srs.to_meter();
549 m = std::isnan(m) ? 1 : m;
550
551 std::vector<double> res = resolution();
552
553 SpatRaster first = out.geometry();
554
555 std::string tempfile = "";
556 std::vector<double> above(ncol(), std::numeric_limits<double>::infinity());
557 std::vector<double> d, v, vv;
558
559 if (!readStart()) {
560 out.setError(getError());
561 return(out);
562 }
563 std::string filename = opt.get_filename();
564 opt.set_filenames({""});
565 if (!first.writeStart(opt)) { return first; }
566
567 // bool lonlat = is_lonlat();
568 size_t nc = ncol();
569 for (size_t i = 0; i < first.bs.n; i++) {
570 v = readBlock(first.bs, i);
571 // if (lonlat) {
572 // double lat = yFromRow(first.bs.row[i]);
573 // d = broom_dist_geo(v, above, res, first.bs.nrows[i], nc, lat, -1);
574 // } else {
575 d = broom_dist_planar(v, above, res, first.bs.nrows[i], nc, m);
576 // }
577 if (!first.writeValues(d, first.bs.row[i], first.bs.nrows[i], 0, nc)) return first;
578 }
579 first.writeStop();
580
581 if (!first.readStart()) {
582 out.setError(first.getError());
583 return(out);
584 }
585
586 opt.set_filenames({filename});
587 above = std::vector<double>(ncol(), std::numeric_limits<double>::infinity());
588
589 if (!out.writeStart(opt)) {
590 readStop();
591 return out;
592 }
593 for (int i = out.bs.n; i>0; i--) {
594 v = readBlock(out.bs, i-1);
595 std::reverse(v.begin(), v.end());
596 // if (lonlat) {
597 // double lat = yFromRow(out.bs.row[i-1] + out.bs.nrows[i-1] - 1);
598 // d = broom_dist_geo(v, above, res, out.bs.nrows[i-1], nc, lat, 1);
599 // } else {
600 d = broom_dist_planar(v, above, res, out.bs.nrows[i-1], nc, m);
601 // }
602 vv = first.readBlock(out.bs, i-1);
603 std::transform (d.rbegin(), d.rend(), vv.begin(), vv.begin(), [](double a, double b) {return std::min(a,b);});
604 if (!out.writeValues(vv, out.bs.row[i-1], out.bs.nrows[i-1], 0, nc)) return out;
605 }
606 out.writeStop();
607 readStop();
608 first.readStop();
609
610 if (warn) out.addWarning(msg);
611 return(out);
612
613 }
614
615
616 /*
617 std::vector<double> do_edge(std::vector<double> &d, size_t nrow, size_t ncol, bool before, bool after, bool classes, bool inner, unsigned dirs) {
618
619 bool falseval = 0;
620
621 size_t n = nrow * ncol;
622 std::vector<double> val(n, NAN);
623
624 // main
625 int r[8] = { -1,0,0,1 , -1,-1,1,1};
626 int c[8] = { 0,-1,1,0 , -1,1,-1,1};
627 // first col
628 int fr[5] = {-1,0,1,-1,1};
629 int fc[5] = { 0,1,0, 1,1};
630 // last col
631 int lr[5] = { -1, 0,1, -1, 1};
632 int lc[5] = { 0,-1,0, -1,-1};
633
634
635 // first row
636 int br[5] = { 0,0,1 , 1,1};
637 int bc[5] = { -1,1,0 ,-1,1};
638 // first col
639 int bfr[3] = { 0,1 ,1};
640 int bfc[3] = { 1,0 ,1};
641 // last col
642 int blr[3] = { 0,1 , 1};
643 int blc[3] = { -1,0 ,-1};
644
645
646 // last row
647 int ar[5] = { -1,0,0, -1,-1};
648 int ac[5] = { 0,-1,1, -1, 1};
649 // first col
650 int afr[3] = { -1,0,-1};
651 int afc[3] = { 0 ,1, 1};
652 // last col
653 int alr[3] = { -1,0, -1};
654 int alc[3] = { 0,-1, -1};
655
656
657 size_t rowoff = 0;
658 size_t nrows = nrow;
659 if (before) {
660 rowoff = 1;
661 }
662 if (after) {
663 nrows++;
664 }
665 size_t hrdirs = dirs == 4 ? 3 : 5;
666 size_t hcdirs = dirs == 4 ? 2 : 3;
667
668 if (classes) { // by class
669
670 for (size_t i = 1; i < (nrows); i++) {
671 for (size_t j = 1; j < (ncol-1); j++) {
672 size_t cell = i * ncol+j ;
673 double test = d[cell + r[0] * ncol + c[0]];
674 val[cell] = std::isnan(test) ? NAN : falseval;
675 for (size_t k=1; k<dirs; k++) {
676 double v = d[cell+r[k]*ncol +c[k]];
677 if (std::isnan(test)) {
678 if (!std::isnan(v)) {
679 val[cell] = 1;
680 break;
681 }
682 } else if (test != v) {
683 val[cell] = 1;
684 break;
685 }
686 }
687 }
688 }
689 } else { // not by class
690 if (inner) { ////// inner ////
691 if (!before) { // no row above
692 for (size_t j = 1; j < (ncol-1); j++) {
693 // cell = j
694 if (!std::isnan(d[j])) {
695 val[j] = 0;
696 for (size_t k=0; k < hrdirs; k++) {
697 if ( std::isnan(d[j + br[k] * ncol + bc[k] ])) {
698 val[j] = 1;
699 break;
700 }
701 }
702 }
703 } // first column of first row
704 // cell = j = 0
705 if (!std::isnan(d[0])) {
706 val[0] = 0;
707 for (size_t k=0; k < hcdirs; k++) {
708 if ( std::isnan(d[bfr[k] * ncol + bfc[k] ])) {
709 val[0] = 1;
710 break;
711 }
712 }
713 } // last column of first row
714 size_t cell = ncol-1;
715 if (!std::isnan(d[cell])) {
716 val[cell] = 0;
717 for (size_t k=0; k < hcdirs; k++) {
718 if ( std::isnan(d[cell + blr[k] * ncol + blc[k] ])) {
719 val[cell] = 1;
720 break;
721 }
722 }
723 }
724 }
725 if (!after) { // no row below
726 size_t i = nrows-1;
727 for (size_t j = 1; j < (ncol-1); j++) {
728 size_t cell = i * ncol + j;
729 size_t outcell = (i-rowoff) * ncol + j;
730 if (!std::isnan(d[cell])) {
731 val[outcell] = 0;
732 for (size_t k=0; k < hrdirs; k++) {
733 if ( std::isnan(d[cell+ ar[k] * ncol + ac[k] ])) {
734 val[outcell] = 1;
735 break;
736 }
737 }
738 }
739 } // first cell for last row
740 size_t cell = (nrows-1) * ncol;
741 size_t outcell = (nrows-1-rowoff) * ncol;
742 if (!std::isnasn(d[cell])) {
743 val[outcell] = 0;
744 for (size_t k=0; k < hcdirs; k++) {
745 if ( std::isnan(d[cell + afr[k] * ncol + afc[k] ])) {
746 val[outcell] = 1;
747 break;
748 }
749 }
750 } // last cell for last row
751 cell += ncol-1;
752 outcell += ncol-1;
753 if (!std::isnan(d[cell])) {
754 val[outcell] = 0;
755 for (size_t k=0; k < hcdirs; k++) {
756 if ( std::isnan(d[cell+ alr[k] * ncol + alc[k] ])) {
757 val[outcell] = 1;
758 break;
759 }
760 }
761 }
762 } // other rows
763
764
765 for (size_t i = 1; i < nrows; i++) {
766 for (size_t j = 1; j < (ncol-1); j++) {
767 size_t cell = i * ncol + j;
768 if (!std::isnan(d[cell])) {
769 size_t outcell = (i-rowoff) * ncol + j;
770 val[outcell] = 0;
771 for (size_t k=0; k < dirs; k++) {
772 if ( std::isnan(d[cell+ r[k] * ncol + c[k] ])) {
773 val[outcell] = 1;
774 break;
775 }
776 }
777 }
778 }
779
780 // first column
781 size_t cell = i * ncol;
782 size_t outcell = (i-rowoff) * ncol;
783 if (!std::isnan(d[cell])) {
784 val[outcell] = 0;
785 for (size_t k=0; k < hrdirs; k++) {
786 if ( std::isnan(d[cell + fr[k] * ncol + fc[k] ])) {
787 val[outcell] = 1;
788 break;
789 }
790 }
791 }
792 // last column
793 cell += ncol - 1;
794 outcell += ncol - 1;
795 if (!std::isnan(d[cell])) {
796 val[outcell] = 0;
797 for (size_t k=0; k < hrdirs; k++) {
798 if ( std::isnan( d[cell + lr[k] * ncol + lc[k] ])) {
799 val[outcell] = 1;
800 break;
801 }
802 }
803 }
804 }
805
806 } else { ////// outer ////
807
808
809 if (!before) {
810 for (size_t j = 1; j < (ncol-1); j++) {
811 size_t cell = j;
812 if (std::isnan(d[cell])) {
813 val[cell] = NAN;
814 for (size_t k=0; k < hcdirs; k++) {
815 if ( !std::isnan(d[j + br[k] * ncol + bc[k] ])) {
816 val[cell] = 1;
817 break;
818 }
819 }
820 } else {
821 val[cell] = 0;
822 }
823 }
824 }
825 if (!after) {
826 size_t i = (nrow - 1) * ncol;
827 for (size_t j = 1; j < (ncol-1); j++) {
828 size_t cell = (i-rowoff) * ncol + j;
829 if (std::isnan(d[cell])) {
830 val[cell] = NAN;
831 for (size_t k=0; k < hcdirs; k++) {
832 if (!std::isnan(d[cell+ ar[k] * ncol + ac[k] ])) {
833 val[cell] = 1;
834 break;
835 }
836 }
837 } else {
838 val[cell] = 0;
839 }
840 }
841 }
842
843 for (size_t i = 1; i < nrows; i++) {
844 for (size_t j = 1; j < (ncol-1); j++) {
845 size_t cell = (i-rowoff) * ncol + j;
846 if (std::isnan(d[cell])) {
847 val[cell] = NAN;
848 for (size_t k=0; k<dirs; k++) {
849 if (!std::isnan(d[cell + r[k] * ncol + c[k]])) {
850 val[cell] = 1;
851 break;
852 }
853 }
854 } else {
855 val[cell] = 0;
856 }
857 }
858 }
859 }
860
861 }
862 // val.erase(val.begin(), val.begin()+ncol);
863 // val.erase(val.end()-ncol, val.end());
864 return(val);
865 }
866
867 */
868
869 /*
870 std::vector<double> get_border(std::vector<double> xd, size_t nrows, size_t ncols, bool classes, std::string edgetype, unsigned dirs) {
871
872 size_t n = nrows * ncols;
873
874 std::vector<double> xval(n, 0);
875
876 int r[8] = {-1,0,0,1, -1,-1,1,1};
877 int c[8] = {0,-1,1,0, -1,1,-1,1};
878 int falseval = 0;
879
880 if (!classes) {
881 if (edgetype == "inner") {
882 for (size_t i = 1; i < (nrows-1); i++) {
883 for (size_t j = 1; j < (ncols-1); j++) {
884 size_t cell = i*ncols+j;
885 if (std::isnan(xd[cell])) {
886 xval[cell] = NAN;
887 } else {
888 xval[cell] = falseval;
889 for (size_t k=0; k< dirs; k++) {
890 if (std::isnan (xd[cell + r[k] * ncols + c[k]])) {
891 xval[cell] = 1;
892 break;
893 }
894 }
895 }
896 }
897 }
898
899 } else { // if (edgetype == "outer"
900 for (size_t i = 1; i < (nrows-1); i++) {
901 for (size_t j = 1; j < (ncols-1); j++) {
902 size_t cell = i*ncols+j;
903 xval[cell] = falseval;
904 if (std::isnan(xd[cell])) {
905 xval[cell] = NAN;
906 } else {
907 for (size_t k=0; k < dirs; k++) {
908 if (std::isnan(xd[cell+ r[k] * ncols + c[k] ])) {
909 xval[cell] = 1;
910 break;
911 }
912 }
913 }
914 }
915 }
916 }
917 } else { // by class
918 for (size_t i = 1; i < (nrows-1); i++) {
919 for (size_t j = 1; j < (ncols-1); j++) {
920 size_t cell = i*ncols+j;
921 double test = xd[ cell + r[0]*ncols + c[0] ];
922 if (std::isnan(test)) {
923 xval[cell] = NAN;
924 } else {
925 xval[cell] = falseval;
926 for (size_t k=1; k < dirs; k++) {
927 if (test != xd[ cell+r[k]*ncols +c[k] ]) {
928 xval[cell] = 1;
929 break;
930 }
931 }
932 }
933 }
934 }
935
936 }
937 return(xval);
938 }
939 */
940
941
942 /*
943 SpatRaster SpatRaster::edges(bool classes, std::string type, unsigned directions, SpatOptions &opt) {
944
945 SpatRaster out = geometry();
946 if (nlyr() > 1) {
947 out.setError("boundary detection can only be done for one layer at a time --- to be improved");
948 return(out);
949 }
950 if (!hasValues()) {
951 out.setError("SpatRaster has no values");
952 return out;
953 }
954
955
956 if ((directions != 4) && (directions != 8)) {
957 out.setError("directions should be 4 or 8");
958 return(out);
959 }
960 if ((type != "inner") && (type != "outer")) {
961 out.setError("directions should be 'inner' or 'outer'");
962 return(out);
963 }
964 bool inner = type == "inner";
965
966 size_t nc = ncol();
967
968 if (!readStart()) {
969 out.setError(getError());
970 return(out);
971 }
972
973 if (!out.writeStart(opt)) {
974 readStop();
975 return out;
976 }
977
978 for (size_t i = 0; i < out.bs.n; i++) {
979 std::vector<double> v;
980 bool before = false;
981 bool after = false;
982 if (i == 0) {
983 if (out.bs.n == 1) {
984 v = readValues(out.bs.row[i], out.bs.nrows[i], 0, nc);
985 } else {
986 v = readValues(out.bs.row[i], out.bs.nrows[i]+1, 0, nc);
987 after = true;
988 }
989 } else {
990 before = true;
991 if (i == out.bs.n) {
992 v = readValues(out.bs.row[i]-1, out.bs.nrows[i]+1, 0, nc);
993 } else {
994 v = readValues(out.bs.row[i]-1, out.bs.nrows[i]+2, 0, nc);
995 after = true;
996 }
997 }
998 std::vector<double> vv = do_edge(v, out.bs.nrows[i], nc, before, after, classes, inner, directions);
999 if (!out.writeValues(vv, out.bs.row[i], out.bs.nrows[i], 0, nc)) return out;
1000 }
1001 out.writeStop();
1002 readStop();
1003
1004 return(out);
1005 }
1006 */
1007
do_edge(const std::vector<double> & d,const size_t nrow,const size_t ncol,const bool classes,const bool inner,const unsigned dirs,double falseval)1008 std::vector<double> do_edge(const std::vector<double> &d, const size_t nrow, const size_t ncol, const bool classes, const bool inner, const unsigned dirs, double falseval) {
1009
1010 size_t n = nrow * ncol;
1011 std::vector<double> val(n, falseval);
1012
1013 int r[8] = { -1,0,0,1 , -1,-1,1,1};
1014 int c[8] = { 0,-1,1,0 , -1,1,-1,1};
1015
1016 if (!classes) {
1017 if (inner) { // inner
1018 for (size_t i = 1; i < (nrow-1); i++) {
1019 for (size_t j = 1; j < (ncol-1); j++) {
1020 size_t cell = i*ncol+j;
1021 val[cell] = NAN;
1022 if ( !std::isnan(d[cell])) {
1023 val[cell] = falseval;
1024 for (size_t k=0; k< dirs; k++) {
1025 if ( std::isnan(d[cell + r[k] * ncol + c[k]])) {
1026 val[cell] = 1;
1027 break;
1028 }
1029 }
1030 }
1031 }
1032 }
1033
1034 } else { //outer
1035 for (size_t i = 1; i < (nrow-1); i++) {
1036 for (size_t j = 1; j < (ncol-1); j++) {
1037 size_t cell = i*ncol+j;
1038 val[cell] = falseval;
1039 if (std::isnan(d[cell])) {
1040 val[cell] = NAN;
1041 for (size_t k=0; k < dirs; k++) {
1042 if ( !std::isnan(d[cell+ r[k] * ncol + c[k] ])) {
1043 val[cell] = 1;
1044 break;
1045 }
1046 }
1047 }
1048 }
1049 }
1050 }
1051 } else { // by class
1052 for (size_t i = 1; i < (nrow-1); i++) {
1053 for (size_t j = 1; j < (ncol-1); j++) {
1054 size_t cell = i*ncol+j;
1055 double test = d[cell+r[0]*ncol+c[0]];
1056 val[cell] = std::isnan(test) ? NAN : falseval;
1057 for (size_t k=1; k<dirs; k++) {
1058 double v = d[cell+r[k]*ncol +c[k]];
1059 if (std::isnan(test)) {
1060 if (!std::isnan(v)) {
1061 val[cell] = 1;
1062 break;
1063 }
1064 } else if (test != v) {
1065 val[cell] = 1;
1066 break;
1067 }
1068 }
1069 }
1070 }
1071
1072 }
1073 return(val);
1074 }
1075
1076
1077
addrowcol(std::vector<double> & v,size_t nr,size_t nc,bool rowbefore,bool rowafter,bool cols)1078 void addrowcol(std::vector<double> &v, size_t nr, size_t nc, bool rowbefore, bool rowafter, bool cols) {
1079
1080 if (rowbefore) {
1081 v.insert(v.begin(), v.begin(), v.begin()+nc);
1082 nr++;
1083 }
1084 if (rowafter) {
1085 v.insert(v.end(), v.end()-nc, v.end());
1086 nr++;
1087 }
1088 if (cols) {
1089 for (size_t i=0; i<nr; i++) {
1090 size_t j = i*(nc+2);
1091 v.insert(v.begin()+j+nc, v[j+nc-1]);
1092 v.insert(v.begin()+j, v[j]);
1093 }
1094 }
1095 }
1096
1097
striprowcol(std::vector<double> & v,size_t nr,size_t nc,bool rows,bool cols)1098 void striprowcol(std::vector<double> &v, size_t nr, size_t nc, bool rows, bool cols) {
1099 if (rows) {
1100 v.erase(v.begin(), v.begin()+nc);
1101 v.erase(v.end()-nc, v.end());
1102 nr -= 2;
1103 }
1104 if (cols) {
1105 nc -= 2;
1106 for (size_t i=0; i<nr; i++) {
1107 size_t j = i*nc;
1108 v.erase(v.begin()+j);
1109 v.erase(v.begin()+j+nc);
1110 }
1111 }
1112 }
1113
1114
edges(bool classes,std::string type,unsigned directions,double falseval,SpatOptions & opt)1115 SpatRaster SpatRaster::edges(bool classes, std::string type, unsigned directions, double falseval, SpatOptions &opt) {
1116
1117 SpatRaster out = geometry();
1118 if (nlyr() > 1) {
1119 out.addWarning("boundary detection is only done for the first layer");
1120 std::vector<unsigned> lyr = {0};
1121 SpatOptions ops(opt);
1122 *this = subset(lyr, ops);
1123 }
1124 if (!hasValues()) {
1125 out.setError("SpatRaster has no values");
1126 return out;
1127 }
1128
1129
1130 if ((directions != 4) && (directions != 8)) {
1131 out.setError("directions should be 4 or 8");
1132 return(out);
1133 }
1134 if ((type != "inner") && (type != "outer")) {
1135 out.setError("directions should be 'inner' or 'outer'");
1136 return(out);
1137 }
1138 bool inner = type == "inner";
1139
1140 size_t nc = ncol();
1141 size_t nr = nrow();
1142
1143
1144 if (!readStart()) {
1145 out.setError(getError());
1146 return(out);
1147 }
1148
1149 if (!out.writeStart(opt)) {
1150 readStop();
1151 return out;
1152 }
1153
1154 for (size_t i = 0; i < out.bs.n; i++) {
1155 std::vector<double> v;
1156 //bool before = false;
1157 //bool after = false;
1158 if (i == 0) {
1159 if (out.bs.n == 1) {
1160 v = readValues(out.bs.row[i], out.bs.nrows[i], 0, nc);
1161 addrowcol(v, nr, nc, true, true, true);
1162 } else {
1163 v = readValues(out.bs.row[i], out.bs.nrows[i]+1, 0, nc);
1164 addrowcol(v, nr, nc, true, false, true);
1165 //after = true;
1166 }
1167 } else {
1168 //before = true;
1169 if (i == out.bs.n) {
1170 v = readValues(out.bs.row[i]-1, out.bs.nrows[i]+1, 0, nc);
1171 addrowcol(v, nr, nc, false, true, true);
1172 } else {
1173 v = readValues(out.bs.row[i]-1, out.bs.nrows[i]+2, 0, nc);
1174 addrowcol(v, nr, nc, false, false, true);
1175 //after = true;
1176 }
1177 }
1178 //before, after,
1179 std::vector<double> vv = do_edge(v, out.bs.nrows[i]+2, nc+2, classes, inner, directions, falseval);
1180 striprowcol(vv, out.bs.nrows[i]+2, nc+2, true, true);
1181 if (!out.writeValues(vv, out.bs.row[i], out.bs.nrows[i], 0, nc)) return out;
1182 }
1183 out.writeStop();
1184 readStop();
1185
1186 return(out);
1187 }
1188
1189
1190
buffer(double d,SpatOptions & opt)1191 SpatRaster SpatRaster::buffer(double d, SpatOptions &opt) {
1192 SpatRaster out = geometry(1);
1193 if (d <= 0) {
1194 out.setError("buffer size <= 0; nothing to compute");
1195 return out;
1196 }
1197
1198 SpatOptions ops(opt);
1199 bool warn = false;
1200 std::string msg;
1201 if (nlyr() > 1) {
1202 warn = true;
1203 msg = "distance computations are only done for the first input layer";
1204 std::vector<unsigned> lyr = {0};
1205 *this = subset(lyr, ops);
1206 }
1207
1208 std::string etype = "inner";
1209 SpatRaster e = edges(false, etype, 8, 0, ops);
1210 SpatVector p = e.as_points(false, true, opt);
1211 out = out.distance_vector_rasterize(p, false, ops);
1212 out = out.arith(d, "<=", false, opt);
1213 if (warn) addWarning(msg);
1214 return out;
1215 }
1216
1217
fix_lonlat_overflow()1218 void SpatVector::fix_lonlat_overflow() {
1219
1220 if (! ((extent.xmin < -180) || (extent.xmax > 180))) { return; }
1221 SpatExtent world(-180, 180, -90, 90);
1222
1223 std::string vt = type();
1224 if (vt == "points") {
1225 for (size_t i=0; i<geoms.size(); i++) {
1226 SpatGeom g = geoms[i];
1227 for (size_t j=0; j<g.parts.size(); j++) {
1228 for (size_t k=0; k<g.parts[j].x.size(); k++) {
1229 if (geoms[i].parts[j].x[k] < -180) { geoms[i].parts[j].x[k] += 360; }
1230 if (geoms[i].parts[j].x[k] > 180) { geoms[i].parts[j].x[k] -= 360; }
1231 }
1232 }
1233 }
1234 } else {
1235 SpatExtent east(-360, -180, -180, 180);
1236 SpatExtent west(180, 360, -180, 180);
1237
1238 for (size_t i=0; i<geoms.size(); i++) {
1239 if (geoms[i].extent.xmin < -180) {
1240 SpatVector v(geoms[i]);
1241 if (geoms[i].extent.xmax <= -180) {
1242 v = v.shift(360, 0);
1243 } else {
1244 SpatVector add = v.crop(east);
1245 add = add.shift(360, 0);
1246 v = v.crop(world);
1247 v.geoms[i].addPart(add.geoms[0].parts[0]);
1248 }
1249 replaceGeom(v.geoms[0], i);
1250 }
1251 if (geoms[i].extent.xmax > 180) {
1252 SpatVector v(geoms[i]);
1253 if (geoms[i].extent.xmin >= 180) {
1254 v = v.shift(-360, 0);
1255 } else {
1256 SpatVector add = v.crop(west);
1257 add = add.shift(-360, 0);
1258 v = v.crop(world);
1259 v.geoms[i].addPart(add.geoms[0].parts[0]);
1260 }
1261 replaceGeom(v.geoms[0], i);
1262 }
1263 }
1264 }
1265
1266 if ((extent.ymax > 90) || (extent.ymin < -90)) {
1267 SpatVector out = crop(world);
1268 geoms = out.geoms;
1269 extent = out.extent;
1270 df = out.df;
1271 srs = out.srs;
1272 }
1273 return;
1274 }
1275
1276
sort_unique_2d(std::vector<double> & x,std::vector<double> & y)1277 void sort_unique_2d(std::vector<double> &x, std::vector<double> &y) {
1278 std::vector<std::vector<double>> v(x.size());
1279 for (size_t i=0; i<v.size(); i++) {
1280 v[i] = {x[i], y[i]};
1281 }
1282 std::sort(v.begin(), v.end(), []
1283 (const std::vector<double> &a, const std::vector<double> &b)
1284 { return a[0] < b[0];});
1285
1286 v.erase(std::unique(v.begin(), v.end()), v.end());
1287 x.resize(v.size());
1288 y.resize(v.size());
1289 for (size_t i=0; i<x.size(); i++) {
1290 x[i] = v[i][0];
1291 y[i] = v[i][1];
1292 }
1293 }
1294
1295
split_dateline(SpatVector & v)1296 void split_dateline(SpatVector &v) {
1297 SpatExtent e1 = {-1, 180, -91, 91};
1298 SpatExtent e2 = {180, 361, -91, 91};
1299 SpatVector ve(e1, "");
1300 SpatVector ve2(e2, "");
1301 ve = ve.append(ve2, true);
1302 v = v.intersect(ve);
1303 ve = v.subset_rows(1);
1304 ve = ve.shift(-360, 0);
1305 v.geoms[1] = ve.geoms[0];
1306 v = v.aggregate(false);
1307 }
1308
1309
1310
1311
fix_date_line(SpatGeom & g,std::vector<double> & x,const std::vector<double> & y)1312 bool fix_date_line(SpatGeom &g, std::vector<double> &x, const std::vector<double> &y) {
1313
1314 SpatPart p(x, y);
1315 double minx = vmin(x, false);
1316 double maxx = vmax(x, false);
1317 // need a better check but this should work for all normal cases
1318 if ((minx < -170) && (maxx > 170)) {
1319 for (size_t i=0; i<x.size(); i++) {
1320 if (x[i] < 0) {
1321 x[i] += 360;
1322 }
1323 }
1324 double minx2 = vmin(x, false);
1325 double maxx2 = vmax(x, false);
1326 if ((maxx - minx) < (maxx2 - minx2)) {
1327 g.reSetPart(p);
1328 return false;
1329 }
1330 p.x = x;
1331 g.reSetPart(p);
1332 SpatVector v(g);
1333 split_dateline(v);
1334 g = v.geoms[0];
1335 return true;
1336 }
1337 g.reSetPart(p);
1338 return false;
1339 }
1340
1341
point_buffer(std::vector<double> d,unsigned quadsegs,bool no_multipolygons)1342 SpatVector SpatVector::point_buffer(std::vector<double> d, unsigned quadsegs, bool no_multipolygons) {
1343
1344 SpatVector out;
1345 std::string vt = type();
1346 if (vt != "points") {
1347 out.setError("geometry must be points");
1348 return out;
1349 }
1350
1351 size_t npts = size();
1352
1353 size_t n = quadsegs * 4;
1354 double step = 360.0 / n;
1355 SpatGeom g(polygons);
1356 g.addPart(SpatPart(0, 0));
1357
1358 std::vector<std::vector<double>> xy = coordinates();
1359
1360 if (is_lonlat()) {
1361 std::vector<double> brng(n);
1362 for (size_t i=0; i<n; i++) {
1363 brng[i] = i * step;
1364 }
1365 double a = 6378137.0;
1366 double f = 1/298.257223563;
1367 struct geod_geodesic gd;
1368 geod_init(&gd, a, f);
1369 double lat, lon, azi, s12, azi2;
1370
1371 // not checking for empty points
1372
1373 for (size_t i=0; i<npts; i++) {
1374 if (std::isnan(xy[0][i] || std::isnan(xy[1][i]) || (xy[1][i]) > 90) || (xy[1][i] < -90)) {
1375 out.addGeom(SpatGeom(polygons));
1376 } else {
1377 std::vector<double> ptx;
1378 std::vector<double> pty;
1379 ptx.reserve(n);
1380 pty.reserve(n);
1381 geod_inverse(&gd, xy[1][i], xy[0][i], 90, xy[0][i], &s12, &azi, &azi2);
1382 bool npole = s12 < d[i];
1383 geod_inverse(&gd, xy[1][i], xy[0][i], -90, xy[0][i], &s12, &azi, &azi2);
1384 bool spole = s12 < d[i];
1385 if (npole && spole) {
1386 ptx = std::vector<double> {-180, 0, 180, 180, 180, 0, -180, -180};
1387 pty = std::vector<double> { 90, 90, 90, 0, -90, -90, -90, 0};
1388 npole = false;
1389 spole = false;
1390 } else {
1391 for (size_t j=0; j < n; j++) {
1392 geod_direct(&gd, xy[1][i], xy[0][i], brng[j], d[i], &lat, &lon, &azi);
1393 ptx.push_back(lon);
1394 pty.push_back(lat);
1395 }
1396 }
1397 if (npole) {
1398 sort_unique_2d(ptx, pty);
1399 if (ptx[ptx.size()-1] < 180) {
1400 ptx.push_back(180);
1401 pty.push_back(pty[pty.size()-1]);
1402 }
1403 ptx.push_back(180);
1404 pty.push_back(90);
1405 ptx.push_back(-180);
1406 pty.push_back(90);
1407 if (ptx[0] > -180) {
1408 ptx.push_back(-180);
1409 pty.push_back(pty[0]);
1410 }
1411 ptx.push_back(ptx[0]);
1412 pty.push_back(pty[0]);
1413 g.reSetPart(SpatPart(ptx, pty));
1414 out.addGeom(g);
1415 } else if (spole) {
1416 sort_unique_2d(ptx, pty);
1417 if (ptx[ptx.size()-1] < 180) {
1418 ptx.push_back(180);
1419 pty.push_back(pty[pty.size()-1]);
1420 }
1421 ptx.push_back(180);
1422 pty.push_back(-90);
1423 ptx.push_back(-180);
1424 pty.push_back(-90);
1425 if (ptx[0] > -180) {
1426 ptx.push_back(-180);
1427 pty.push_back(pty[0]);
1428 }
1429 ptx.push_back(ptx[0]);
1430 pty.push_back(pty[0]);
1431 g.reSetPart(SpatPart(ptx, pty));
1432 out.addGeom(g);
1433 } else {
1434 ptx.push_back(ptx[0]);
1435 pty.push_back(pty[0]);
1436 bool split = fix_date_line(g, ptx, pty);
1437 if (split & no_multipolygons) {
1438 for (size_t j=0; j<g.parts.size(); j++) {
1439 SpatGeom gg(g.parts[j]);
1440 out.addGeom(gg);
1441 }
1442 } else {
1443 out.addGeom(g);
1444 }
1445 }
1446 }
1447 }
1448 } else {
1449 std::vector<double> cosb(n);
1450 std::vector<double> sinb(n);
1451 std::vector<double> px(n+1);
1452 std::vector<double> py(n+1);
1453 for (size_t i=0; i<n; i++) {
1454 double brng = i * step;
1455 brng = toRad(brng);
1456 cosb[i] = d[i] * cos(brng);
1457 sinb[i] = d[i] * sin(brng);
1458 }
1459 for (size_t i=0; i<npts; i++) {
1460 if (std::isnan(xy[0][i]) || std::isnan(xy[1][i])) {
1461 out.addGeom(SpatGeom(polygons));
1462 } else {
1463 for (size_t j=0; j<n; j++) {
1464 px[j] = xy[0][i] + cosb[j];
1465 py[j] = xy[1][i] + sinb[j];
1466 }
1467 px[n] = px[0];
1468 py[n] = py[0];
1469 g.setPart(SpatPart(px, py), 0);
1470 out.addGeom(g);
1471 }
1472 }
1473 }
1474 out.srs = srs;
1475 out.df = df;
1476 return(out);
1477 }
1478
1479
1480
1481
1482
area_polygon_lonlat(geod_geodesic & g,const std::vector<double> & lon,const std::vector<double> & lat)1483 double area_polygon_lonlat(geod_geodesic &g, const std::vector<double> &lon, const std::vector<double> &lat) {
1484 struct geod_polygon p;
1485 geod_polygon_init(&p, 0);
1486 size_t n = lat.size();
1487 for (size_t i=0; i < n; i++) {
1488 //double lat = lat[i] > 90 ? 90 : lat[i] < -90 ? -90 : lat[i];
1489 // for #397
1490 double flat = lat[i] < -90 ? -90 : lat[i];
1491 geod_polygon_addpoint(&g, &p, flat, lon[i]);
1492 }
1493 double area, P;
1494 geod_polygon_compute(&g, &p, 0, 1, &area, &P);
1495 return(area < 0 ? -area : area);
1496 }
1497
1498
1499
area_polygon_plane(std::vector<double> x,std::vector<double> y)1500 double area_polygon_plane(std::vector<double> x, std::vector<double> y) {
1501 // based on http://paulbourke.net/geometry/polygonmesh/source1.c
1502 size_t n = x.size();
1503 double area = x[n-1] * y[0];
1504 area -= y[n-1] * x[0];
1505 for (size_t i=0; i < (n-1); i++) {
1506 area += x[i] * y[i+1];
1507 area -= x[i+1] * y[i];
1508 }
1509 area /= 2;
1510 return(area < 0 ? -area : area);
1511 }
1512
1513
area_lonlat(geod_geodesic & g,const SpatGeom & geom)1514 double area_lonlat(geod_geodesic &g, const SpatGeom &geom) {
1515 double area = 0;
1516 if (geom.gtype != polygons) return area;
1517 for (size_t i=0; i<geom.parts.size(); i++) {
1518 area += area_polygon_lonlat(g, geom.parts[i].x, geom.parts[i].y);
1519 for (size_t j=0; j < geom.parts[i].holes.size(); j++) {
1520 area -= area_polygon_lonlat(g, geom.parts[i].holes[j].x, geom.parts[i].holes[j].y);
1521 }
1522 }
1523 return area;
1524 }
1525
1526
area_plane(const SpatGeom & geom)1527 double area_plane(const SpatGeom &geom) {
1528 double area = 0;
1529 if (geom.gtype != polygons) return area;
1530 for (size_t i=0; i < geom.parts.size(); i++) {
1531 area += area_polygon_plane(geom.parts[i].x, geom.parts[i].y);
1532 for (size_t j=0; j < geom.parts[i].holes.size(); j++) {
1533 area -= area_polygon_plane(geom.parts[i].holes[j].x, geom.parts[i].holes[j].y);
1534 }
1535 }
1536 return area;
1537 }
1538
1539
area(std::string unit,bool transform,std::vector<double> mask)1540 std::vector<double> SpatVector::area(std::string unit, bool transform, std::vector<double> mask) {
1541
1542 size_t s = size();
1543 size_t m = mask.size();
1544 bool domask = false;
1545 if (m > 0) {
1546 if (s != mask.size()) {
1547 addWarning("mask size is not correct");
1548 } else {
1549 domask = true;
1550 }
1551 }
1552
1553 std::vector<double> ar;
1554 ar.reserve(s);
1555
1556 std::vector<std::string> ss {"m", "km", "ha"};
1557 if (std::find(ss.begin(), ss.end(), unit) == ss.end()) {
1558 setError("invalid unit");
1559 return {NAN};
1560 }
1561 double adj = unit == "m" ? 1 : unit == "km" ? 1000000 : 10000;
1562
1563
1564 if (srs.wkt == "") {
1565 addWarning("unknown CRS. Results can be wrong");
1566 if (domask) {
1567 for (size_t i=0; i<s; i++) {
1568 if (std::isnan(mask[i])) {
1569 ar.push_back(NAN);
1570 } else {
1571 ar.push_back(area_plane(geoms[i]));
1572 }
1573 }
1574 } else {
1575 for (size_t i=0; i<s; i++) {
1576 ar.push_back(area_plane(geoms[i]));
1577 }
1578 }
1579 } else {
1580 if (!srs.is_lonlat()) {
1581 if (transform) {
1582 SpatVector v = project("EPSG:4326");
1583 if (v.hasError()) {
1584 setError(v.getError());
1585 return {NAN};
1586 }
1587 return v.area(unit, false, mask);
1588
1589 } else {
1590 double m = srs.to_meter();
1591 adj *= std::isnan(m) ? 1 : m * m;
1592 if (domask) {
1593 for (size_t i=0; i<s; i++) {
1594 if (std::isnan(mask[i])) {
1595 ar.push_back(NAN);
1596 } else {
1597 ar.push_back(area_plane(geoms[i]));
1598 }
1599 }
1600 } else {
1601 for (size_t i=0; i<s; i++) {
1602 ar.push_back(area_plane(geoms[i]));
1603 }
1604 }
1605 }
1606
1607 } else {
1608
1609 struct geod_geodesic g;
1610 double a = 6378137;
1611 double f = 1 / 298.257223563;
1612 geod_init(&g, a, f);
1613 if (domask) {
1614 for (size_t i=0; i<s; i++) {
1615 if (std::isnan(mask[i])) {
1616 ar.push_back(NAN);
1617 } else {
1618 ar.push_back(area_lonlat(g, geoms[i]));
1619 }
1620 }
1621 } else {
1622 for (size_t i=0; i<s; i++) {
1623 ar.push_back(area_lonlat(g, geoms[i]));
1624 }
1625 }
1626 }
1627 }
1628
1629 if (adj != 1) {
1630 for (double& i : ar) i /= adj;
1631 }
1632 return ar;
1633 }
1634
1635
1636
length_line_lonlat(geod_geodesic & g,const std::vector<double> & lon,const std::vector<double> & lat)1637 double length_line_lonlat(geod_geodesic &g, const std::vector<double> &lon, const std::vector<double> &lat) {
1638 size_t n = lat.size();
1639 double length = 0;
1640 for (size_t i=1; i < n; i++) {
1641 length += distance_lonlat(lon[i-1], lat[i-1], lon[i], lat[i]);
1642 }
1643 return (length);
1644 }
1645
1646
length_line_plane(std::vector<double> x,std::vector<double> y)1647 double length_line_plane(std::vector<double> x, std::vector<double> y) {
1648 size_t n = x.size();
1649 double length = 0;
1650 for (size_t i=1; i<n; i++) {
1651 length += sqrt(pow(x[i-1] - x[i], 2) + pow(y[i-1] - y[i], 2));
1652 }
1653 return (length);
1654 }
1655
1656
length_lonlat(geod_geodesic & g,const SpatGeom & geom)1657 double length_lonlat(geod_geodesic &g, const SpatGeom &geom) {
1658 double length = 0;
1659 if (geom.gtype == points) return length;
1660 for (size_t i=0; i<geom.parts.size(); i++) {
1661 length += length_line_lonlat(g, geom.parts[i].x, geom.parts[i].y);
1662 for (size_t j=0; j<geom.parts[i].holes.size(); j++) {
1663 length += length_line_lonlat(g, geom.parts[i].holes[j].x, geom.parts[i].holes[j].y);
1664 }
1665 }
1666 return length;
1667 }
1668
1669
length_plane(const SpatGeom & geom)1670 double length_plane(const SpatGeom &geom) {
1671 double length = 0;
1672 if (geom.gtype == points) return length;
1673 for (size_t i=0; i < geom.parts.size(); i++) {
1674 length += length_line_plane(geom.parts[i].x, geom.parts[i].y);
1675 for (size_t j=0; j < geom.parts[i].holes.size(); j++) {
1676 length += length_line_plane(geom.parts[i].holes[j].x, geom.parts[i].holes[j].y);
1677 }
1678 }
1679 return length;
1680 }
1681
1682
length()1683 std::vector<double> SpatVector::length() {
1684
1685 size_t s = size();
1686 std::vector<double> r;
1687 r.reserve(s);
1688
1689 double m = srs.to_meter();
1690 m = std::isnan(m) ? 1 : m;
1691
1692 if (m == 0) {
1693 struct geod_geodesic g;
1694 double a = 6378137;
1695 double f = 1 / 298.257223563;
1696 geod_init(&g, a, f);
1697 for (size_t i=0; i<s; i++) {
1698 r.push_back(length_lonlat(g, geoms[i]));
1699 }
1700 } else {
1701 for (size_t i=0; i<s; i++) {
1702 r.push_back(length_plane(geoms[i]) * m);
1703 }
1704 }
1705 return r;
1706 }
1707
rst_area(bool mask,std::string unit,bool transform,SpatOptions & opt)1708 SpatRaster SpatRaster::rst_area(bool mask, std::string unit, bool transform, SpatOptions &opt) {
1709
1710 SpatRaster out = geometry(1);
1711 if (out.source[0].srs.wkt == "") {
1712 out.setError("empty CRS");
1713 return out;
1714 }
1715
1716 std::vector<std::string> f {"m", "km", "ha"};
1717 if (std::find(f.begin(), f.end(), unit) == f.end()) {
1718 out.setError("invalid unit");
1719 return out;
1720 }
1721
1722 if (opt.names.size() == 0) {
1723 opt.names = {"area"};
1724 }
1725 bool lonlat = is_lonlat();
1726
1727 SpatOptions mopt(opt);
1728 if (mask) {
1729 if (!hasValues()) {
1730 mask = false;
1731 } else {
1732 mopt.filenames = opt.filenames;
1733 opt = SpatOptions(opt);
1734 }
1735 }
1736
1737
1738
1739 if (lonlat) {
1740 bool disagg = false;
1741 SpatOptions xopt(opt);
1742 SpatExtent extent = getExtent();
1743 if ((out.ncol() == 1) && ((extent.xmax - extent.xmin) > 180)) {
1744 disagg = true;
1745 std::vector<unsigned> fact = {1,2};
1746 out = out.disaggregate(fact, xopt);
1747 }
1748
1749 if (!out.writeStart(opt)) { return out; }
1750
1751 SpatExtent e = {extent.xmin, extent.xmin+out.xres(), extent.ymin, extent.ymax};
1752 SpatRaster onecol = out.crop(e, "near", xopt);
1753 SpatVector p = onecol.as_polygons(false, false, false, false, xopt);
1754 if (p.hasError()) {
1755 out.setError(p.getError());
1756 return out;
1757 }
1758 std::vector<double> a = p.area(unit, true, {});
1759 size_t nc = out.ncol();
1760 for (size_t i = 0; i < out.bs.n; i++) {
1761 std::vector<double> v;
1762 for (size_t j=0; j<out.bs.nrows[i]; j++) {
1763 size_t r = out.bs.row[i] + j;
1764 v.insert(v.end(), nc, a[r]);
1765 }
1766 if (!out.writeValues(v, out.bs.row[i], out.bs.nrows[i], 0, ncol())) return out;
1767 }
1768 if (disagg) {
1769 out.writeStop();
1770 SpatOptions dopt(opt);
1771 SpatRaster tmp = out.to_memory_copy(dopt);
1772 std::vector<unsigned> fact = {1,2};
1773 opt.overwrite=true;
1774 out = tmp.aggregate(fact, "sum", true, opt);
1775 } else {
1776 out.writeStop();
1777 }
1778
1779 } else {
1780 if (!out.writeStart(opt)) { return out; }
1781 if (transform) {
1782 SpatExtent extent = getExtent();
1783 double dy = yres() / 2;
1784 SpatOptions popt(opt);
1785 for (size_t i = 0; i < out.bs.n; i++) {
1786 double ymax = yFromRow(out.bs.row[i]) + dy;
1787 double ymin = yFromRow(out.bs.row[i] + out.bs.nrows[i]-1) - dy;
1788 SpatExtent e = {extent.xmin, extent.xmax, ymin, ymax};
1789 SpatRaster onechunk = out.crop(e, "near", popt);
1790 SpatVector p = onechunk.as_polygons(false, false, false, false, popt);
1791 //std::vector<double> cells(onechunk.ncell());
1792 //std::iota (cells.begin(), cells.end(), 0);
1793 //onechunk.setValues(cells);
1794 //SpatVector p = onechunk.as_polygons(false, true, false, false, popt);
1795 std::vector<double> v;
1796 v = p.area(unit, true, {});
1797 if (!out.writeValues(v, out.bs.row[i], out.bs.nrows[i], 0, ncol())) return out;
1798 }
1799 } else {
1800 double u = unit == "m" ? 1 : unit == "km" ? 1000000 : 10000;
1801 double m = out.source[0].srs.to_meter();
1802 double a = std::isnan(m) ? 1 : m;
1803 a *= xres() * yres() / u;
1804 for (size_t i = 0; i < out.bs.n; i++) {
1805 std::vector<double> v(out.bs.nrows[i]*ncol(), a);
1806 if (!out.writeValues(v, out.bs.row[i], out.bs.nrows[i], 0, ncol())) return out;
1807 }
1808 }
1809 out.writeStop();
1810 }
1811
1812 if (mask) {
1813 out = out.mask(*this, false, NAN, NAN, mopt);
1814 }
1815 return(out);
1816 }
1817
1818
sum_area(std::string unit,bool transform,SpatOptions & opt)1819 std::vector<double> SpatRaster::sum_area(std::string unit, bool transform, SpatOptions &opt) {
1820
1821 if (source[0].srs.wkt == "") {
1822 setError("empty CRS");
1823 return {NAN};
1824 }
1825
1826 std::vector<std::string> f {"m", "km", "ha"};
1827 if (std::find(f.begin(), f.end(), unit) == f.end()) {
1828 setError("invalid unit");
1829 return {NAN};
1830 }
1831
1832 std::vector<double> out(nlyr(), 0);
1833
1834 if (transform) { //avoid very large polygon objects
1835 opt.set_memfrac(std::max(0.1, opt.get_memfrac()/2));
1836 }
1837 BlockSize bs = getBlockSize(opt);
1838 if (!readStart()) {
1839 std::vector<double> err(nlyr(), -1);
1840 return(err);
1841 }
1842
1843 if (is_lonlat()) {
1844 SpatRaster x = geometry(1);
1845 SpatExtent extent = x.getExtent();
1846 //SpatOptions opt;
1847 if ((x.ncol() == 1) && ((extent.xmax - extent.xmin) > 180)) {
1848 std::vector<unsigned> fact= {1,2};
1849 x = x.disaggregate(fact, opt);
1850 }
1851 size_t nc = x.ncol();
1852 SpatExtent e = {extent.xmin, extent.xmin+x.xres(), extent.ymin, extent.ymax};
1853 SpatRaster onecol = x.crop(e, "near", opt);
1854 SpatVector p = onecol.as_polygons(false, false, false, false, opt);
1855 std::vector<double> ar = p.area(unit, true, {});
1856 if (!hasValues()) {
1857 out.resize(1);
1858 for (size_t i=0; i<ar.size(); i++) {
1859 out[0] += ar[i] * nc;
1860 }
1861 } else {
1862 for (size_t i=0; i<bs.n; i++) {
1863 std::vector<double> v = readValues(bs.row[i], bs.nrows[i], 0, ncol());
1864 size_t blockoff = bs.nrows[i] * nc;
1865 for (size_t lyr=0; lyr<nlyr(); lyr++) {
1866 size_t lyroff = lyr * blockoff;
1867 for (size_t j=0; j<bs.nrows[i]; j++) {
1868 size_t row = bs.row[i] + j;
1869 size_t offset = lyroff + row * nc;
1870 size_t n = offset + nc;
1871 for (size_t k=offset; k<n; k++) {
1872 if (!std::isnan(v[k])) out[lyr] += ar[row];
1873 }
1874 }
1875 }
1876 }
1877 }
1878 } else if (transform) {
1879 SpatRaster x = geometry(1);
1880 SpatExtent extent = x.getExtent();
1881 double dy = x.yres() / 2;
1882 SpatOptions popt(opt);
1883 if (!hasValues()) {
1884 out.resize(1);
1885 for (size_t i=0; i<bs.n; i++) {
1886 double ymax = x.yFromRow(bs.row[i]) + dy;
1887 double ymin = x.yFromRow(bs.row[i] + bs.nrows[i]-1) - dy;
1888 SpatExtent e = {extent.xmin, extent.xmax, ymin, ymax};
1889 SpatRaster onechunk = x.crop(e, "near", popt);
1890 SpatVector p = onechunk.as_polygons(false, false, false, false, popt);
1891 p = p.project("EPSG:4326");
1892 std::vector<double> v = p.area(unit, true, {});
1893 out[0] += accumulate(v.begin(), v.end(), 0.0);
1894 }
1895 } else {
1896 for (size_t i=0; i<bs.n; i++) {
1897 double ymax = x.yFromRow(bs.row[i]) + dy;
1898 double ymin = x.yFromRow(bs.row[i] + bs.nrows[i]-1) - dy;
1899 SpatExtent e = {extent.xmin, extent.xmax, ymin, ymax};
1900 SpatRaster onechunk = x.crop(e, "near", popt);
1901 SpatVector p = onechunk.as_polygons(false, false, false, false, popt);
1902 p = p.project("EPSG:4326");
1903 std::vector<double> par = p.area(unit, true, {});
1904
1905 std::vector<double> v = readValues(bs.row[i], bs.nrows[i], 0, ncol());
1906 unsigned off = bs.nrows[i] * ncol() ;
1907 for (size_t lyr=0; lyr<nlyr(); lyr++) {
1908 unsigned offset = lyr * off;
1909 unsigned n = offset + off;
1910 for (size_t j=offset; j<n; j++) {
1911 if (!std::isnan(v[j])) {
1912 out[lyr] += par[j - offset];
1913 }
1914 }
1915 }
1916 }
1917
1918 }
1919 } else {
1920 double adj = unit == "m" ? 1 : unit == "km" ? 1000000 : 10000;
1921 double m = source[0].srs.to_meter();
1922 m = std::isnan(m) ? 1 : m;
1923 double ar = xres() * yres() * m * m / adj;
1924 if (!hasValues()) {
1925 out.resize(1);
1926 out[0] = ncell() * ar;
1927 } else {
1928 for (size_t i=0; i<bs.n; i++) {
1929 std::vector<double> v = readValues(bs.row[i], bs.nrows[i], 0, ncol());
1930 unsigned off = bs.nrows[i] * ncol() ;
1931 for (size_t lyr=0; lyr<nlyr(); lyr++) {
1932 unsigned offset = lyr * off;
1933 unsigned n = offset + off;
1934 for (size_t j=offset; j<n; j++) {
1935 if (!std::isnan(v[j])) out[lyr]++;
1936 }
1937 }
1938 }
1939 for (size_t lyr=0; lyr<nlyr(); lyr++) {
1940 out[lyr] = out[lyr] * ar;
1941 }
1942 }
1943 }
1944 readStop();
1945 return(out);
1946 }
1947
1948
1949
1950 //layer<value-area
area_by_value(SpatOptions & opt)1951 std::vector<std::vector<double>> SpatRaster::area_by_value(SpatOptions &opt) {
1952
1953 double m = source[0].srs.to_meter();
1954 m = std::isnan(m) ? 1 : m;
1955
1956 if (m != 0) {
1957 double ar = xres() * yres() * m * m;
1958 std::vector<std::vector<double>> f = freq(true, false, 0, opt);
1959 for (size_t i=0; i<f.size(); i++) {
1960 size_t fs = f[i].size();
1961 for (size_t j=fs/2; j<fs; j++) {
1962 f[i][j] *= ar;
1963 }
1964 }
1965 return f;
1966 } else {
1967 // to do
1968 // combine freq and area to get area by latitudes
1969
1970 std::vector<std::vector<double>> out(nlyr());
1971 return out;
1972 }
1973 }
1974
1975
1976
do_flowdir(std::vector<double> & val,std::vector<double> const & d,size_t nrow,size_t ncol,double dx,double dy,unsigned seed)1977 void do_flowdir(std::vector<double> &val, std::vector<double> const &d, size_t nrow, size_t ncol, double dx, double dy, unsigned seed) {
1978
1979 size_t n = nrow * ncol;
1980 size_t add = val.size();
1981 val.resize(add+n, NAN);
1982
1983 std::vector<double> r = {0, 0, 0, 0, 0, 0, 0, 0};
1984 std::vector<double> p = {1, 2, 4, 8, 16, 32, 64, 128}; // pow(2, j)
1985 double dxy = sqrt(dx * dx + dy * dy);
1986
1987 std::default_random_engine generator(seed);
1988 std::uniform_int_distribution<> U(0, 1);
1989
1990 for (size_t row=1; row< (nrow-1); row++) {
1991 for (size_t col=1; col< (ncol-1); col++) {
1992 size_t i = row * ncol + col;
1993 if (!std::isnan(d[i])) {
1994 r[0] = (d[i] - d[i+1]) / dx;
1995 r[1] = (d[i] - d[i+1+ncol]) / dxy;
1996 r[2] = (d[i] - d[i+ncol]) / dy;
1997 r[3] = (d[i] - d[i-1+ncol]) / dxy;
1998 r[4] = (d[i] - d[i-1]) / dx;
1999 r[5] = (d[i] - d[i-1-ncol]) / dxy;
2000 r[6] = (d[i] - d[i-ncol]) / dy;
2001 r[7] = (d[i] - d[i+1-ncol]) / dxy;
2002 // using the lowest neighbor, even if it is higher than the focal cell.
2003 double dmin = r[0];
2004 int k = 0;
2005 for (size_t j=1; j<8; j++) {
2006 if (r[j] > dmin) {
2007 dmin = r[j];
2008 k = j;
2009 } else if (r[j] == dmin) {
2010 if (U(generator)) {
2011 dmin = r[j];
2012 k = j;
2013 }
2014 }
2015 }
2016 val[i+add] = p[k];
2017 }
2018 }
2019 }
2020 }
2021
2022
do_TRI(std::vector<double> & val,std::vector<double> const & d,size_t nrow,size_t ncol)2023 void do_TRI(std::vector<double> &val, std::vector<double> const &d, size_t nrow, size_t ncol) {
2024 size_t n = nrow * ncol;
2025 size_t add = val.size();
2026 val.resize(add+n, NAN);
2027 for (size_t row=1; row< (nrow-1); row++) {
2028 for (size_t col=1; col< (ncol-1); col++) {
2029 size_t i = row * ncol + col;
2030 val[i+add] = (fabs(d[i-1-ncol]-d[i]) + fabs(d[i-1]-d[i]) + fabs(d[i-1+ncol]-d[i]) + fabs(d[i-ncol]-d[i]) + fabs(d[i+ncol]-d[i]) + fabs(d[i+1-ncol]-d[i]) + fabs(d[i+1]-d[i]) + fabs(d[i+1+ncol]-d[i])) / 8;
2031 }
2032 }
2033 }
2034
do_TPI(std::vector<double> & val,const std::vector<double> & d,const size_t nrow,const size_t ncol)2035 void do_TPI(std::vector<double> &val, const std::vector<double> &d, const size_t nrow, const size_t ncol) {
2036 size_t n = nrow * ncol;
2037 size_t add = val.size();
2038 val.resize(add+n, NAN);
2039 for (size_t row=1; row< (nrow-1); row++) {
2040 for (size_t col=1; col< (ncol-1); col++) {
2041 size_t i = row * ncol + col;
2042 val[i+add] = d[i] - (d[i-1-ncol] + d[i-1] + d[i-1+ncol] + d[i-ncol]
2043 + d[i+ncol] + d[i+1-ncol] + d[i+1] + d[i+1+ncol]) / 8;
2044 }
2045 }
2046 /*
2047 if (expand) {
2048 for (size_t i=1; i < (ncol-1); i++) {
2049 val[i+add] = d[i] - (d[i-1] + d[i-1+ncol] + d[i+ncol] + d[i+1] + d[i+1+ncol]) / 5;
2050 size_t j = i+(nrow-1) * ncol;
2051 val[j+add] = d[j] - (d[j-1-ncol] + d[j-1] + d[j-ncol] + d[j+1-ncol] + d[j+1]) / 5;
2052 }
2053 for (size_t row=1; row< (nrow-1); row++) {
2054 size_t i = row * ncol;
2055 val[i+add] = d[i] - (d[i-ncol] + d[i+ncol] + d[i+1-ncol] + d[i+1] + d[i+1+ncol]) / 5;
2056 i += ncol - 1;
2057 val[i+add] = d[i] - (d[i-ncol] + d[i] + d[i+ncol] + d[i-ncol]) / 5;
2058 }
2059 size_t i = 0;
2060 val[i+add] = d[i] - (d[i+ncol] + d[i+1] + d[i+1+ncol]) / 3;
2061 i = ncol-1;
2062 val[i+add] = d[i] - (d[i+ncol] + d[i-1] + d[i-1+ncol]) / 3;
2063 i = (nrow-1)*ncol;
2064 val[i+add] = d[i] - (d[i-ncol] + d[i+1] + d[i+1-ncol]) / 3;
2065 i = (nrow*ncol)-1;
2066 val[i+add] = d[i] - (d[i-ncol] + d[i-1] + d[i-1-ncol]) / 3;
2067 }
2068 */
2069 }
2070
2071
2072
do_roughness(std::vector<double> & val,const std::vector<double> & d,size_t nrow,size_t ncol)2073 void do_roughness(std::vector<double> &val, const std::vector<double> &d, size_t nrow, size_t ncol) {
2074 size_t n = nrow * ncol;
2075 size_t add = val.size();
2076 val.resize(add+n, NAN);
2077 int incol = ncol;
2078 int a[9] = { -1-incol, -1, -1+incol, -incol, 0, incol, 1-incol, 1, 1+incol };
2079 double min, max, v;
2080 for (size_t row=1; row< (nrow-1); row++) {
2081 for (size_t col=1; col< (ncol-1); col++) {
2082 size_t i = row * ncol + col;
2083 min = d[i + a[0]];
2084 max = d[i + a[0]];
2085 for (size_t j = 1; j < 9; j++) {
2086 v = d[i + a[j]];
2087 if (v > max) {
2088 max = v;
2089 } else if (v < min) {
2090 min = v;
2091 }
2092 }
2093 val[i+add] = max - min;
2094 }
2095 }
2096 }
2097
2098
2099 #ifndef M_PI
2100 #define M_PI (3.14159265358979323846)
2101 #endif
2102
2103
2104
2105
to_degrees(std::vector<double> & x,size_t start)2106 void to_degrees(std::vector<double>& x, size_t start) {
2107 double adj = 180 / M_PI;
2108 for (size_t i=start; i<x.size(); i++) {
2109 x[i] *= adj;
2110 }
2111 }
2112
2113
do_slope(std::vector<double> & val,const std::vector<double> & d,unsigned ngb,unsigned nrow,unsigned ncol,double dx,double dy,bool geo,std::vector<double> & gy,bool degrees)2114 void do_slope(std::vector<double> &val, const std::vector<double> &d, unsigned ngb, unsigned nrow, unsigned ncol, double dx, double dy, bool geo, std::vector<double> &gy, bool degrees) {
2115
2116 size_t n = nrow * ncol;
2117 size_t add = val.size();
2118 val.resize(add+n, NAN);
2119
2120 std::vector<double> ddx;
2121 if (geo) {
2122 ddx.resize(nrow);
2123 for (size_t i=0; i<nrow; i++) {
2124 ddx[i] = distHaversine(-dx, gy[i], dx, gy[i]) / 2 ;
2125 }
2126 }
2127
2128 if (ngb == 4) {
2129 if (geo) {
2130 double xwi[2] = {-1,1};
2131 double xw[2] = {0,0};
2132 double yw[2] = {-1,1};
2133
2134 for (size_t i=0; i<2; i++) {
2135 yw[i] = yw[i] / (2 * dy);
2136 }
2137
2138 for (size_t row=1; row< (nrow-1); row++) {
2139 for (size_t k=0; k<2; k++) {
2140 xw[k] = xwi[k] / (-2 * ddx[row]);
2141 }
2142 for (size_t col=1; col< (ncol-1); col++) {
2143 size_t i = row * ncol + col;
2144
2145 double zx = d[i-1] * xw[0] + d[i+1] * xw[1];
2146 double zy = d[i-ncol] * yw[0] + d[i+ncol] * yw[1];
2147 val[i+add] = atan(sqrt( pow(zy, 2) + pow(zx, 2) ));
2148 }
2149 }
2150 } else { // ngb == 8
2151
2152 double xw[2] = {-1,1};
2153 double yw[2] = {-1,1};
2154 for (size_t i=0; i<2; i++) {
2155 xw[i] /= -2 * dx;
2156 yw[i] /= 2 * dy;
2157 }
2158 for (size_t row=1; row< (nrow-1); row++) {
2159 for (size_t col=1; col< (ncol-1); col++) {
2160 size_t i = row * ncol + col;
2161 double zx = d[i-1] * xw[0] + d[i+1] * xw[1];
2162 double zy = d[i-ncol] * yw[0] + d[i+ncol] * yw[1];
2163 val[i+add] = atan( sqrt( pow(zy, 2) + pow(zx, 2) ));
2164 }
2165 }
2166 }
2167 } else {
2168
2169 if (geo) {
2170
2171 double xwi[6] = {-1,-2,-1,1,2,1};
2172 double xw[6] = {0,0,0,0,0,0};
2173 double yw[6] = {-1,1,-2,2,-1,1};
2174
2175 for (size_t i=0; i<6; i++) {
2176 yw[i] = yw[i] / (8 * dy);
2177 }
2178
2179 for (size_t row=1; row< (nrow-1); row++) {
2180 for (size_t k=0; k<6; k++) {
2181 xw[k] = xwi[k] / (8 * ddx[row]);
2182 }
2183
2184 for (size_t col=1; col< (ncol-1); col++) {
2185 size_t i = row * ncol + col;
2186 double zx = d[i-1-ncol] * xw[0] + d[i-1] * xw[1] + d[i-1+ncol] * xw[2]
2187 + d[i+1-ncol] * xw[3] + d[i+1] * xw[4] + d[i+1+ncol] * xw[5];
2188
2189 double zy = d[i-1-ncol] * yw[0] + d[i-1+ncol] * yw[1] + d[i-ncol] * yw[2]
2190 + d[i+ncol] * yw[3] + d[i+1-ncol] * yw[4] + d[i+1+ncol] * yw[5];
2191 val[i+add] = atan(sqrt( pow(zy, 2) + pow(zx, 2)));
2192 }
2193 }
2194 } else {
2195
2196 double xw[6] = {-1,-2,-1,1,2,1};
2197 double yw[6] = {-1,1,-2,2,-1,1};
2198 for (size_t i=0; i<6; i++) {
2199 xw[i] /= -8 * dx;
2200 yw[i] /= 8 * dy;
2201 }
2202 for (size_t row=1; row< (nrow-1); row++) {
2203 for (size_t col=1; col< (ncol-1); col++) {
2204 size_t i = row * ncol + col;
2205 double zx = d[i-1-ncol] * xw[0] + d[i-1] * xw[1] + d[i-1+ncol] * xw[2]
2206 + d[i+1-ncol] * xw[3] + d[i+1] * xw[4] + d[i+1+ncol] * xw[5];
2207 double zy = d[i-1-ncol] * yw[0] + d[i-1+ncol] * yw[1] + d[i-ncol] * yw[2]
2208 + d[i+ncol] * yw[3] + d[i+1-ncol] * yw[4] + d[i+1+ncol] * yw[5];
2209 val[i+add] = atan(sqrt( pow(zy, 2) + pow(zx, 2) ));
2210 }
2211 }
2212 }
2213 }
2214 if (degrees) {
2215 to_degrees(val, add);
2216 }
2217 }
2218
2219
dmod(double x,double n)2220 double dmod(double x, double n) {
2221 return(x - n * std::floor(x/n));
2222 }
2223
2224
2225
do_aspect(std::vector<double> & val,const std::vector<double> & d,unsigned ngb,unsigned nrow,unsigned ncol,double dx,double dy,bool geo,std::vector<double> & gy,bool degrees)2226 void do_aspect(std::vector<double> &val, const std::vector<double> &d, unsigned ngb, unsigned nrow, unsigned ncol, double dx, double dy, bool geo, std::vector<double> &gy, bool degrees) {
2227
2228 size_t n = nrow * ncol;
2229 std::vector<double> ddx;
2230 if (geo) {
2231 ddx.resize(nrow);
2232 for (size_t i=0; i<nrow; i++) {
2233 ddx[i] = distHaversine(-dx, gy[i], dx, gy[i]) / 2 ;
2234 }
2235 }
2236 double zy, zx;
2237 size_t add = val.size();
2238 val.resize(add+n, NAN);
2239
2240 //double const pi2 = M_PI / 2;
2241 double const twoPI = 2 * M_PI;
2242 double const halfPI = M_PI / 2;
2243
2244 if (ngb == 4) {
2245 if (geo) {
2246 double xwi[2] = {-1,1};
2247 double xw[2] = {0,0};
2248 double yw[2] = {-1,1};
2249
2250 for (size_t i=0; i<2; i++) {
2251 yw[i] = yw[i] / (2 * dy);
2252 }
2253
2254 for (size_t row=1; row< (nrow-1); row++) {
2255 for (size_t k=0; k<2; k++) {
2256 xw[k] = xwi[k] / (-2 * ddx[row]);
2257 }
2258 for (size_t col=1; col< (ncol-1); col++) {
2259 size_t i = row * ncol + col;
2260 zx = d[i-1] * xw[0] + d[i+1] * xw[1];
2261 zy = d[i-ncol] * yw[0] + d[i+ncol] * yw[1];
2262 zx = atan2(zy, zx);
2263 val[i+add] = dmod( halfPI - zx, twoPI);
2264 }
2265 }
2266 } else {
2267
2268 double xw[2] = {-1,1};
2269 double yw[2] = {-1,1};
2270 for (size_t i=0; i<2; i++) {
2271 xw[i] = xw[i] / (-2 * dx);
2272 yw[i] = yw[i] / (2 * dy);
2273 }
2274 for (size_t row=1; row< (nrow-1); row++) {
2275 for (size_t col=1; col< (ncol-1); col++) {
2276 size_t i = row * ncol + col;
2277 zx = d[i-1] * xw[0] + d[i+1] * xw[1];
2278 zy = d[i-ncol] * yw[0] + d[i+ncol] * yw[1];
2279 zx = atan2(zy, zx);
2280 val[i+add] = dmod( halfPI - zx, twoPI);
2281 }
2282 }
2283 }
2284 } else { // (ngb == 8) {
2285
2286 if (geo) {
2287
2288 double xwi[6] = {-1,-2,-1,1,2,1};
2289 double xw[6] = {0,0,0,0,0,0};
2290 double yw[6] = {-1,1,-2,2,-1,1};
2291
2292 for (size_t i=0; i<6; i++) {
2293 yw[i] /= (8 * dy);
2294 }
2295
2296 for (size_t row=1; row < (nrow-1); row++) {
2297 for (size_t k=0; k<6; k++) {
2298 xw[k] = xwi[k] / (-8 * ddx[row]);
2299 }
2300 for (size_t col=1; col< (ncol-1); col++) {
2301 size_t i = row * ncol + col;
2302
2303 zx = d[i-1-ncol] * xw[0] + d[i-1] * xw[1] + d[i-1+ncol] * xw[2]
2304 + d[i+1-ncol] * xw[3] + d[i+1] * xw[4] + d[i+1+ncol] * xw[5];
2305 zy = d[i-1-ncol] * yw[0] + d[i-1+ncol] * yw[1] + d[i-ncol] * yw[2]
2306 + d[i+ncol] * yw[3] + d[i+1-ncol] * yw[4] + d[i+1+ncol] * yw[5];
2307
2308 zx = atan2(zy, zx);
2309 val[i+add] = dmod( halfPI - zx, twoPI);
2310 }
2311 }
2312
2313 } else {
2314
2315 double xw[6] = {-1,-2,-1,1,2,1};
2316 double yw[6] = {-1,1,-2,2,-1,1};
2317 for (size_t i=0; i<6; i++) {
2318 xw[i] /= -8 * dx;
2319 yw[i] /= 8 * dy;
2320 }
2321 for (size_t row=1; row< (nrow-1); row++) {
2322 for (size_t col=1; col< (ncol-1); col++) {
2323 size_t i = row * ncol + col;
2324 zx = d[i-1-ncol] * xw[0] + d[i-1] * xw[1] + d[i-1+ncol] * xw[2]
2325 + d[i+1-ncol] * xw[3] + d[i+1] * xw[4] + d[i+1+ncol] * xw[5];
2326 zy = d[i-1-ncol] * yw[0] + d[i-1+ncol] * yw[1] + d[i-ncol] * yw[2]
2327 + d[i+ncol] * yw[3] + d[i+1-ncol] * yw[4] + d[i+1+ncol] * yw[5];
2328 zx = atan2(zy, zx);
2329 val[i+add] = dmod( halfPI - zx, twoPI);
2330 }
2331 }
2332 }
2333 }
2334
2335 if (degrees) {
2336 to_degrees(val, add);
2337 }
2338 }
2339
2340
terrain(std::vector<std::string> v,unsigned neighbors,bool degrees,unsigned seed,SpatOptions & opt)2341 SpatRaster SpatRaster::terrain(std::vector<std::string> v, unsigned neighbors, bool degrees, unsigned seed, SpatOptions &opt) {
2342
2343 //TPI, TRI, aspect, flowdir, slope, roughness
2344 //std::sort(v.begin(), v.end());
2345 //v.erase(std::unique(v.begin(), v.end()), v.end());
2346
2347 SpatRaster out = geometry(v.size());
2348 out.setNames(v);
2349
2350 if (nlyr() > 1) {
2351 out.setError("terrain needs a single layer object");
2352 return out;
2353 }
2354
2355 bool aspslope = false;
2356 std::vector<std::string> f {"TPI", "TRI", "aspect", "flowdir", "slope", "roughness"};
2357 for (size_t i=0; i<v.size(); i++) {
2358 if (std::find(f.begin(), f.end(), v[i]) == f.end()) {
2359 out.setError("unknown terrain variable: " + v[i]);
2360 return(out);
2361 }
2362 if (v[i] == "slope" || v[i] == "aspect") {
2363 aspslope=true;
2364 }
2365 }
2366
2367 if ((neighbors != 4) && (neighbors != 8)) {
2368 out.setError("neighbors should be 4 or 8");
2369 return out;
2370 }
2371 bool lonlat = is_lonlat();
2372 double yr = yres();
2373
2374 if (!readStart()) {
2375 out.setError(getError());
2376 return(out);
2377 }
2378 if (!out.writeStart(opt)) {
2379 readStop();
2380 return out;
2381 }
2382
2383 if (nrow() < 3 || ncol() < 3) {
2384 for (size_t i = 0; i < out.bs.n; i++) {
2385 std::vector<double> val(out.bs.nrows[i] * ncol(), NAN);
2386 if (!out.writeValues(val, out.bs.row[i], out.bs.nrows[i], 0, ncol())) return out;
2387 }
2388 return out;
2389 }
2390
2391
2392 std::vector<double> y;
2393 for (size_t i = 0; i < out.bs.n; i++) {
2394 std::vector<double> d = readValues(out.bs.row[i], out.bs.nrows[i], 0, ncol());
2395 if (lonlat && aspslope) {
2396 std::vector<int_64> rows(out.bs.nrows[i]);
2397 std::iota(rows.begin(), rows.end(), out.bs.row[i]);
2398 y = yFromRow(rows);
2399 yr = distHaversine(0, 0, 0, yr);
2400 }
2401 std::vector<double> val;
2402 for (size_t j =0; j<v.size(); j++) {
2403 if (v[j] == "slope") {
2404 do_slope(val, d, neighbors, out.bs.nrows[i], ncol(), xres(), yr, lonlat, y, degrees);
2405 } else if (v[j] == "aspect") {
2406 do_aspect(val, d, neighbors, out.bs.nrows[i], ncol(), xres(), yr, lonlat, y, degrees);
2407 } else if (v[j] == "flowdir") {
2408 double dx = xres();
2409 double dy = yres();
2410 if (lonlat) {
2411 double yhalf = yFromRow((size_t) nrow()/2);
2412 dx = distHaversine(0, yhalf, dx, yhalf);
2413 dy = distHaversine(0, 0, 0, dy);
2414 }
2415 do_flowdir(val, d, out.bs.nrows[i], ncol(), dx, dy, seed);
2416 } else if (v[j] == "roughness") {
2417 do_roughness(val, d, out.bs.nrows[i], ncol());
2418 } else if (v[j] == "TPI") {
2419 do_TPI(val, d, out.bs.nrows[i], ncol());
2420 } else if (v[j] == "TRI") {
2421 do_TRI(val, d, out.bs.nrows[i], ncol());
2422 } else {
2423 out.setError("?"); return out;
2424 }
2425 }
2426 if (!out.writeValues(val, out.bs.row[i], out.bs.nrows[i], 0, ncol())) return out;
2427 }
2428 out.writeStop();
2429 readStop();
2430 return out;
2431 }
2432
2433