-
-
Notifications
You must be signed in to change notification settings - Fork 320
/
simplification.c
399 lines (321 loc) · 10.3 KB
/
simplification.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
/****************************************************************
*
* MODULE: v.generalize
*
* AUTHOR(S): Daniel Bundala
*
* PURPOSE: Module for line simplification and smoothing
*
* COPYRIGHT: (C) 2002-2005 by the GRASS Development Team
*
* This program is free software under the
* GNU General Public License (>=v2).
* Read the file COPYING that comes with GRASS
* for details.
*
****************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <grass/gis.h>
#include <grass/vector.h>
#include <grass/glocale.h>
#include "point.h"
#include "pq.h"
#include "misc.h"
int douglas_peucker(struct line_pnts *Points, double thresh, int with_z)
{
int *stack = G_malloc(sizeof(int) * Points->n_points * 2);
if (!stack) {
G_fatal_error(_("Out of memory"));
return Points->n_points;
}
int *index = G_malloc(
sizeof(int) * Points->n_points); /* Indices of points in output line */
if (!index) {
G_fatal_error(_("Out of memory"));
G_free(stack);
return Points->n_points;
}
int top = 2; /* first free slot in the stack */
int icount = 1; /* number of indices stored */
int i;
thresh *= thresh;
index[0] = 0; /* first point is always in output line */
/* stack contains pairs of elements: (beginning, end) */
stack[0] = 0;
stack[1] = Points->n_points - 1;
while (top > 0) { /*there are still segments to consider */
/*Pop indices of the segment from the stack */
int last = stack[--top];
int first = stack[--top];
double x1 = Points->x[first];
double y1 = Points->y[first];
double z1 = Points->z[first];
double x2 = Points->x[last];
double y2 = Points->y[last];
double z2 = Points->z[last];
int maxindex = -1;
double maxdist = -1;
for (i = first + 1; i <= last - 1;
i++) { /* Find the furthermost point between first, last */
double px, py, pz, pdist;
int status;
double dist = dig_distance2_point_to_line(
Points->x[i], Points->y[i], Points->z[i], x1, y1, z1, x2, y2,
z2, with_z, &px, &py, &pz, &pdist, &status);
if (maxindex == -1 ||
dist > maxdist) { /* update the furthermost point so far seen */
maxindex = i;
maxdist = dist;
}
}
if (maxindex == -1 ||
maxdist <= thresh) { /* no points between or all point are inside
the threshold */
index[icount++] = last;
}
else {
/* break line into two parts, the order of pushing is crucial! It
* guarantees, that we are going to the left */
stack[top++] = maxindex;
stack[top++] = last;
stack[top++] = first;
stack[top++] = maxindex;
}
}
Points->n_points = icount;
/* finally, select only points marked in the algorithm */
for (i = 0; i < icount; i++) {
Points->x[i] = Points->x[index[i]];
Points->y[i] = Points->y[index[i]];
Points->z[i] = Points->z[index[i]];
}
G_free(stack);
G_free(index);
return (Points->n_points);
}
int lang(struct line_pnts *Points, double thresh, int look_ahead, int with_z)
{
int count = 1; /* place where the next point will be added. i.e after the
last point */
int from = 0;
int to = look_ahead;
thresh *= thresh;
while (from <
Points->n_points - 1) { /* repeat until we reach the last point */
int i;
int found =
0; /* whether we have found the point outside the threshold */
double x1 = Points->x[from];
double y1 = Points->y[from];
double z1 = Points->z[from];
if (Points->n_points - 1 <
to) { /* check that we are always in the line */
to = Points->n_points - 1;
}
double x2 = Points->x[to];
double y2 = Points->y[to];
double z2 = Points->z[to];
for (i = from + 1; i < to; i++) {
double px, py, pz, pdist;
int status;
if (dig_distance2_point_to_line(
Points->x[i], Points->y[i], Points->z[i], x1, y1, z1, x2,
y2, z2, with_z, &px, &py, &pz, &pdist, &status) > thresh) {
found = 1;
break;
}
}
if (found) {
to--;
}
else {
Points->x[count] = Points->x[to];
Points->y[count] = Points->y[to];
Points->z[count] = Points->z[to];
count++;
from = to;
to += look_ahead;
}
}
Points->n_points = count;
return Points->n_points;
}
/* Eliminates all vertices which are close(r than eps) to each other */
int vertex_reduction(struct line_pnts *Points, double eps, int with_z)
{
int start, i, count, n;
n = Points->n_points;
/* there's almost nothing to remove */
if (n <= 2)
return Points->n_points;
count = 0;
start = 0;
eps *= eps;
count = 1; /*we never remove the first point */
for (i = 0; i < n - 1; i++) {
double dx = Points->x[i] - Points->x[start];
double dy = Points->y[i] - Points->y[start];
double dz = Points->z[i] - Points->z[start];
double dst = dx * dx + dy * dy;
if (with_z) {
dst += dz * dz;
}
if (dst > eps) {
Points->x[count] = Points->x[i];
Points->y[count] = Points->y[i];
Points->z[count] = Points->z[i];
count++;
start = i;
}
}
/* last point is also always preserved */
Points->x[count] = Points->x[n - 1];
Points->y[count] = Points->y[n - 1];
Points->z[count] = Points->z[n - 1];
count++;
Points->n_points = count;
return Points->n_points;
}
/*Reumann-Witkam algorithm
* Returns number of points in the output line
*/
int reumann_witkam(struct line_pnts *Points, double thresh, int with_z)
{
int i, count;
POINT x0, x1, x2, sub, diff;
double subd, diffd, sp, dist;
int n;
n = Points->n_points;
if (n < 3)
return n;
thresh *= thresh;
count = 1;
point_assign(Points, 0, with_z, &x1, 0);
point_assign(Points, 1, with_z, &x2, 0);
point_subtract(x2, x1, &sub);
subd = point_dist2(sub);
for (i = 2; i < n; i++) {
point_assign(Points, i, with_z, &x0, 0);
point_subtract(x1, x0, &diff);
diffd = point_dist2(diff);
sp = point_dot(diff, sub);
dist = (diffd * subd - sp * sp) / subd;
/* if the point is out of the threshlod-sausage, store it and calculate
* all variables which do not change for each line-point calculation */
if (dist > thresh) {
point_assign(Points, i - 1, with_z, &x1, 0);
point_assign(Points, i, with_z, &x2, 0);
point_subtract(x2, x1, &sub);
subd = point_dist2(sub);
Points->x[count] = x0.x;
Points->y[count] = x0.y;
Points->z[count] = x0.z;
count++;
}
}
Points->x[count] = Points->x[n - 1];
Points->y[count] = Points->y[n - 1];
Points->z[count] = Points->z[n - 1];
Points->n_points = count + 1;
return Points->n_points;
}
/* douglas-peucker algorithm which simplifies a line to a line with
* at most reduction% of points.
* returns the number of points in the output line. It is approx
* reduction/100 * Points->n_points.
*/
int douglas_peucker_reduction(struct line_pnts *Points, double thresh,
double reduction, int with_z)
{
int i;
int n = Points->n_points;
/* the maximum number of points which may be
* included in the output */
int nexp = n * (reduction / (double)100.0);
/* line too short */
if (n < 3)
return n;
/* indicates which point were selected by the algorithm */
int *sel;
sel = G_calloc(sizeof(int), n);
if (sel == NULL) {
G_fatal_error(_("Out of memory"));
return n;
}
/* array used for storing the indices of line segments+furthest point */
int *index;
index = G_malloc(sizeof(int) * 3 * n);
if (index == NULL) {
G_fatal_error(_("Out of memory"));
G_free(sel);
return n;
}
int indices;
indices = 0;
/* preserve first and last point */
sel[0] = sel[n - 1] = 1;
nexp -= 2;
thresh *= thresh;
double d;
int mid = get_furthest(Points, 0, n - 1, with_z, &d);
int em;
/* priority queue of line segments,
* key is the distance of the furthest point */
binary_heap pq;
if (!binary_heap_init(n, &pq)) {
G_fatal_error(_("Out of memory"));
G_free(sel);
G_free(index);
return n;
}
if (d > thresh) {
index[0] = 0;
index[1] = n - 1;
index[2] = mid;
binary_heap_push(d, 0, &pq);
indices = 3;
}
/* while we can add new points and queue is non-empty */
while (nexp > 0) {
/* empty heap */
if (!binary_heap_extract_max(&pq, &em))
break;
int left = index[em];
int right = index[em + 1];
int furt = index[em + 2];
/*mark the furthest point */
sel[furt] = 1;
nexp--;
/* consider left and right segment */
mid = get_furthest(Points, left, furt, with_z, &d);
if (d > thresh) {
binary_heap_push(d, indices, &pq);
index[indices++] = left;
index[indices++] = furt;
index[indices++] = mid;
}
mid = get_furthest(Points, furt, right, with_z, &d);
if (d > thresh) {
binary_heap_push(d, indices, &pq);
index[indices++] = furt;
index[indices++] = right;
index[indices++] = mid;
}
}
/* copy selected points */
int selected = 0;
for (i = 0; i < n; i++) {
if (sel[i]) {
Points->x[selected] = Points->x[i];
Points->y[selected] = Points->y[i];
Points->z[selected] = Points->z[i];
selected++;
}
}
G_free(sel);
G_free(index);
binary_heap_free(&pq);
Points->n_points = selected;
return Points->n_points;
}