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