Coverage Report

Created: 2025-06-13 06:18

/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
}