/src/MapServer/src/mapsmoothing.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: RFC94 Shape smoothing |
6 | | * Author: Alan Boudreault (aboudreault@mapgears.com) |
7 | | * Author: Daniel Morissette (dmorissette@mapgears.com) |
8 | | * |
9 | | ****************************************************************************** |
10 | | * Copyright (c) 1996-2005 Regents of the University of Minnesota. |
11 | | * |
12 | | * Permission is hereby granted, free of charge, to any person obtaining a |
13 | | * copy of this software and associated documentation files (the "Software"), |
14 | | * to deal in the Software without restriction, including without limitation |
15 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
16 | | * and/or sell copies of the Software, and to permit persons to whom the |
17 | | * Software is furnished to do so, subject to the following conditions: |
18 | | * |
19 | | * The above copyright notice and this permission notice shall be included in |
20 | | * all copies of this Software or works derived from this Software. |
21 | | * |
22 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
23 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
25 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
26 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
27 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
28 | | * DEALINGS IN THE SOFTWARE. |
29 | | *****************************************************************************/ |
30 | | |
31 | | #include "mapserver.h" |
32 | | |
33 | 0 | #define FP_EPSILON 1e-12 |
34 | 0 | #define FP_EQ(a, b) (fabs((a) - (b)) < FP_EPSILON) |
35 | | |
36 | | /* Internal Use, represent a line window (points) */ |
37 | | typedef struct { |
38 | | int pos; /* current point position in line */ |
39 | | int size; |
40 | | int index; /* index of the current point in points array */ |
41 | | lineObj *line; |
42 | | int lineIsRing; /* closed ring? */ |
43 | | pointObj **points; |
44 | | } lineWindow; |
45 | | |
46 | 0 | static void initLineWindow(lineWindow *lw, lineObj *line, int size) { |
47 | 0 | lw->pos = -1; |
48 | 0 | lw->lineIsRing = MS_FALSE; |
49 | 0 | lw->size = size; |
50 | 0 | lw->line = line; |
51 | 0 | lw->index = |
52 | 0 | floor(lw->size / 2); /* index of current position in points array */ |
53 | 0 | lw->points = (pointObj **)msSmallMalloc(sizeof(pointObj *) * size); |
54 | |
|
55 | 0 | if ((line->numpoints >= 2) && |
56 | 0 | ((FP_EQ(line->point[0].x, line->point[line->numpoints - 1].x)) && |
57 | 0 | (FP_EQ(line->point[0].y, line->point[line->numpoints - 1].y)))) { |
58 | 0 | lw->lineIsRing = 1; |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | 0 | static void freeLineWindow(lineWindow *lw) { free(lw->points); } |
63 | | |
64 | 0 | static int nextLineWindow(lineWindow *lw) { |
65 | 0 | int i; |
66 | |
|
67 | 0 | if (++lw->pos >= lw->line->numpoints) |
68 | 0 | return MS_DONE; |
69 | | |
70 | 0 | lw->points[lw->index] = &lw->line->point[lw->pos]; |
71 | |
|
72 | 0 | for (i = 0; i < lw->index; ++i) { |
73 | 0 | int r, l; |
74 | 0 | r = lw->pos - (i + 1); |
75 | 0 | l = lw->pos + (i + 1); |
76 | | |
77 | | /* adjust values */ |
78 | 0 | if ((r < 0) && lw->lineIsRing) |
79 | 0 | r = lw->line->numpoints - (i + 2); |
80 | 0 | if ((l >= lw->line->numpoints) && lw->lineIsRing) |
81 | 0 | l = 1 + (l - lw->line->numpoints); |
82 | | |
83 | | /* return if the window in not valid.. */ |
84 | 0 | if (r < 0 || l >= lw->line->numpoints) |
85 | 0 | return MS_FALSE; |
86 | | |
87 | 0 | lw->points[lw->index - (i + 1)] = &lw->line->point[r]; |
88 | 0 | lw->points[lw->index + (i + 1)] = &lw->line->point[l]; |
89 | 0 | } |
90 | | |
91 | 0 | return MS_TRUE; |
92 | 0 | } |
93 | | |
94 | | /* Calculates the distance ratio between the total distance of a path and the |
95 | | distance between the first and last point (to detect a loop). */ |
96 | 0 | static double computePathDistanceRatio(pointObj **points, int len) { |
97 | 0 | double sum; |
98 | 0 | int i; |
99 | |
|
100 | 0 | for (sum = 0, i = 1; i < len; ++i) { |
101 | 0 | sum += msDistancePointToPoint(points[i - 1], points[i]); |
102 | 0 | } |
103 | |
|
104 | 0 | return sum / msDistancePointToPoint(points[0], points[len - 1]); |
105 | 0 | } |
106 | | |
107 | | /* Pre-Processing of a shape. It modifies the shape by adding intermediate |
108 | | points where a loop is detected to improve the smoothing result. */ |
109 | 0 | static int processShapePathDistance(shapeObj *shape, int force) { |
110 | 0 | shapeObj initialShape, *newShape; |
111 | 0 | int i; |
112 | | |
113 | | /* initial shape to process */ |
114 | 0 | msInitShape(&initialShape); |
115 | 0 | msCopyShape(shape, &initialShape); |
116 | |
|
117 | 0 | newShape = shape; /* we modify the shape object directly */ |
118 | 0 | shape = &initialShape; |
119 | | |
120 | | /* Clean our shape object */ |
121 | 0 | for (i = 0; i < newShape->numlines; i++) |
122 | 0 | free(newShape->line[i].point); |
123 | 0 | newShape->numlines = 0; |
124 | 0 | if (newShape->line) |
125 | 0 | free(newShape->line); |
126 | |
|
127 | 0 | for (i = 0; i < shape->numlines; ++i) { |
128 | 0 | const int windowSize = 5; |
129 | 0 | int res; |
130 | 0 | lineWindow lw; |
131 | 0 | lineObj line = {0, NULL}; |
132 | |
|
133 | 0 | initLineWindow(&lw, &shape->line[i], windowSize); |
134 | 0 | msAddLine(newShape, &line); |
135 | |
|
136 | 0 | while ((res = nextLineWindow(&lw)) != MS_DONE) { |
137 | 0 | double ratio = 0; |
138 | 0 | pointObj point = {0}; // initialize |
139 | |
|
140 | 0 | if (lw.lineIsRing && lw.pos == lw.line->numpoints - 1) { |
141 | 0 | point = newShape->line[i].point[0]; |
142 | 0 | msAddPointToLine(&newShape->line[i], &point); |
143 | 0 | continue; |
144 | 0 | } |
145 | | |
146 | 0 | if (res == MS_FALSE) { /* invalid window */ |
147 | 0 | msAddPointToLine(&newShape->line[i], lw.points[lw.index]); |
148 | 0 | continue; |
149 | 0 | } |
150 | | |
151 | 0 | if (!force) |
152 | 0 | ratio = computePathDistanceRatio(lw.points, windowSize); |
153 | |
|
154 | 0 | if (force || (ratio > 1.3)) { |
155 | 0 | point.x = (lw.line->point[lw.pos].x + lw.points[lw.index - 1]->x) / 2; |
156 | 0 | point.y = (lw.line->point[lw.pos].y + lw.points[lw.index - 1]->y) / 2; |
157 | 0 | msAddPointToLine(&newShape->line[i], &point); |
158 | 0 | } |
159 | |
|
160 | 0 | point = lw.line->point[lw.pos]; |
161 | 0 | msAddPointToLine(&newShape->line[i], &point); |
162 | |
|
163 | 0 | if (force || (ratio > 1.3)) { |
164 | 0 | point.x = (lw.line->point[lw.pos].x + lw.points[lw.index + 1]->x) / 2; |
165 | 0 | point.y = (lw.line->point[lw.pos].y + lw.points[lw.index + 1]->y) / 2; |
166 | 0 | msAddPointToLine(&newShape->line[i], &point); |
167 | 0 | } |
168 | 0 | } |
169 | |
|
170 | 0 | freeLineWindow(&lw); |
171 | 0 | } |
172 | |
|
173 | 0 | msFreeShape(shape); |
174 | |
|
175 | 0 | return MS_SUCCESS; |
176 | 0 | } |
177 | | |
178 | | shapeObj *msSmoothShapeSIA(shapeObj *shape, int ss, int si, |
179 | 0 | char *preprocessing) { |
180 | 0 | int i, j; |
181 | 0 | pointObj *p; |
182 | 0 | double *coeff; |
183 | 0 | shapeObj *newShape; |
184 | |
|
185 | 0 | newShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj)); |
186 | 0 | msInitShape(newShape); |
187 | 0 | newShape->type = shape->type; // preserve the type |
188 | |
|
189 | 0 | if (ss < 3) |
190 | 0 | ss = 3; |
191 | |
|
192 | 0 | if (si < 1) |
193 | 0 | si = 1; |
194 | | |
195 | | /* Apply preprocessing */ |
196 | 0 | if (preprocessing) { |
197 | 0 | if (strcasecmp(preprocessing, "all") == 0) |
198 | 0 | processShapePathDistance(shape, MS_TRUE); |
199 | 0 | else if (strcasecmp(preprocessing, "angle") == 0) |
200 | 0 | processShapePathDistance(shape, MS_FALSE); |
201 | 0 | } |
202 | |
|
203 | 0 | p = (pointObj *)msSmallMalloc(ss * sizeof(pointObj)); |
204 | 0 | coeff = (double *)msSmallMalloc(ss * sizeof(double)); |
205 | |
|
206 | 0 | for (i = 0; i < si; ++i) { |
207 | 0 | shapeObj initialShape; |
208 | |
|
209 | 0 | if (si > 1 && i > 0) { |
210 | 0 | msInitShape(&initialShape); |
211 | 0 | msCopyShape(shape, &initialShape); |
212 | | |
213 | | /* Clean our shape object */ |
214 | 0 | for (j = 0; j < newShape->numlines; ++j) |
215 | 0 | free(newShape->line[j].point); |
216 | 0 | newShape->numlines = 0; |
217 | 0 | if (newShape->line) { |
218 | 0 | free(newShape->line); |
219 | 0 | newShape->line = NULL; |
220 | 0 | } |
221 | |
|
222 | 0 | shape = &initialShape; |
223 | 0 | } |
224 | |
|
225 | 0 | for (j = 0; j < shape->numlines; ++j) { |
226 | 0 | int k, ws, res; |
227 | 0 | lineObj newLine = {0, NULL}; |
228 | 0 | lineWindow lw; |
229 | | |
230 | | /* determine if we can use the ss for this line */ |
231 | 0 | ws = ss; |
232 | 0 | if (ws >= shape->line[j].numpoints) { |
233 | 0 | ws = shape->line[j].numpoints - 1; |
234 | 0 | } |
235 | |
|
236 | 0 | if (ws % 2 == 0) |
237 | 0 | ws -= 1; |
238 | |
|
239 | 0 | initLineWindow(&lw, &shape->line[j], ws); |
240 | 0 | msAddLine(newShape, &newLine); |
241 | |
|
242 | 0 | coeff[lw.index] = 1; |
243 | 0 | for (k = 0; k < lw.index; ++k) { |
244 | 0 | coeff[lw.index + (k + 1)] = coeff[lw.index - k] / 2; |
245 | 0 | coeff[lw.index - (k + 1)] = coeff[lw.index + k] / 2; |
246 | 0 | } |
247 | |
|
248 | 0 | while ((res = nextLineWindow(&lw)) != MS_DONE) { |
249 | 0 | double sum_x = 0, sum_y = 0, sum = 0; |
250 | 0 | pointObj point = {0}; // initialize |
251 | 0 | int k = 0; |
252 | |
|
253 | 0 | if (res == MS_FALSE) { /* invalid window */ |
254 | 0 | msAddPointToLine(&newShape->line[j], lw.points[lw.index]); |
255 | 0 | continue; |
256 | 0 | } |
257 | | |
258 | | /* Apply Coefficient */ |
259 | 0 | p[lw.index] = *lw.points[lw.index]; |
260 | 0 | for (k = 0; k < lw.index; ++k) { |
261 | 0 | p[lw.index - (k + 1)] = *lw.points[lw.index - (k + 1)]; |
262 | 0 | p[lw.index - (k + 1)].x *= coeff[lw.index - (k + 1)]; |
263 | 0 | p[lw.index - (k + 1)].y *= coeff[lw.index - (k + 1)]; |
264 | 0 | p[lw.index + (k + 1)] = *lw.points[lw.index + (k + 1)]; |
265 | 0 | p[lw.index + (k + 1)].x *= coeff[lw.index + (k + 1)]; |
266 | 0 | p[lw.index + (k + 1)].y *= coeff[lw.index + (k + 1)]; |
267 | 0 | } |
268 | |
|
269 | 0 | for (k = 0; k < lw.size; ++k) { |
270 | 0 | sum += coeff[k]; |
271 | 0 | sum_x += p[k].x; |
272 | 0 | sum_y += p[k].y; |
273 | 0 | } |
274 | |
|
275 | 0 | point.x = sum_x / sum; |
276 | 0 | point.y = sum_y / sum; |
277 | 0 | msAddPointToLine(&newShape->line[j], &point); |
278 | 0 | } |
279 | |
|
280 | 0 | freeLineWindow(&lw); |
281 | 0 | } |
282 | |
|
283 | 0 | if (i > 0) { |
284 | 0 | msFreeShape(shape); |
285 | 0 | shape = newShape; |
286 | 0 | } |
287 | 0 | } |
288 | |
|
289 | 0 | free(p); |
290 | 0 | free(coeff); |
291 | |
|
292 | 0 | return newShape; |
293 | 0 | } |