Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MapServer/src/mapprimitive.cpp
Line
Count
Source
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Implementations for rectObj, pointObj, lineObj, shapeObj, etc.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2008 Regents of the University of Minnesota.
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 ****************************************************************************/
29
#include "mapserver.h"
30
#include "mapprimitive.h"
31
#include <assert.h>
32
#include <locale.h>
33
#include "fontcache.h"
34
35
#include <algorithm>
36
37
typedef enum { CLIP_LEFT, CLIP_MIDDLE, CLIP_RIGHT } CLIP_STATE;
38
39
#define CLIP_CHECK(min, a, max)                                                \
40
0
  ((a) < (min) ? CLIP_LEFT : ((a) > (max) ? CLIP_RIGHT : CLIP_MIDDLE));
41
#define ROUND(a) ((a) + 0.5)
42
0
#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
43
#define EDGE_CHECK(x0, x, x1)                                                  \
44
0
  ((x) < MS_MIN((x0), (x1))                                                    \
45
0
       ? CLIP_LEFT                                                             \
46
0
       : ((x) > MS_MAX((x0), (x1)) ? CLIP_RIGHT : CLIP_MIDDLE))
47
48
#ifndef INFINITY
49
#define INFINITY (1.0e+30)
50
#endif
51
0
#define NEARZERO (1.0e-30) /* 1/INFINITY */
52
53
0
void msPrintShape(shapeObj *p) {
54
0
  int i, j;
55
56
0
  msDebug("Shape contains %d parts.\n", p->numlines);
57
0
  for (i = 0; i < p->numlines; i++) {
58
0
    msDebug("\tPart %d contains %d points.\n", i, p->line[i].numpoints);
59
0
    for (j = 0; j < p->line[i].numpoints; j++) {
60
0
      msDebug("\t\t%d: (%f, %f)\n", j, p->line[i].point[j].x,
61
0
              p->line[i].point[j].y);
62
0
    }
63
0
  }
64
0
}
65
66
16.7k
shapeObj *msShapeFromWKT(const char *string) {
67
#ifdef USE_GEOS
68
  return msGEOSShapeFromWKT(string);
69
#else
70
16.7k
  return msOGRShapeFromWKT(string);
71
16.7k
#endif
72
16.7k
}
73
74
0
char *msShapeToWKT(shapeObj *shape) {
75
#ifdef USE_GEOS
76
  char *pszGEOSStr;
77
  char *pszStr;
78
  pszGEOSStr = msGEOSShapeToWKT(shape);
79
  pszStr = (pszGEOSStr) ? msStrdup(pszGEOSStr) : NULL;
80
  msGEOSFreeWKT(pszGEOSStr);
81
  return pszStr;
82
#else
83
0
  return msOGRShapeToWKT(shape);
84
0
#endif
85
0
}
86
87
0
shapeObj::shapeObj() { msInitShape(this); }
88
89
0
shapeObj::~shapeObj() { msFreeShape(this); }
90
91
0
shapeObj::shapeObj(const shapeObj &other) {
92
0
  msInitShape(this);
93
0
  operator=(other);
94
0
}
95
96
0
shapeObj::shapeObj(shapeObj &&other) {
97
0
  msInitShape(this);
98
0
  operator=(std::move(other));
99
0
}
100
101
0
shapeObj &shapeObj::operator=(const shapeObj &other) {
102
0
  msFreeShape(this);
103
0
  msCopyShape(&other, this);
104
0
  return *this;
105
0
}
106
107
0
shapeObj &shapeObj::operator=(shapeObj &&other) {
108
0
  std::swap(line, other.line);
109
0
  std::swap(numlines, other.numlines);
110
0
  type = other.type;
111
0
  bounds = other.bounds;
112
113
  /* attribute component */
114
0
  std::swap(values, other.values);
115
0
  std::swap(numvalues, other.numvalues);
116
117
0
  std::swap(geometry, other.geometry);
118
0
  std::swap(renderer_cache, other.renderer_cache);
119
120
  /* annotation component */
121
0
  std::swap(text, other.text);
122
123
  /* bookkeeping component */
124
0
  classindex = other.classindex; /* default class */
125
0
  tileindex = other.tileindex;
126
0
  index = other.index;
127
0
  resultindex = other.resultindex;
128
129
0
  scratch = other.scratch;
130
131
0
  return *this;
132
0
}
133
134
1.84M
void msInitShape(shapeObj *shape) {
135
  /* spatial component */
136
1.84M
  shape->line = NULL;
137
1.84M
  shape->numlines = 0;
138
1.84M
  shape->type = MS_SHAPE_NULL;
139
1.84M
  shape->bounds.minx = shape->bounds.miny = -1;
140
1.84M
  shape->bounds.maxx = shape->bounds.maxy = -1;
141
142
  /* attribute component */
143
1.84M
  shape->values = NULL;
144
1.84M
  shape->numvalues = 0;
145
146
1.84M
  shape->geometry = NULL;
147
1.84M
  shape->renderer_cache = NULL;
148
149
  /* annotation component */
150
1.84M
  shape->text = NULL;
151
152
  /* bookkeeping component */
153
1.84M
  shape->classindex = 0; /* default class */
154
1.84M
  shape->tileindex = shape->index = shape->resultindex = -1;
155
156
1.84M
  shape->scratch = MS_FALSE; /* not a temporary/scratch shape */
157
1.84M
}
158
159
1.13k
int msCopyShape(const shapeObj *from, shapeObj *to) {
160
1.13k
  int i;
161
162
1.13k
  if (!from || !to)
163
0
    return (-1);
164
165
1.31k
  for (i = 0; i < from->numlines; i++)
166
175
    msAddLine(to, &(from->line[i])); /* copy each line */
167
168
1.13k
  to->type = from->type;
169
170
1.13k
  to->bounds.minx = from->bounds.minx;
171
1.13k
  to->bounds.miny = from->bounds.miny;
172
1.13k
  to->bounds.maxx = from->bounds.maxx;
173
1.13k
  to->bounds.maxy = from->bounds.maxy;
174
175
1.13k
  if (from->text)
176
151
    to->text = msStrdup(from->text);
177
178
1.13k
  to->classindex = from->classindex;
179
1.13k
  to->index = from->index;
180
1.13k
  to->tileindex = from->tileindex;
181
1.13k
  to->resultindex = from->resultindex;
182
183
1.13k
  if (from->values) {
184
717
    if (to->values)
185
0
      msFreeCharArray(to->values, to->numvalues);
186
717
    to->values = (char **)msSmallMalloc(sizeof(char *) * from->numvalues);
187
1.31k
    for (i = 0; i < from->numvalues; i++)
188
595
      to->values[i] = msStrdup(from->values[i]);
189
717
    to->numvalues = from->numvalues;
190
717
  }
191
192
1.13k
  to->geometry = NULL; /* GEOS code will build automatically if necessary */
193
1.13k
  to->scratch = from->scratch;
194
195
1.13k
  return (0);
196
1.13k
}
197
198
626k
void msFreeShape(shapeObj *shape) {
199
626k
  int c;
200
201
626k
  if (!shape)
202
4.25k
    return; /* for safety */
203
204
835k
  for (c = 0; c < shape->numlines; c++)
205
213k
    free(shape->line[c].point);
206
207
621k
  if (shape->line)
208
212k
    free(shape->line);
209
621k
  if (shape->values)
210
1.28k
    msFreeCharArray(shape->values, shape->numvalues);
211
621k
  if (shape->text)
212
315
    free(shape->text);
213
214
#ifdef USE_GEOS
215
  msGEOSFreeGeometry(shape);
216
#endif
217
218
621k
  msInitShape(shape); /* now reset */
219
621k
}
220
221
0
int msGetShapeRAMSize(shapeObj *shape) {
222
0
  int i;
223
0
  int size = 0;
224
0
  size += sizeof(shapeObj);
225
0
  size += shape->numlines * sizeof(lineObj);
226
0
  for (i = 0; i < shape->numlines; i++) {
227
0
    size += shape->line[i].numpoints * sizeof(pointObj);
228
0
  }
229
0
  size += shape->numvalues * sizeof(char *);
230
0
  for (i = 0; i < shape->numvalues; i++) {
231
0
    if (shape->values[i])
232
0
      size += strlen(shape->values[i]) + 1;
233
0
  }
234
0
  if (shape->text)
235
0
    size += strlen(shape->text) + 1;
236
0
  return size;
237
0
}
238
239
0
void msFreeLabelPathObj(labelPathObj *path) {
240
0
  msFreeShape(&(path->bounds));
241
0
  msFree(path->path.point);
242
0
  msFree(path->angles);
243
0
  msFree(path);
244
0
}
245
246
0
void msShapeDeleteLine(shapeObj *shape, int line) {
247
0
  if (line < 0 || line >= shape->numlines) {
248
0
    assert(0);
249
0
    return;
250
0
  }
251
252
0
  free(shape->line[line].point);
253
0
  if (line < shape->numlines - 1) {
254
0
    memmove(shape->line + line, shape->line + line + 1,
255
0
            sizeof(lineObj) * (shape->numlines - line - 1));
256
0
  }
257
0
  shape->numlines--;
258
0
}
259
260
0
void msComputeBounds(shapeObj *shape) {
261
0
  int i, j;
262
0
  if (shape->numlines <= 0)
263
0
    return;
264
0
  for (i = 0; i < shape->numlines; i++) {
265
0
    if (shape->line[i].numpoints > 0) {
266
0
      shape->bounds.minx = shape->bounds.maxx = shape->line[i].point[0].x;
267
0
      shape->bounds.miny = shape->bounds.maxy = shape->line[i].point[0].y;
268
0
      break;
269
0
    }
270
0
  }
271
0
  if (i == shape->numlines)
272
0
    return;
273
274
0
  for (i = 0; i < shape->numlines; i++) {
275
0
    for (j = 0; j < shape->line[i].numpoints; j++) {
276
0
      shape->bounds.minx =
277
0
          MS_MIN(shape->bounds.minx, shape->line[i].point[j].x);
278
0
      shape->bounds.maxx =
279
0
          MS_MAX(shape->bounds.maxx, shape->line[i].point[j].x);
280
0
      shape->bounds.miny =
281
0
          MS_MIN(shape->bounds.miny, shape->line[i].point[j].y);
282
0
      shape->bounds.maxy =
283
0
          MS_MAX(shape->bounds.maxy, shape->line[i].point[j].y);
284
0
    }
285
0
  }
286
0
}
287
288
/* checks to see if ring r is an outer ring of shape */
289
0
int msIsOuterRing(shapeObj *shape, int r) {
290
0
  int i, status = MS_TRUE;
291
0
  int result1, result2;
292
293
0
  if (!shape)
294
0
    return MS_FALSE;
295
0
  if (shape->numlines == 1)
296
0
    return MS_TRUE;
297
0
  if (r < 0 || r >= shape->numlines)
298
0
    return MS_FALSE; /* bad ring index */
299
300
0
  for (i = 0; i < shape->numlines; i++) {
301
0
    if (i == r)
302
0
      continue;
303
304
    /*
305
    ** We have to test 2, or perhaps 3 points on the shape against the ring
306
    *because
307
    ** it is possible that at most one point could touch the ring and the
308
    *function
309
    ** msPointInPolygon() is indeterminite in that case. (bug #2434)
310
    */
311
0
    result1 = msPointInPolygon(&(shape->line[r].point[0]), &(shape->line[i]));
312
0
    result2 = msPointInPolygon(&(shape->line[r].point[1]), &(shape->line[i]));
313
0
    if (result1 ==
314
0
        result2) { /* same result twice, neither point was on the edge */
315
0
      if (result1 == MS_TRUE)
316
0
        status = !status;
317
0
    } else { /* one of the first 2 points were on the edge of the ring, the next
318
                one isn't */
319
0
      if (msPointInPolygon(&(shape->line[r].point[2]), &(shape->line[i])) ==
320
0
          MS_TRUE)
321
0
        status = !status;
322
0
    }
323
0
  }
324
325
0
  return (status);
326
0
}
327
328
/*
329
** Returns a list of outer rings for shape (the list has one entry for each
330
*ring,
331
** MS_TRUE for outer rings).
332
*/
333
0
int *msGetOuterList(shapeObj *shape) {
334
0
  int i;
335
0
  int *list;
336
337
0
  if (!shape)
338
0
    return NULL;
339
340
0
  list = (int *)malloc(sizeof(int) * shape->numlines);
341
0
  MS_CHECK_ALLOC(list, sizeof(int) * shape->numlines, NULL);
342
343
0
  for (i = 0; i < shape->numlines; i++)
344
0
    list[i] = msIsOuterRing(shape, i);
345
346
0
  return list;
347
0
}
348
349
/*
350
** Returns a list of inner rings for ring r in shape (given a list of outer
351
*rings).
352
*/
353
0
int *msGetInnerList(shapeObj *shape, int r, int *outerlist) {
354
0
  int i;
355
0
  int *list;
356
357
0
  if (!shape || !outerlist)
358
0
    return NULL;
359
0
  if (r < 0 || r >= shape->numlines)
360
0
    return NULL; /* bad ring index */
361
362
0
  list = (int *)malloc(sizeof(int) * shape->numlines);
363
0
  MS_CHECK_ALLOC(list, sizeof(int) * shape->numlines, NULL);
364
365
0
  for (i = 0; i < shape->numlines; i++) { /* test all rings against the ring */
366
367
0
    if (outerlist[i] == MS_TRUE) { /* ring is an outer and can't be an inner */
368
0
      list[i] = MS_FALSE;
369
0
      continue;
370
0
    }
371
372
    /* A valid inner ring may touch its outer ring at most one point. */
373
    /* In the case the first point matches a vertex of an outer ring, */
374
    /* msPointInPolygon() might return 0 or 1 (depending on coordinate values,
375
     */
376
    /* see msGetOuterList()), so test a second point if the first test */
377
    /* returned that the point is not inside the outer ring. */
378
    /* Fixes #5299 */
379
    /* Of course all of this assumes that the geometries are indeed valid in */
380
    /* OGC terms, otherwise all logic of msIsOuterRing(), msGetOuterList(), */
381
    /* and msGetInnerList() has undefined behavior. */
382
0
    list[i] = msPointInPolygon(&(shape->line[i].point[0]), &(shape->line[r])) ||
383
0
              msPointInPolygon(&(shape->line[i].point[1]), &(shape->line[r]));
384
0
  }
385
386
0
  return (list);
387
0
}
388
389
/*
390
** Add point to a line object.
391
**
392
** Note that reallocating the point array larger for each point can
393
** be pretty inefficient, so use this function sparingly.  Mostly
394
** geometries creators should create their own working lineObj and
395
** then call msAddLine() to add it to a shape.
396
*/
397
398
0
int msAddPointToLine(lineObj *line, pointObj *point) {
399
0
  line->numpoints += 1;
400
401
0
  line->point = (pointObj *)msSmallRealloc(line->point,
402
0
                                           sizeof(pointObj) * line->numpoints);
403
0
  line->point[line->numpoints - 1] = *point;
404
405
0
  return MS_SUCCESS;
406
0
}
407
408
1.23k
int msAddLine(shapeObj *p, const lineObj *new_line) {
409
1.23k
  lineObj lineCopy;
410
411
1.23k
  lineCopy.numpoints = new_line->numpoints;
412
1.23k
  lineCopy.point = (pointObj *)malloc(new_line->numpoints * sizeof(pointObj));
413
1.23k
  MS_CHECK_ALLOC(lineCopy.point, new_line->numpoints * sizeof(pointObj),
414
1.23k
                 MS_FAILURE);
415
416
1.23k
  if (new_line->point)
417
193
    memcpy(lineCopy.point, new_line->point,
418
193
           sizeof(pointObj) * new_line->numpoints);
419
420
  // cppcheck-suppress memleak
421
1.23k
  return msAddLineDirectly(p, &lineCopy);
422
1.23k
}
423
424
/*
425
** Same as msAddLine(), except that this version "seizes" the points
426
** array from the passed in line and uses it instead of copying it.
427
*/
428
12.4k
int msAddLineDirectly(shapeObj *p, lineObj *new_line) {
429
12.4k
  int c;
430
431
12.4k
  if (p->numlines == 0) {
432
11.4k
    p->line = (lineObj *)malloc(sizeof(lineObj));
433
11.4k
  } else {
434
992
    lineObj *newline =
435
992
        (lineObj *)realloc(p->line, (p->numlines + 1) * sizeof(lineObj));
436
992
    if (!newline) {
437
0
      free(p->line);
438
0
    }
439
992
    p->line = newline;
440
992
  }
441
12.4k
  if (!p->line) {
442
0
    free(new_line->point);
443
0
    new_line->point = NULL;
444
0
    new_line->numpoints = 0;
445
0
  }
446
12.4k
  MS_CHECK_ALLOC(p->line, (p->numlines + 1) * sizeof(lineObj), MS_FAILURE);
447
448
  /* Copy the new line onto the end of the extended line array */
449
12.4k
  c = p->numlines;
450
12.4k
  p->line[c].numpoints = new_line->numpoints;
451
12.4k
  p->line[c].point = new_line->point;
452
453
  /* strip points reference off the passed in lineObj */
454
12.4k
  new_line->point = NULL;
455
12.4k
  new_line->numpoints = 0;
456
457
  /* Update the polygon information */
458
12.4k
  p->numlines++;
459
460
12.4k
  return (MS_SUCCESS);
461
12.4k
}
462
463
/*
464
** Converts a rect array to a shapeObj structure. Note order is CW assuming y
465
*origin
466
** is in the lower left corner (normal Cartesian coordinate system). Also
467
*polygon is
468
** is closed (i.e. first=last). This conforms to the shapefile specification.
469
*For image
470
** coordinate systems (i.e. GD) this is back-ass-ward, which is fine cause the
471
*function
472
** that calculates direction assumes min y = lower left, this way it'll still
473
*work. Drawing
474
** functions are independent of direction. Orientation problems can cause some
475
*nasty bugs.
476
*/
477
0
void msRectToPolygon(rectObj rect, shapeObj *poly) {
478
0
  lineObj line = {0, NULL};
479
480
0
  line.point = (pointObj *)msSmallMalloc(sizeof(pointObj) * 5);
481
482
0
  line.point[0].x = rect.minx;
483
0
  line.point[0].y = rect.miny;
484
0
  line.point[1].x = rect.minx;
485
0
  line.point[1].y = rect.maxy;
486
0
  line.point[2].x = rect.maxx;
487
0
  line.point[2].y = rect.maxy;
488
0
  line.point[3].x = rect.maxx;
489
0
  line.point[3].y = rect.miny;
490
0
  line.point[4].x = line.point[0].x;
491
0
  line.point[4].y = line.point[0].y;
492
493
0
  line.numpoints = 5;
494
495
0
  msAddLine(poly, &line);
496
0
  if (poly->numlines == 1) { /* poly was empty to begin with */
497
0
    poly->type = MS_SHAPE_POLYGON;
498
0
    poly->bounds = rect;
499
0
  } else
500
0
    msMergeRect(&poly->bounds, &rect);
501
0
  free(line.point);
502
0
}
503
504
/*
505
** Private implementation of the Sutherland-Cohen algorithm. Inspired by
506
** "Getting Graphic: Programming Fundamentals in C and C++" by Mark Finlay
507
** and John Petritis. (pages 179-182)
508
*/
509
static int clipLine(double *x1, double *y1, double *x2, double *y2,
510
0
                    rectObj rect) {
511
0
  double x, y;
512
0
  double slope;
513
0
  CLIP_STATE check1, check2;
514
515
0
  if (*x1 < rect.minx && *x2 < rect.minx)
516
0
    return (MS_FALSE);
517
0
  if (*x1 > rect.maxx && *x2 > rect.maxx)
518
0
    return (MS_FALSE);
519
520
0
  check1 = CLIP_CHECK(rect.minx, *x1, rect.maxx);
521
0
  check2 = CLIP_CHECK(rect.minx, *x2, rect.maxx);
522
0
  if (check1 == CLIP_LEFT || check2 == CLIP_LEFT) {
523
0
    slope = (*y2 - *y1) / (*x2 - *x1);
524
0
    y = *y1 + (rect.minx - *x1) * slope;
525
0
    if (check1 == CLIP_LEFT) {
526
0
      *x1 = rect.minx;
527
0
      *y1 = y;
528
0
    } else {
529
0
      *x2 = rect.minx;
530
0
      *y2 = y;
531
0
    }
532
0
  }
533
0
  if (check1 == CLIP_RIGHT || check2 == CLIP_RIGHT) {
534
0
    slope = (*y2 - *y1) / (*x2 - *x1);
535
0
    y = *y1 + (rect.maxx - *x1) * slope;
536
0
    if (check1 == CLIP_RIGHT) {
537
0
      *x1 = rect.maxx;
538
0
      *y1 = y;
539
0
    } else {
540
0
      *x2 = rect.maxx;
541
0
      *y2 = y;
542
0
    }
543
0
  }
544
545
0
  if (*y1 < rect.miny && *y2 < rect.miny)
546
0
    return (MS_FALSE);
547
0
  if (*y1 > rect.maxy && *y2 > rect.maxy)
548
0
    return (MS_FALSE);
549
550
0
  check1 = CLIP_CHECK(rect.miny, *y1, rect.maxy);
551
0
  check2 = CLIP_CHECK(rect.miny, *y2, rect.maxy);
552
0
  if (check1 == CLIP_LEFT || check2 == CLIP_LEFT) {
553
0
    slope = (*x2 - *x1) / (*y2 - *y1);
554
0
    x = *x1 + (rect.miny - *y1) * slope;
555
0
    if (check1 == CLIP_LEFT) {
556
0
      *x1 = x;
557
0
      *y1 = rect.miny;
558
0
    } else {
559
0
      *x2 = x;
560
0
      *y2 = rect.miny;
561
0
    }
562
0
  }
563
0
  if (check1 == CLIP_RIGHT || check2 == CLIP_RIGHT) {
564
0
    slope = (*x2 - *x1) / (*y2 - *y1);
565
0
    x = *x1 + (rect.maxy - *y1) * slope;
566
0
    if (check1 == CLIP_RIGHT) {
567
0
      *x1 = x;
568
0
      *y1 = rect.maxy;
569
0
    } else {
570
0
      *x2 = x;
571
0
      *y2 = rect.maxy;
572
0
    }
573
0
  }
574
575
0
  return (MS_TRUE);
576
0
}
577
578
/*
579
** Routine for clipping a polyline, stored in a shapeObj struct, to a
580
** rectangle. Uses clipLine() function to create a new shapeObj.
581
*/
582
0
void msClipPolylineRect(shapeObj *shape, rectObj rect) {
583
0
  int i, j;
584
0
  lineObj line = {0, NULL};
585
0
  double x1, x2, y1, y2;
586
0
  shapeObj tmp;
587
588
0
  if (!shape || shape->numlines == 0) /* nothing to clip */
589
0
    return;
590
591
  /*
592
  ** Don't do any clip processing of shapes completely within the
593
  ** clip rectangle based on a comparison of bounds.   We could do
594
  ** something similar for completely outside, but that rarely occurs
595
  ** since the spatial query at the layer read level has generally already
596
  ** discarded all shapes completely outside the rect.
597
  */
598
0
  if (shape->bounds.maxx <= rect.maxx && shape->bounds.minx >= rect.minx &&
599
0
      shape->bounds.maxy <= rect.maxy && shape->bounds.miny >= rect.miny) {
600
0
    return;
601
0
  }
602
603
0
  for (i = 0; i < shape->numlines; i++) {
604
605
0
    line.point =
606
0
        (pointObj *)msSmallMalloc(sizeof(pointObj) * shape->line[i].numpoints);
607
0
    line.numpoints = 0;
608
609
0
    x1 = shape->line[i].point[0].x;
610
0
    y1 = shape->line[i].point[0].y;
611
0
    for (j = 1; j < shape->line[i].numpoints; j++) {
612
0
      x2 = shape->line[i].point[j].x;
613
0
      y2 = shape->line[i].point[j].y;
614
615
0
      if (clipLine(&x1, &y1, &x2, &y2, rect) == MS_TRUE) {
616
0
        if (line.numpoints == 0) { /* first segment, add both points */
617
0
          line.point[0].x = x1;
618
0
          line.point[0].y = y1;
619
0
          line.point[1].x = x2;
620
0
          line.point[1].y = y2;
621
0
          line.numpoints = 2;
622
0
        } else { /* add just the last point */
623
0
          line.point[line.numpoints].x = x2;
624
0
          line.point[line.numpoints].y = y2;
625
0
          line.numpoints++;
626
0
        }
627
628
0
        if ((x2 != shape->line[i].point[j].x) ||
629
0
            (y2 != shape->line[i].point[j].y)) {
630
0
          msAddLine(&tmp, &line);
631
0
          line.numpoints = 0; /* new line */
632
0
        }
633
0
      }
634
635
0
      x1 = shape->line[i].point[j].x;
636
0
      y1 = shape->line[i].point[j].y;
637
0
    }
638
639
0
    if (line.numpoints > 0) {
640
0
      msAddLineDirectly(&tmp, &line);
641
0
    } else {
642
0
      free(line.point);
643
0
      line.numpoints = 0; /* new line */
644
0
    }
645
0
  }
646
647
0
  for (i = 0; i < shape->numlines; i++)
648
0
    free(shape->line[i].point);
649
0
  free(shape->line);
650
0
  shape->numlines = 0;
651
0
  shape->line = nullptr;
652
653
0
  std::swap(shape->line, tmp.line);
654
0
  std::swap(shape->numlines, tmp.numlines);
655
656
0
  msComputeBounds(shape);
657
0
}
658
659
/*
660
** Slightly modified version of the Liang-Barsky polygon clipping algorithm
661
*/
662
0
void msClipPolygonRect(shapeObj *shape, rectObj rect) {
663
0
  int i, j;
664
0
  double deltax, deltay, xin, xout, yin, yout;
665
0
  double tinx, tiny, toutx, touty, tin1, tin2, tout;
666
0
  double x1, y1, x2, y2;
667
668
0
  shapeObj tmp;
669
0
  lineObj line = {0, NULL};
670
671
0
  if (!shape || shape->numlines == 0) /* nothing to clip */
672
0
    return;
673
674
0
  msInitShape(&tmp);
675
676
  /*
677
  ** Don't do any clip processing of shapes completely within the
678
  ** clip rectangle based on a comparison of bounds.   We could do
679
  ** something similar for completely outside, but that rarely occurs
680
  ** since the spatial query at the layer read level has generally already
681
  ** discarded all shapes completely outside the rect.
682
  */
683
0
  if (shape->bounds.maxx <= rect.maxx && shape->bounds.minx >= rect.minx &&
684
0
      shape->bounds.maxy <= rect.maxy && shape->bounds.miny >= rect.miny) {
685
0
    return;
686
0
  }
687
688
0
  for (j = 0; j < shape->numlines; j++) {
689
690
    /* A single edge can emit up to 3 points (a clip-rect corner, the entry
691
       intersection and the exit/endpoint), so the worst case over numpoints-1
692
       edges plus the closure point is 3*numpoints-2. */
693
0
    line.point = (pointObj *)msSmallMalloc(
694
0
        sizeof(pointObj) * 3 * shape->line[j].numpoints + sizeof(pointObj));
695
0
    line.numpoints = 0;
696
697
0
    for (i = 0; i < shape->line[j].numpoints - 1; i++) {
698
699
0
      x1 = shape->line[j].point[i].x;
700
0
      y1 = shape->line[j].point[i].y;
701
0
      x2 = shape->line[j].point[i + 1].x;
702
0
      y2 = shape->line[j].point[i + 1].y;
703
704
0
      deltax = x2 - x1;
705
0
      if (deltax == 0) { /* bump off of the vertical */
706
0
        deltax = (x1 > rect.minx) ? -NEARZERO : NEARZERO;
707
0
      }
708
0
      deltay = y2 - y1;
709
0
      if (deltay == 0) { /* bump off of the horizontal */
710
0
        deltay = (y1 > rect.miny) ? -NEARZERO : NEARZERO;
711
0
      }
712
713
0
      if (deltax > 0) { /*  points to right */
714
0
        xin = rect.minx;
715
0
        xout = rect.maxx;
716
0
      } else {
717
0
        xin = rect.maxx;
718
0
        xout = rect.minx;
719
0
      }
720
0
      if (deltay > 0) { /*  points up */
721
0
        yin = rect.miny;
722
0
        yout = rect.maxy;
723
0
      } else {
724
0
        yin = rect.maxy;
725
0
        yout = rect.miny;
726
0
      }
727
728
0
      tinx = (xin - x1) / deltax;
729
0
      tiny = (yin - y1) / deltay;
730
731
0
      if (tinx < tiny) { /* hits x first */
732
0
        tin1 = tinx;
733
0
        tin2 = tiny;
734
0
      } else { /* hits y first */
735
0
        tin1 = tiny;
736
0
        tin2 = tinx;
737
0
      }
738
739
0
      if (1 >= tin1) {
740
0
        if (0 < tin1) {
741
0
          line.point[line.numpoints].x = xin;
742
0
          line.point[line.numpoints].y = yin;
743
0
          line.numpoints++;
744
0
        }
745
0
        if (1 >= tin2) {
746
0
          toutx = (xout - x1) / deltax;
747
0
          touty = (yout - y1) / deltay;
748
749
0
          tout = (toutx < touty) ? toutx : touty;
750
751
0
          if (0 < tin2 || 0 < tout) {
752
0
            if (tin2 <= tout) {
753
0
              if (0 < tin2) {
754
0
                if (tinx > tiny) {
755
0
                  line.point[line.numpoints].x = xin;
756
0
                  line.point[line.numpoints].y = y1 + tinx * deltay;
757
0
                  line.numpoints++;
758
0
                } else {
759
0
                  line.point[line.numpoints].x = x1 + tiny * deltax;
760
0
                  line.point[line.numpoints].y = yin;
761
0
                  line.numpoints++;
762
0
                }
763
0
              }
764
0
              if (1 > tout) {
765
0
                if (toutx < touty) {
766
0
                  line.point[line.numpoints].x = xout;
767
0
                  line.point[line.numpoints].y = y1 + toutx * deltay;
768
0
                  line.numpoints++;
769
0
                } else {
770
0
                  line.point[line.numpoints].x = x1 + touty * deltax;
771
0
                  line.point[line.numpoints].y = yout;
772
0
                  line.numpoints++;
773
0
                }
774
0
              } else {
775
0
                line.point[line.numpoints].x = x2;
776
0
                line.point[line.numpoints].y = y2;
777
0
                line.numpoints++;
778
0
              }
779
0
            } else {
780
0
              if (tinx > tiny) {
781
0
                line.point[line.numpoints].x = xin;
782
0
                line.point[line.numpoints].y = yout;
783
0
                line.numpoints++;
784
0
              } else {
785
0
                line.point[line.numpoints].x = xout;
786
0
                line.point[line.numpoints].y = yin;
787
0
                line.numpoints++;
788
0
              }
789
0
            }
790
0
          }
791
0
        }
792
0
      }
793
0
    }
794
795
0
    if (line.numpoints > 0) {
796
0
      line.point[line.numpoints].x = line.point[0].x; /* force closure */
797
0
      line.point[line.numpoints].y = line.point[0].y;
798
0
      line.numpoints++;
799
0
      msAddLineDirectly(&tmp, &line);
800
0
    } else {
801
0
      free(line.point);
802
0
    }
803
0
  } /* next line */
804
805
0
  for (i = 0; i < shape->numlines; i++)
806
0
    free(shape->line[i].point);
807
0
  free(shape->line);
808
0
  shape->numlines = 0;
809
0
  shape->line = nullptr;
810
811
0
  std::swap(shape->line, tmp.line);
812
0
  std::swap(shape->numlines, tmp.numlines);
813
814
0
  msComputeBounds(shape);
815
816
0
  return;
817
0
}
818
819
/*
820
** offsets a point relative to an image position
821
*/
822
0
void msOffsetPointRelativeTo(pointObj *point, layerObj *layer) {
823
0
  double x = 0, y = 0;
824
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
825
0
    return;
826
827
0
  if (layer->transform == MS_TRUE)
828
0
    return; /* nothing to do */
829
830
0
  if (layer->units == MS_PERCENTAGES) {
831
0
    point->x *= (layer->map->width - 1);
832
0
    point->y *= (layer->map->height - 1);
833
0
  }
834
835
0
  if (layer->transform == MS_FALSE || layer->transform == MS_UL)
836
0
    return; /* done */
837
838
0
  switch (layer->transform) {
839
0
  case MS_UC:
840
0
    x = (layer->map->width - 1) / 2;
841
0
    y = 0;
842
0
    break;
843
0
  case MS_UR:
844
0
    x = layer->map->width - 1;
845
0
    y = 0;
846
0
    break;
847
0
  case MS_CL:
848
0
    x = 0;
849
0
    y = layer->map->height / 2;
850
0
    break;
851
0
  case MS_CC:
852
0
    x = layer->map->width / 2;
853
0
    y = layer->map->height / 2;
854
0
    break;
855
0
  case MS_CR:
856
0
    x = layer->map->width - 1;
857
0
    y = layer->map->height / 2;
858
0
    break;
859
0
  case MS_LL:
860
0
    x = 0;
861
0
    y = layer->map->height - 1;
862
0
    break;
863
0
  case MS_LC:
864
0
    x = layer->map->width / 2;
865
0
    y = layer->map->height - 1;
866
0
    break;
867
0
  case MS_LR:
868
0
    x = layer->map->width - 1;
869
0
    y = layer->map->height - 1;
870
0
    break;
871
0
  }
872
873
0
  point->x += x;
874
0
  point->y += y;
875
876
0
  return;
877
0
}
878
879
/*
880
** offsets a shape relative to an image position
881
*/
882
0
void msOffsetShapeRelativeTo(shapeObj *shape, layerObj *layer) {
883
0
  int i, j;
884
0
  double x = 0, y = 0;
885
886
0
  if (layer->transform == MS_TRUE)
887
0
    return; /* nothing to do */
888
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
889
0
    return;
890
891
0
  if (layer->units == MS_PERCENTAGES) {
892
0
    for (i = 0; i < shape->numlines; i++) {
893
0
      for (j = 0; j < shape->line[i].numpoints; j++) {
894
0
        shape->line[i].point[j].x *= (layer->map->width - 1);
895
0
        shape->line[i].point[j].y *= (layer->map->height - 1);
896
0
      }
897
0
    }
898
0
  }
899
900
0
  if (layer->transform == MS_FALSE || layer->transform == MS_UL)
901
0
    return; /* done */
902
903
0
  switch (layer->transform) {
904
0
  case MS_UC:
905
0
    x = (layer->map->width - 1) / 2;
906
0
    y = 0;
907
0
    break;
908
0
  case MS_UR:
909
0
    x = layer->map->width - 1;
910
0
    y = 0;
911
0
    break;
912
0
  case MS_CL:
913
0
    x = 0;
914
0
    y = layer->map->height / 2;
915
0
    break;
916
0
  case MS_CC:
917
0
    x = layer->map->width / 2;
918
0
    y = layer->map->height / 2;
919
0
    break;
920
0
  case MS_CR:
921
0
    x = layer->map->width - 1;
922
0
    y = layer->map->height / 2;
923
0
    break;
924
0
  case MS_LL:
925
0
    x = 0;
926
0
    y = layer->map->height - 1;
927
0
    break;
928
0
  case MS_LC:
929
0
    x = layer->map->width / 2;
930
0
    y = layer->map->height - 1;
931
0
    break;
932
0
  case MS_LR:
933
0
    x = layer->map->width - 1;
934
0
    y = layer->map->height - 1;
935
0
    break;
936
0
  }
937
938
0
  for (i = 0; i < shape->numlines; i++) {
939
0
    for (j = 0; j < shape->line[i].numpoints; j++) {
940
0
      shape->line[i].point[j].x += x;
941
0
      shape->line[i].point[j].y += y;
942
0
    }
943
0
  }
944
945
0
  return;
946
0
}
947
948
void msTransformShapeSimplify(shapeObj *shape, rectObj extent,
949
0
                              double cellsize) {
950
0
  int i, j, k, beforelast; /* loop counters */
951
0
  double dx, dy;
952
0
  pointObj *point;
953
0
  double inv_cs = 1.0 / cellsize; /* invert and multiply much faster */
954
0
  int ok = 0;
955
0
  if (shape->numlines == 0)
956
0
    return; /* nothing to transform */
957
958
0
  if (shape->type == MS_SHAPE_LINE) {
959
    /*
960
     * loop through the shape's lines, and do naive simplification
961
     * to discard the points that are too close to one another.
962
     * currently the threshold is to discard points if they fall
963
     * less than a pixel away from their predecessor.
964
     * the simplified line is guaranteed to contain at
965
     * least its first and last point
966
     */
967
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
968
0
      if (shape->line[i].numpoints < 2) {
969
0
        shape->line[i].numpoints = 0;
970
0
        continue; /*skip degenerate lines*/
971
0
      }
972
0
      point = shape->line[i].point;
973
      /*always keep first point*/
974
0
      point[0].x = MS_MAP2IMAGE_X_IC_DBL(point[0].x, extent.minx, inv_cs);
975
0
      point[0].y = MS_MAP2IMAGE_Y_IC_DBL(point[0].y, extent.maxy, inv_cs);
976
0
      beforelast = shape->line[i].numpoints - 1;
977
0
      for (j = 1, k = 1; j < beforelast;
978
0
           j++) { /*loop from second point to first-before-last point*/
979
0
        point[k].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
980
0
        point[k].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
981
0
        dx = (point[k].x - point[k - 1].x);
982
0
        dy = (point[k].y - point[k - 1].y);
983
0
        if (dx * dx + dy * dy > 1)
984
0
          k++;
985
0
      }
986
      /* try to keep last point */
987
0
      point[k].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
988
0
      point[k].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
989
      /* discard last point if equal to the one before it */
990
0
      if (point[k].x != point[k - 1].x || point[k].y != point[k - 1].y) {
991
0
        shape->line[i].numpoints = k + 1;
992
0
      } else {
993
0
        shape->line[i].numpoints = k;
994
0
      }
995
      /* skip degenerate line once more */
996
0
      if (shape->line[i].numpoints < 2) {
997
0
        shape->line[i].numpoints = 0;
998
0
      } else {
999
0
        ok = 1; /* we have at least one line with more than two points */
1000
0
      }
1001
0
    }
1002
0
  } else if (shape->type == MS_SHAPE_POLYGON) {
1003
    /*
1004
     * loop through the shape's lines, and do naive simplification
1005
     * to discard the points that are too close to one another.
1006
     * currently the threshold is to discard points if they fall
1007
     * less than a pixel away from their predecessor.
1008
     * the simplified polygon is guaranteed to contain at
1009
     * least its first, second and last point
1010
     */
1011
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1012
0
      if (shape->line[i].numpoints < 4) {
1013
0
        shape->line[i].numpoints = 0;
1014
0
        continue; /*skip degenerate lines*/
1015
0
      }
1016
0
      point = shape->line[i].point;
1017
      /*always keep first and second point*/
1018
0
      point[0].x = MS_MAP2IMAGE_X_IC_DBL(point[0].x, extent.minx, inv_cs);
1019
0
      point[0].y = MS_MAP2IMAGE_Y_IC_DBL(point[0].y, extent.maxy, inv_cs);
1020
0
      point[1].x = MS_MAP2IMAGE_X_IC_DBL(point[1].x, extent.minx, inv_cs);
1021
0
      point[1].y = MS_MAP2IMAGE_Y_IC_DBL(point[1].y, extent.maxy, inv_cs);
1022
0
      beforelast = shape->line[i].numpoints - 2;
1023
0
      for (j = 2, k = 2; j < beforelast;
1024
0
           j++) { /*loop from second point to second-before-last point*/
1025
0
        point[k].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
1026
0
        point[k].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
1027
0
        dx = (point[k].x - point[k - 1].x);
1028
0
        dy = (point[k].y - point[k - 1].y);
1029
0
        if (dx * dx + dy * dy > 1)
1030
0
          k++;
1031
0
      }
1032
      /*always keep last two points (the last point is the repetition of the
1033
       * first one */
1034
0
      point[k].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
1035
0
      point[k].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
1036
0
      point[k + 1].x =
1037
0
          MS_MAP2IMAGE_X_IC_DBL(point[j + 1].x, extent.minx, inv_cs);
1038
0
      point[k + 1].y =
1039
0
          MS_MAP2IMAGE_Y_IC_DBL(point[j + 1].y, extent.maxy, inv_cs);
1040
0
      shape->line[i].numpoints = k + 2;
1041
0
      ok = 1;
1042
0
    }
1043
0
  } else { /* only for untyped shapes, as point layers don't go through this
1044
              function */
1045
0
    for (i = 0; i < shape->numlines; i++) {
1046
0
      point = shape->line[i].point;
1047
0
      for (j = 0; j < shape->line[i].numpoints; j++) {
1048
0
        point[j].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
1049
0
        point[j].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
1050
0
      }
1051
0
    }
1052
0
    ok = 1;
1053
0
  }
1054
0
  if (!ok) {
1055
0
    for (i = 0; i < shape->numlines; i++) {
1056
0
      free(shape->line[i].point);
1057
0
    }
1058
0
    shape->numlines = 0;
1059
0
  }
1060
0
}
1061
1062
/**
1063
 * Generic function to transform the shape coordinates to output coordinates
1064
 */
1065
void msTransformShape(shapeObj *shape, rectObj extent, double cellsize,
1066
0
                      imageObj *image) {
1067
0
  if (image != NULL && MS_RENDERER_PLUGIN(image->format)) {
1068
0
    rendererVTableObj *renderer = MS_IMAGE_RENDERER(image);
1069
0
    if (renderer->transform_mode == MS_TRANSFORM_SNAPTOGRID) {
1070
0
      msTransformShapeToPixelSnapToGrid(shape, extent, cellsize,
1071
0
                                        renderer->approximation_scale);
1072
0
    } else if (renderer->transform_mode == MS_TRANSFORM_SIMPLIFY) {
1073
0
      msTransformShapeSimplify(shape, extent, cellsize);
1074
0
    } else if (renderer->transform_mode == MS_TRANSFORM_ROUND) {
1075
0
      msTransformShapeToPixelRound(shape, extent, cellsize);
1076
0
    } else if (renderer->transform_mode == MS_TRANSFORM_FULLRESOLUTION) {
1077
0
      msTransformShapeToPixelDoublePrecision(shape, extent, cellsize);
1078
0
    } else if (renderer->transform_mode == MS_TRANSFORM_NONE) {
1079
      /* nothing to do */
1080
0
      return;
1081
0
    }
1082
    /* unknown, do nothing */
1083
0
    return;
1084
0
  }
1085
0
  msTransformShapeToPixelRound(shape, extent, cellsize);
1086
0
}
1087
1088
void msTransformShapeToPixelSnapToGrid(shapeObj *shape, rectObj extent,
1089
                                       double cellsize,
1090
0
                                       double grid_resolution) {
1091
0
  int i, j, k; /* loop counters */
1092
0
  double inv_cs;
1093
0
  if (shape->numlines == 0)
1094
0
    return;
1095
0
  inv_cs = 1.0 / cellsize; /* invert and multiply much faster */
1096
1097
0
  if (shape->type == MS_SHAPE_LINE ||
1098
0
      shape->type == MS_SHAPE_POLYGON) {    /* remove duplicate vertices */
1099
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1100
0
      int snap = 1;
1101
0
      const double x0 = MS_MAP2IMAGE_X_IC_SNAP(
1102
0
          shape->line[i].point[0].x, extent.minx, inv_cs, grid_resolution);
1103
0
      const double y0 = MS_MAP2IMAGE_Y_IC_SNAP(
1104
0
          shape->line[i].point[0].y, extent.maxy, inv_cs, grid_resolution);
1105
1106
      /*do a quick heuristic: will we risk having a degenerate shape*/
1107
0
      if (shape->type == MS_SHAPE_LINE) {
1108
        /*a line is degenerate if it has a single pixel. we check that the first
1109
         * and last pixel are different*/
1110
0
        const double x1 = MS_MAP2IMAGE_X_IC_SNAP(
1111
0
            shape->line[i].point[shape->line[i].numpoints - 1].x, extent.minx,
1112
0
            inv_cs, grid_resolution);
1113
0
        const double y1 = MS_MAP2IMAGE_Y_IC_SNAP(
1114
0
            shape->line[i].point[shape->line[i].numpoints - 1].y, extent.maxy,
1115
0
            inv_cs, grid_resolution);
1116
0
        if (x0 == x1 && y0 == y1) {
1117
0
          snap = 0;
1118
0
        }
1119
0
      } else /* if(shape->type == MS_SHAPE_POLYGON) */ {
1120
0
        const double x1 = MS_MAP2IMAGE_X_IC_SNAP(
1121
0
            shape->line[i].point[shape->line[i].numpoints / 3].x, extent.minx,
1122
0
            inv_cs, grid_resolution);
1123
0
        const double y1 = MS_MAP2IMAGE_Y_IC_SNAP(
1124
0
            shape->line[i].point[shape->line[i].numpoints / 3].y, extent.maxy,
1125
0
            inv_cs, grid_resolution);
1126
0
        const double x2 = MS_MAP2IMAGE_X_IC_SNAP(
1127
0
            shape->line[i].point[shape->line[i].numpoints / 3 * 2].x,
1128
0
            extent.minx, inv_cs, grid_resolution);
1129
0
        const double y2 = MS_MAP2IMAGE_Y_IC_SNAP(
1130
0
            shape->line[i].point[shape->line[i].numpoints / 3 * 2].y,
1131
0
            extent.maxy, inv_cs, grid_resolution);
1132
0
        if ((x0 == x1 && y0 == y1) || (x0 == x2 && y0 == y2) ||
1133
0
            (x1 == x2 && y1 == y2)) {
1134
0
          snap = 0;
1135
0
        }
1136
0
      }
1137
1138
0
      if (snap) {
1139
0
        shape->line[i].point[0].x = x0;
1140
0
        shape->line[i].point[0].y = y0;
1141
0
        for (j = 1, k = 1; j < shape->line[i].numpoints; j++) {
1142
0
          shape->line[i].point[k].x = MS_MAP2IMAGE_X_IC_SNAP(
1143
0
              shape->line[i].point[j].x, extent.minx, inv_cs, grid_resolution);
1144
0
          shape->line[i].point[k].y = MS_MAP2IMAGE_Y_IC_SNAP(
1145
0
              shape->line[i].point[j].y, extent.maxy, inv_cs, grid_resolution);
1146
0
          if (shape->line[i].point[k].x != shape->line[i].point[k - 1].x ||
1147
0
              shape->line[i].point[k].y != shape->line[i].point[k - 1].y)
1148
0
            k++;
1149
0
        }
1150
0
        shape->line[i].numpoints = k;
1151
0
      } else {
1152
0
        if (shape->type == MS_SHAPE_LINE) {
1153
0
          shape->line[i].point[0].x = MS_MAP2IMAGE_X_IC_DBL(
1154
0
              shape->line[i].point[0].x, extent.minx, inv_cs);
1155
0
          shape->line[i].point[0].y = MS_MAP2IMAGE_Y_IC_DBL(
1156
0
              shape->line[i].point[0].y, extent.maxy, inv_cs);
1157
0
          shape->line[i].point[1].x = MS_MAP2IMAGE_X_IC_DBL(
1158
0
              shape->line[i].point[shape->line[i].numpoints - 1].x, extent.minx,
1159
0
              inv_cs);
1160
0
          shape->line[i].point[1].y = MS_MAP2IMAGE_Y_IC_DBL(
1161
0
              shape->line[i].point[shape->line[i].numpoints - 1].y, extent.maxy,
1162
0
              inv_cs);
1163
0
          shape->line[i].numpoints = 2;
1164
0
        } else {
1165
0
          for (j = 0; j < shape->line[i].numpoints; j++) {
1166
0
            shape->line[i].point[j].x = MS_MAP2IMAGE_X_IC_DBL(
1167
0
                shape->line[i].point[j].x, extent.minx, inv_cs);
1168
0
            shape->line[i].point[j].y = MS_MAP2IMAGE_Y_IC_DBL(
1169
0
                shape->line[i].point[j].y, extent.maxy, inv_cs);
1170
0
          }
1171
0
        }
1172
0
      }
1173
0
    }
1174
0
  } else {                                  /* points or untyped shapes */
1175
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1176
0
      for (j = 1; j < shape->line[i].numpoints; j++) {
1177
0
        shape->line[i].point[j].x = MS_MAP2IMAGE_X_IC_DBL(
1178
0
            shape->line[i].point[j].x, extent.minx, inv_cs);
1179
0
        shape->line[i].point[j].y = MS_MAP2IMAGE_Y_IC_DBL(
1180
0
            shape->line[i].point[j].y, extent.maxy, inv_cs);
1181
0
      }
1182
0
    }
1183
0
  }
1184
0
}
1185
1186
void msTransformShapeToPixelRound(shapeObj *shape, rectObj extent,
1187
0
                                  double cellsize) {
1188
0
  int i, j, k; /* loop counters */
1189
0
  double inv_cs;
1190
0
  if (shape->numlines == 0)
1191
0
    return;
1192
0
  inv_cs = 1.0 / cellsize; /* invert and multiply much faster */
1193
0
  if (shape->type == MS_SHAPE_LINE ||
1194
0
      shape->type == MS_SHAPE_POLYGON) {    /* remove duplicate vertices */
1195
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1196
0
      shape->line[i].point[0].x =
1197
0
          MS_MAP2IMAGE_X_IC(shape->line[i].point[0].x, extent.minx, inv_cs);
1198
0
      ;
1199
0
      shape->line[i].point[0].y =
1200
0
          MS_MAP2IMAGE_Y_IC(shape->line[i].point[0].y, extent.maxy, inv_cs);
1201
0
      for (j = 1, k = 1; j < shape->line[i].numpoints; j++) {
1202
0
        shape->line[i].point[k].x =
1203
0
            MS_MAP2IMAGE_X_IC(shape->line[i].point[j].x, extent.minx, inv_cs);
1204
0
        shape->line[i].point[k].y =
1205
0
            MS_MAP2IMAGE_Y_IC(shape->line[i].point[j].y, extent.maxy, inv_cs);
1206
0
        if (shape->line[i].point[k].x != shape->line[i].point[k - 1].x ||
1207
0
            shape->line[i].point[k].y != shape->line[i].point[k - 1].y)
1208
0
          k++;
1209
0
      }
1210
0
      shape->line[i].numpoints = k;
1211
0
    }
1212
0
  } else {                                  /* points or untyped shapes */
1213
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1214
0
      for (j = 0; j < shape->line[i].numpoints; j++) {
1215
0
        shape->line[i].point[j].x =
1216
0
            MS_MAP2IMAGE_X_IC(shape->line[i].point[j].x, extent.minx, inv_cs);
1217
0
        shape->line[i].point[j].y =
1218
0
            MS_MAP2IMAGE_Y_IC(shape->line[i].point[j].y, extent.maxy, inv_cs);
1219
0
      }
1220
0
    }
1221
0
  }
1222
0
}
1223
1224
void msTransformShapeToPixelDoublePrecision(shapeObj *shape, rectObj extent,
1225
0
                                            double cellsize) {
1226
0
  int i, j;                       /* loop counters */
1227
0
  double inv_cs = 1.0 / cellsize; /* invert and multiply much faster */
1228
0
  for (i = 0; i < shape->numlines; i++) {
1229
0
    for (j = 0; j < shape->line[i].numpoints; j++) {
1230
0
      shape->line[i].point[j].x =
1231
0
          MS_MAP2IMAGE_X_IC_DBL(shape->line[i].point[j].x, extent.minx, inv_cs);
1232
0
      shape->line[i].point[j].y =
1233
0
          MS_MAP2IMAGE_Y_IC_DBL(shape->line[i].point[j].y, extent.maxy, inv_cs);
1234
0
    }
1235
0
  }
1236
0
}
1237
1238
/*
1239
** Converts from map coordinates to image coordinates
1240
*/
1241
0
void msTransformPixelToShape(shapeObj *shape, rectObj extent, double cellsize) {
1242
0
  int i, j; /* loop counters */
1243
1244
0
  if (shape->numlines == 0)
1245
0
    return; /* nothing to transform */
1246
1247
0
  if (shape->type == MS_SHAPE_LINE ||
1248
0
      shape->type == MS_SHAPE_POLYGON) { /* remove co-linear vertices */
1249
1250
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1251
0
      for (j = 0; j < shape->line[i].numpoints; j++) {
1252
0
        shape->line[i].point[j].x =
1253
0
            MS_IMAGE2MAP_X(shape->line[i].point[j].x, extent.minx, cellsize);
1254
0
        shape->line[i].point[j].y =
1255
0
            MS_IMAGE2MAP_Y(shape->line[i].point[j].y, extent.maxy, cellsize);
1256
0
      }
1257
0
    }
1258
0
  } else { /* points or untyped shapes */
1259
1260
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1261
0
      for (j = 1; j < shape->line[i].numpoints; j++) {
1262
0
        shape->line[i].point[j].x =
1263
0
            MS_IMAGE2MAP_X(shape->line[i].point[j].x, extent.minx, cellsize);
1264
0
        shape->line[i].point[j].y =
1265
0
            MS_IMAGE2MAP_Y(shape->line[i].point[j].y, extent.maxy, cellsize);
1266
0
      }
1267
0
    }
1268
0
  }
1269
1270
0
  return;
1271
0
}
1272
1273
/*
1274
** Not a generic intersection test, we KNOW the lines aren't parallel or
1275
*coincident. To be used with the next
1276
** buffering code only. See code in mapsearch.c for a boolean test for
1277
*intersection.
1278
*/
1279
static pointObj generateLineIntersection(pointObj a, pointObj b, pointObj c,
1280
0
                                         pointObj d) {
1281
0
  pointObj p;
1282
0
  double r;
1283
0
  double denominator, numerator;
1284
1285
0
  if (b.x == c.x && b.y == c.y)
1286
0
    return (b);
1287
1288
0
  numerator = ((a.y - c.y) * (d.x - c.x) - (a.x - c.x) * (d.y - c.y));
1289
0
  denominator = ((b.x - a.x) * (d.y - c.y) - (b.y - a.y) * (d.x - c.x));
1290
1291
0
  r = numerator / denominator;
1292
1293
0
  p.x = MS_NINT(a.x + r * (b.x - a.x));
1294
0
  p.y = MS_NINT(a.y + r * (b.y - a.y));
1295
0
  p.z = 0;
1296
0
  p.m = 0;
1297
1298
0
  return (p);
1299
0
}
1300
1301
0
void bufferPolyline(shapeObj *p, shapeObj *op, int w) {
1302
0
  int i, j;
1303
0
  pointObj a = {0, 0, 0, 0};
1304
0
  lineObj inside, outside;
1305
0
  double angle;
1306
0
  double dx, dy;
1307
1308
0
  for (i = 0; i < p->numlines; i++) {
1309
1310
0
    inside.point =
1311
0
        (pointObj *)msSmallMalloc(sizeof(pointObj) * p->line[i].numpoints);
1312
0
    outside.point =
1313
0
        (pointObj *)msSmallMalloc(sizeof(pointObj) * p->line[i].numpoints);
1314
0
    inside.numpoints = outside.numpoints = p->line[i].numpoints;
1315
1316
0
    angle = asin(MS_ABS(p->line[i].point[1].x - p->line[i].point[0].x) /
1317
0
                 sqrt((((p->line[i].point[1].x - p->line[i].point[0].x) *
1318
0
                        (p->line[i].point[1].x - p->line[i].point[0].x)) +
1319
0
                       ((p->line[i].point[1].y - p->line[i].point[0].y) *
1320
0
                        (p->line[i].point[1].y - p->line[i].point[0].y)))));
1321
0
    if (p->line[i].point[0].x < p->line[i].point[1].x)
1322
0
      dy = sin(angle) * (w / 2);
1323
0
    else
1324
0
      dy = -sin(angle) * (w / 2);
1325
0
    if (p->line[i].point[0].y < p->line[i].point[1].y)
1326
0
      dx = -cos(angle) * (w / 2);
1327
0
    else
1328
0
      dx = cos(angle) * (w / 2);
1329
1330
0
    inside.point[0].x = p->line[i].point[0].x + dx;
1331
0
    inside.point[1].x = p->line[i].point[1].x + dx;
1332
0
    inside.point[0].y = p->line[i].point[0].y + dy;
1333
0
    inside.point[1].y = p->line[i].point[1].y + dy;
1334
1335
0
    outside.point[0].x = p->line[i].point[0].x - dx;
1336
0
    outside.point[1].x = p->line[i].point[1].x - dx;
1337
0
    outside.point[0].y = p->line[i].point[0].y - dy;
1338
0
    outside.point[1].y = p->line[i].point[1].y - dy;
1339
1340
0
    for (j = 2; j < p->line[i].numpoints; j++) {
1341
1342
0
      angle =
1343
0
          asin(MS_ABS(p->line[i].point[j].x - p->line[i].point[j - 1].x) /
1344
0
               sqrt((((p->line[i].point[j].x - p->line[i].point[j - 1].x) *
1345
0
                      (p->line[i].point[j].x - p->line[i].point[j - 1].x)) +
1346
0
                     ((p->line[i].point[j].y - p->line[i].point[j - 1].y) *
1347
0
                      (p->line[i].point[j].y - p->line[i].point[j - 1].y)))));
1348
0
      if (p->line[i].point[j - 1].x < p->line[i].point[j].x)
1349
0
        dy = sin(angle) * (w / 2);
1350
0
      else
1351
0
        dy = -sin(angle) * (w / 2);
1352
0
      if (p->line[i].point[j - 1].y < p->line[i].point[j].y)
1353
0
        dx = -cos(angle) * (w / 2);
1354
0
      else
1355
0
        dx = cos(angle) * (w / 2);
1356
1357
0
      a.x = p->line[i].point[j - 1].x + dx;
1358
0
      inside.point[j].x = p->line[i].point[j].x + dx;
1359
0
      a.y = p->line[i].point[j - 1].y + dy;
1360
0
      inside.point[j].y = p->line[i].point[j].y + dy;
1361
0
      inside.point[j - 1] = generateLineIntersection(
1362
0
          inside.point[j - 2], inside.point[j - 1], a, inside.point[j]);
1363
1364
0
      a.x = p->line[i].point[j - 1].x - dx;
1365
0
      outside.point[j].x = p->line[i].point[j].x - dx;
1366
0
      a.y = p->line[i].point[j - 1].y - dy;
1367
0
      outside.point[j].y = p->line[i].point[j].y - dy;
1368
0
      outside.point[j - 1] = generateLineIntersection(
1369
0
          outside.point[j - 2], outside.point[j - 1], a, outside.point[j]);
1370
0
    }
1371
1372
    /* need a touch of code if 1st point equals last point in p (find
1373
     * intersection) */
1374
1375
0
    msAddLine(op, &inside);
1376
0
    msAddLine(op, &outside);
1377
1378
0
    free(inside.point);
1379
0
    free(outside.point);
1380
0
  }
1381
1382
0
  return;
1383
0
}
1384
1385
0
static double getRingArea(lineObj *ring) {
1386
0
  int i;
1387
0
  double s = 0;
1388
1389
0
  for (i = 0; i < ring->numpoints - 1; i++)
1390
0
    s += (ring->point[i].x * ring->point[i + 1].y -
1391
0
          ring->point[i + 1].x * ring->point[i].y);
1392
1393
0
  return (MS_ABS(s / 2));
1394
0
}
1395
1396
0
double msGetPolygonArea(shapeObj *p) {
1397
0
  int i;
1398
0
  double area = 0;
1399
1400
0
  if (!p)
1401
0
    return 0;
1402
1403
0
  for (i = 0; i < p->numlines; i++) {
1404
0
    if (msIsOuterRing(p, i))
1405
0
      area += getRingArea(&(p->line[i]));
1406
0
    else
1407
0
      area -= getRingArea(&(p->line[i])); /* hole */
1408
0
  }
1409
1410
0
  return area;
1411
0
}
1412
1413
/*
1414
** Computes the center of gravity for a polygon based on it's largest outer ring
1415
*only.
1416
*/
1417
0
static int getPolygonCenterOfGravity(shapeObj *p, pointObj *lp) {
1418
0
  double sx = 0, sy = 0; /* sums */
1419
0
  double largestArea = 0;
1420
1421
0
  for (int i = 0; i < p->numlines; i++) {
1422
0
    double tsx = 0;
1423
0
    double tsy = 0;
1424
0
    double s = 0; /* reset the ring sums */
1425
0
    for (int j = 0; j < p->line[i].numpoints - 1; j++) {
1426
0
      double a = p->line[i].point[j].x * p->line[i].point[j + 1].y -
1427
0
                 p->line[i].point[j + 1].x * p->line[i].point[j].y;
1428
0
      s += a;
1429
0
      tsx += (p->line[i].point[j].x + p->line[i].point[j + 1].x) * a;
1430
0
      tsy += (p->line[i].point[j].y + p->line[i].point[j + 1].y) * a;
1431
0
    }
1432
0
    double area = MS_ABS(s / 2);
1433
1434
0
    if (area > largestArea) {
1435
0
      largestArea = area;
1436
0
      sx = s > 0 ? tsx : -tsx;
1437
0
      sy = s > 0 ? tsy : -tsy;
1438
0
    }
1439
0
  }
1440
0
  if (largestArea == 0) /*degenerate polygon*/
1441
0
    return MS_FAILURE;
1442
1443
0
  lp->x = sx / (6 * largestArea);
1444
0
  lp->y = sy / (6 * largestArea);
1445
1446
0
  return MS_SUCCESS;
1447
0
}
1448
1449
int msGetPolygonCentroid(shapeObj *p, pointObj *lp, double *miny,
1450
0
                         double *maxy) {
1451
0
  int i, j;
1452
0
  double cent_weight_x = 0.0, cent_weight_y = 0.0;
1453
0
  double len, total_len = 0;
1454
1455
0
  *miny = *maxy = p->line[0].point[0].y;
1456
0
  for (i = 0; i < p->numlines; i++) {
1457
0
    for (j = 1; j < p->line[i].numpoints; j++) {
1458
0
      *miny = MS_MIN(*miny, p->line[i].point[j].y);
1459
0
      *maxy = MS_MAX(*maxy, p->line[i].point[j].y);
1460
0
      len = msDistancePointToPoint(&(p->line[i].point[j - 1]),
1461
0
                                   &(p->line[i].point[j]));
1462
0
      cent_weight_x +=
1463
0
          len * ((p->line[i].point[j - 1].x + p->line[i].point[j].x) / 2);
1464
0
      cent_weight_y +=
1465
0
          len * ((p->line[i].point[j - 1].y + p->line[i].point[j].y) / 2);
1466
0
      total_len += len;
1467
0
    }
1468
0
  }
1469
1470
0
  if (total_len == 0)
1471
0
    return (MS_FAILURE);
1472
1473
0
  lp->x = cent_weight_x / total_len;
1474
0
  lp->y = cent_weight_y / total_len;
1475
1476
0
  return (MS_SUCCESS);
1477
0
}
1478
1479
/*
1480
** Find a label point in a polygon.
1481
*/
1482
0
int msPolygonLabelPoint(shapeObj *p, pointObj *lp, double min_dimension) {
1483
0
  double slope;
1484
0
  pointObj *point1 = NULL, *point2 = NULL, cp;
1485
0
  int i, j, nfound;
1486
0
  double x, y, *intersect, temp;
1487
0
  double min, max;
1488
0
  int wrong_order, n;
1489
0
  double len, max_len = 0;
1490
0
  double minx, maxx, maxy, miny;
1491
1492
#ifdef notdef
1493
  int method = 2;
1494
#endif
1495
1496
0
  msComputeBounds(p);
1497
0
  minx = p->bounds.minx;
1498
0
  miny = p->bounds.miny;
1499
0
  maxx = p->bounds.maxx;
1500
0
  maxy = p->bounds.maxy;
1501
1502
0
  if (min_dimension > 0)
1503
0
    if (MS_MIN(maxx - minx, maxy - miny) < min_dimension)
1504
0
      return (MS_FAILURE);
1505
1506
0
  cp.x = (maxx + minx) / 2.0;
1507
0
  cp.y = (maxy + miny) / 2.0;
1508
1509
#ifdef notdef
1510
  switch (method) {
1511
  case 0: /* MBR */
1512
    lp->x = cp.x;
1513
    lp->y = cp.y;
1514
    break;
1515
  case 1: /* centroid */
1516
    if (msGetPolygonCentroid(p, lp, &miny, &maxy) != MS_SUCCESS)
1517
      return (MS_FAILURE);
1518
    break;
1519
  case 2: /* center of gravity */
1520
    if (getPolygonCenterOfGravity(p, lp) != MS_SUCCESS)
1521
      return (MS_FAILURE);
1522
    break;
1523
  }
1524
#else
1525
0
  if (getPolygonCenterOfGravity(p, lp) != MS_SUCCESS)
1526
0
    return (MS_FAILURE);
1527
0
#endif
1528
1529
0
  if (msIntersectPointPolygon(lp, p) == MS_TRUE) {
1530
0
    double dist, min_dist = -1;
1531
1532
    /* compute a distance to the polygon */
1533
0
    for (j = 0; j < p->numlines; j++) {
1534
0
      for (i = 1; i < p->line[j].numpoints; i++) {
1535
0
        dist = msSquareDistancePointToSegment(lp, &(p->line[j].point[i - 1]),
1536
0
                                              &(p->line[j].point[i]));
1537
0
        if ((dist < min_dist) || (min_dist < 0))
1538
0
          min_dist = dist;
1539
0
      }
1540
0
    }
1541
0
    min_dist = sqrt(min_dist);
1542
1543
0
    if (min_dist > .1 * MS_MAX(maxx - minx, maxy - miny))
1544
0
      return (MS_SUCCESS); /* point is not too close to the edge */
1545
0
  }
1546
1547
  /* printf("label: %s\n", p->text);
1548
     printf("    bbox: %g %g %g %g\n",minx, miny, maxx, maxy);
1549
     printf("    center: %g %g\n", cp.x, cp.y);
1550
     printf("    center of gravity: %g %g\n", lp->x, lp->y);
1551
     printf("    dx: %g, dy: %g\n", lp->x-cp.x, lp->y-cp.y);
1552
     printf("    distance to parent shape: %g\n", min_dist);
1553
     return MS_SUCCESS; */
1554
1555
0
  n = 0;
1556
0
  for (j = 0; j < p->numlines; j++) /* count total number of points */
1557
0
    n += p->line[j].numpoints;
1558
0
  intersect = (double *)calloc(n, sizeof(double));
1559
0
  MS_CHECK_ALLOC(intersect, n * sizeof(double), MS_FAILURE);
1560
1561
0
  if (MS_ABS((int)lp->x - (int)cp.x) >
1562
0
      MS_ABS((int)lp->y - (int)cp.y)) { /* center horizontally, fix y */
1563
1564
0
    y = lp->y;
1565
1566
    /* need to find a y that won't intersect any vertices exactly */
1567
0
    max = y - 1; /* first initializing min, max to be any 2 pnts on either side
1568
                    of y */
1569
0
    min = y + 1;
1570
0
    for (j = 0; j < p->numlines; j++) {
1571
0
      if ((min < y) && (max >= y))
1572
0
        break;
1573
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1574
0
        if ((min < y) && (max >= y))
1575
0
          break;
1576
0
        if (p->line[j].point[i].y < y)
1577
0
          min = p->line[j].point[i].y;
1578
0
        if (p->line[j].point[i].y >= y)
1579
0
          max = p->line[j].point[i].y;
1580
0
      }
1581
0
    }
1582
1583
0
    n = 0;
1584
0
    for (j = 0; j < p->numlines; j++) {
1585
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1586
0
        if ((p->line[j].point[i].y < y) &&
1587
0
            ((y - p->line[j].point[i].y) < (y - min)))
1588
0
          min = p->line[j].point[i].y;
1589
0
        if ((p->line[j].point[i].y >= y) &&
1590
0
            ((p->line[j].point[i].y - y) < (max - y)))
1591
0
          max = p->line[j].point[i].y;
1592
0
      }
1593
0
    }
1594
1595
0
    if (min == max) {
1596
0
      msFree(intersect);
1597
0
      return (MS_FAILURE);
1598
0
    } else
1599
0
      y = (max + min) / 2.0;
1600
1601
0
    nfound = 0;
1602
0
    for (j = 0; j < p->numlines; j++) { /* for each line */
1603
1604
0
      point1 = &(p->line[j].point[p->line[j].numpoints - 1]);
1605
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1606
0
        point2 = &(p->line[j].point[i]);
1607
1608
0
        if (EDGE_CHECK(point1->y, y, point2->y) == CLIP_MIDDLE) {
1609
1610
0
          if (point1->y == point2->y)
1611
0
            continue; /* ignore horizontal edges */
1612
0
          else
1613
0
            slope = (point2->x - point1->x) / (point2->y - point1->y);
1614
1615
0
          x = point1->x + (y - point1->y) * slope;
1616
0
          intersect[nfound++] = x;
1617
0
        } /* end checking this edge */
1618
1619
0
        point1 = point2; /* next edge */
1620
0
      }
1621
0
    } /* finished line */
1622
1623
    /* sort the intersections */
1624
0
    do {
1625
0
      wrong_order = 0;
1626
0
      for (i = 0; i < nfound - 1; i++) {
1627
0
        if (intersect[i] > intersect[i + 1]) {
1628
0
          wrong_order = 1;
1629
0
          SWAP(intersect[i], intersect[i + 1], temp);
1630
0
        }
1631
0
      }
1632
0
    } while (wrong_order);
1633
1634
    /* find longest span */
1635
0
    for (i = 0; i < nfound; i += 2) {
1636
0
      len = fabs(intersect[i] - intersect[i + 1]);
1637
0
      if (len > max_len) {
1638
0
        max_len = len;
1639
0
        lp->x = (intersect[i] + intersect[i + 1]) / 2;
1640
0
        lp->y = y;
1641
0
      }
1642
0
    }
1643
0
  } else { /* center vertically, fix x */
1644
0
    x = lp->x;
1645
1646
    /* need to find a x that won't intersect any vertices exactly */
1647
0
    max = x - 1; /* first initializing min, max to be any 2 pnts on either side
1648
                    of x */
1649
0
    min = x + 1;
1650
0
    for (j = 0; j < p->numlines; j++) {
1651
0
      if ((min < x) && (max >= x))
1652
0
        break;
1653
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1654
0
        if ((min < x) && (max >= x))
1655
0
          break;
1656
0
        if (p->line[j].point[i].x < x)
1657
0
          min = p->line[j].point[i].x;
1658
0
        if (p->line[j].point[i].x >= x)
1659
0
          max = p->line[j].point[i].x;
1660
0
      }
1661
0
    }
1662
1663
0
    n = 0;
1664
0
    for (j = 0; j < p->numlines; j++) {
1665
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1666
0
        if ((p->line[j].point[i].x < x) &&
1667
0
            ((x - p->line[j].point[i].x) < (x - min)))
1668
0
          min = p->line[j].point[i].x;
1669
0
        if ((p->line[j].point[i].x >= x) &&
1670
0
            ((p->line[j].point[i].x - x) < (max - x)))
1671
0
          max = p->line[j].point[i].x;
1672
0
      }
1673
0
    }
1674
1675
0
    if (min == max) {
1676
0
      msFree(intersect);
1677
0
      return (MS_FAILURE);
1678
0
    } else
1679
0
      x = (max + min) / 2.0;
1680
1681
0
    nfound = 0;
1682
0
    for (j = 0; j < p->numlines; j++) { /* for each line */
1683
1684
0
      point1 = &(p->line[j].point[p->line[j].numpoints - 1]);
1685
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1686
0
        point2 = &(p->line[j].point[i]);
1687
1688
0
        if (EDGE_CHECK(point1->x, x, point2->x) == CLIP_MIDDLE) {
1689
1690
0
          if (point1->x == point2->x)
1691
0
            continue; /* ignore vertical edges */
1692
0
          else if (point1->y == point2->y)
1693
0
            y = point1->y; /* for a horizontal edge we know y */
1694
0
          else {
1695
0
            slope = (point2->x - point1->x) / (point2->y - point1->y);
1696
0
            y = (x - point1->x) / slope + point1->y;
1697
0
          }
1698
1699
0
          intersect[nfound++] = y;
1700
0
        } /* end checking this edge */
1701
1702
0
        point1 = point2; /* next edge */
1703
0
      }
1704
0
    } /* finished line */
1705
1706
    /* sort the intersections */
1707
0
    do {
1708
0
      wrong_order = 0;
1709
0
      for (i = 0; i < nfound - 1; i++) {
1710
0
        if (intersect[i] > intersect[i + 1]) {
1711
0
          wrong_order = 1;
1712
0
          SWAP(intersect[i], intersect[i + 1], temp);
1713
0
        }
1714
0
      }
1715
0
    } while (wrong_order);
1716
1717
    /* find longest span */
1718
0
    for (i = 0; i < nfound; i += 2) {
1719
0
      len = fabs(intersect[i] - intersect[i + 1]);
1720
0
      if (len > max_len) {
1721
0
        max_len = len;
1722
0
        lp->y = (intersect[i] + intersect[i + 1]) / 2;
1723
0
        lp->x = x;
1724
0
      }
1725
0
    }
1726
0
  }
1727
1728
0
  free(intersect);
1729
1730
0
  if (max_len > 0)
1731
0
    return (MS_SUCCESS);
1732
0
  else
1733
0
    return (MS_FAILURE);
1734
0
}
1735
1736
/* Compute all the lineString/segment lengths and determine the longest
1737
 * lineString of a multiLineString shape: in parameter, the multiLineString to
1738
 * compute. struct polyline_lengths pll: out parameter, all line and segment
1739
 * lengths
1740
 */
1741
void msPolylineComputeLineSegments(shapeObj *shape,
1742
0
                                   struct polyline_lengths *pll) {
1743
0
  int i, j;
1744
0
  double max_line_length = -1, max_segment_length = -1, segment_length;
1745
1746
0
  pll->ll = (struct line_lengths *)msSmallMalloc(shape->numlines *
1747
0
                                                 sizeof(struct line_lengths));
1748
0
  pll->total_length = 0;
1749
0
  pll->longest_line_index = 0;
1750
1751
0
  for (i = 0; i < shape->numlines; i++) {
1752
0
    struct line_lengths *ll = &pll->ll[i];
1753
0
    double max_subline_segment_length = -1;
1754
1755
0
    if (shape->line[i].numpoints > 1) {
1756
0
      ll->segment_lengths = (double *)msSmallMalloc(
1757
0
          sizeof(double) * (shape->line[i].numpoints - 1));
1758
0
    } else {
1759
0
      ll->segment_lengths = NULL;
1760
0
    }
1761
0
    ll->total_length = 0;
1762
1763
0
    for (j = 1; j < shape->line[i].numpoints; j++) {
1764
0
      segment_length =
1765
0
          sqrt((((shape->line[i].point[j].x - shape->line[i].point[j - 1].x) *
1766
0
                 (shape->line[i].point[j].x - shape->line[i].point[j - 1].x)) +
1767
0
                ((shape->line[i].point[j].y - shape->line[i].point[j - 1].y) *
1768
0
                 (shape->line[i].point[j].y - shape->line[i].point[j - 1].y))));
1769
0
      ll->total_length += segment_length;
1770
0
      ll->segment_lengths[j - 1] = segment_length;
1771
0
      if (segment_length > max_subline_segment_length) {
1772
0
        max_subline_segment_length = segment_length;
1773
0
        ll->longest_segment_index = j;
1774
0
      }
1775
0
      if (segment_length > max_segment_length) {
1776
0
        max_segment_length = segment_length;
1777
0
        pll->longest_segment_line_index = i;
1778
0
        pll->longest_segment_point_index = j;
1779
0
      }
1780
0
    }
1781
0
    pll->total_length += ll->total_length;
1782
1783
0
    if (ll->total_length > max_line_length) {
1784
0
      max_line_length = ll->total_length;
1785
0
      pll->longest_line_index = i;
1786
0
    }
1787
0
  }
1788
0
}
1789
1790
/*
1791
** If no repeatdistance, find center of longest segment in polyline p. The
1792
*polyline must have been converted
1793
** to image coordinates before calling this function.
1794
*/
1795
int msPolylineLabelPoint(mapObj *map, shapeObj *p, textSymbolObj *ts,
1796
                         labelObj *label, struct label_auto_result *lar,
1797
0
                         double resolutionfactor) {
1798
0
  struct polyline_lengths pll;
1799
0
  int i, ret = MS_SUCCESS;
1800
0
  double minfeaturesize = -1;
1801
0
  assert(ts == NULL || ts->annotext);
1802
1803
0
  if (label && ts) {
1804
0
    if (label->autominfeaturesize) {
1805
0
      if (!ts->textpath) {
1806
0
        if (MS_UNLIKELY(MS_FAILURE == msComputeTextPath(map, ts)))
1807
0
          return MS_FAILURE;
1808
0
      }
1809
0
      if (!ts->textpath)
1810
0
        return MS_FAILURE;
1811
0
      minfeaturesize = ts->textpath->bounds.bbox.maxx;
1812
0
    } else if (label->minfeaturesize) {
1813
0
      minfeaturesize = label->minfeaturesize * resolutionfactor;
1814
0
    }
1815
0
  }
1816
1817
  /* compute line lengths, in order to:
1818
    - extract the longest line if we're not repeating
1819
    - check that each line is longer than the text length if using
1820
    minfeaturesize auto
1821
    - check that each line is long enough if using minfeaturesize
1822
   */
1823
0
  msPolylineComputeLineSegments(p, &pll);
1824
1825
0
  if (label && label->repeatdistance > 0) {
1826
0
    for (i = 0; i < p->numlines; i++) {
1827
0
      if (pll.ll[i].total_length > minfeaturesize) {
1828
0
        ret = msLineLabelPoint(map, &p->line[i], ts, &pll.ll[i], lar, label,
1829
0
                               resolutionfactor);
1830
0
      }
1831
0
    }
1832
0
  } else {
1833
0
    i = pll.longest_line_index;
1834
0
    if (pll.ll[i].total_length > minfeaturesize) {
1835
0
      ret = msLineLabelPoint(map, &p->line[i], ts, &pll.ll[i], lar, label,
1836
0
                             resolutionfactor);
1837
0
    }
1838
0
  }
1839
1840
  /* freeing memory: allocated by msPolylineComputeLineSegments */
1841
0
  for (i = 0; i < p->numlines; i++) {
1842
0
    free(pll.ll[i].segment_lengths);
1843
0
  }
1844
0
  free(pll.ll);
1845
1846
0
  return ret;
1847
0
}
1848
1849
int msLineLabelPoint(mapObj *map, lineObj *p, textSymbolObj *ts,
1850
                     struct line_lengths *ll, struct label_auto_result *lar,
1851
0
                     labelObj *label, double resolutionfactor) {
1852
0
  (void)map;
1853
0
  int j, l, n, point_repeat;
1854
0
  double t, theta, fwd_length, point_distance;
1855
0
  double center_point_position, left_point_position, right_point_position,
1856
0
      point_position;
1857
0
  double repeat_distance = -1;
1858
0
  if (label) {
1859
0
    repeat_distance = label->repeatdistance * resolutionfactor;
1860
0
  }
1861
1862
0
  if (MS_UNLIKELY(p->numpoints < 2))
1863
0
    return MS_FAILURE;
1864
0
  point_distance = 0;
1865
0
  point_repeat = 1;
1866
0
  left_point_position = right_point_position = center_point_position =
1867
0
      ll->total_length / 2.0;
1868
1869
0
  if (repeat_distance > 0) {
1870
0
    point_repeat = ll->total_length / repeat_distance;
1871
1872
0
    if (point_repeat > 1) {
1873
0
      if (point_repeat % 2 == 0)
1874
0
        point_repeat -= 1;
1875
0
      point_distance =
1876
0
          (ll->total_length / point_repeat); /* buffer allowed per point */
1877
1878
      /* initial point position */
1879
0
      left_point_position -= ((point_repeat - 1) / 2 * point_distance);
1880
0
      right_point_position += ((point_repeat - 1) / 2 * point_distance);
1881
1882
0
      point_repeat = (point_repeat - 1) / 2 + 1;
1883
0
    } else
1884
0
      point_repeat = 1;
1885
0
  }
1886
1887
0
  for (l = 0; l < point_repeat; ++l) {
1888
0
    if (l ==
1889
0
        point_repeat - 1) { /* last point to place is always the center point */
1890
0
      point_position = center_point_position;
1891
0
      n = 1;
1892
0
    } else {
1893
0
      point_position = right_point_position;
1894
0
      n = 0;
1895
0
    }
1896
1897
0
    do {
1898
0
      lar->angles = (double *)msSmallRealloc(
1899
0
          lar->angles, (lar->num_label_points + 1) * sizeof(double));
1900
0
      lar->label_points = (pointObj *)msSmallRealloc(
1901
0
          lar->label_points, (lar->num_label_points + 1) * sizeof(pointObj));
1902
1903
      /* if there is only 1 label to place... put it in the middle of the
1904
       * current segment (as old behavior) */
1905
0
      if (point_repeat == 1) {
1906
0
        j = ll->longest_segment_index;
1907
0
        lar->label_points[lar->num_label_points].x =
1908
0
            (p->point[j - 1].x + p->point[j].x) / 2.0;
1909
0
        lar->label_points[lar->num_label_points].y =
1910
0
            (p->point[j - 1].y + p->point[j].y) / 2.0;
1911
0
      } else {
1912
0
        j = 0;
1913
0
        fwd_length = 0;
1914
0
        while (fwd_length < point_position) {
1915
0
          fwd_length += ll->segment_lengths[j++];
1916
0
        }
1917
0
        assert(j > 0);
1918
1919
0
        t = 1 - (fwd_length - point_position) / ll->segment_lengths[j - 1];
1920
0
        lar->label_points[lar->num_label_points].x =
1921
0
            t * (p->point[j].x - p->point[j - 1].x) + p->point[j - 1].x;
1922
0
        lar->label_points[lar->num_label_points].y =
1923
0
            t * (p->point[j].y - p->point[j - 1].y) + p->point[j - 1].y;
1924
0
      }
1925
1926
0
      if (label && ts) {
1927
0
        if (label->anglemode != MS_NONE) {
1928
0
          theta = atan2(p->point[j].x - p->point[j - 1].x,
1929
0
                        p->point[j].y - p->point[j - 1].y);
1930
0
          if (label->anglemode == MS_AUTO2) {
1931
0
            theta -= MS_PI2;
1932
0
          } else {                                   /* AUTO, FOLLOW */
1933
0
            if (p->point[j - 1].x < p->point[j].x) { /* i.e. to the left */
1934
0
              theta -= MS_PI2;
1935
0
            } else {
1936
0
              theta += MS_PI2;
1937
0
            }
1938
0
          }
1939
0
          lar->angles[lar->num_label_points] = theta;
1940
0
        } else {
1941
0
          lar->angles[lar->num_label_points] = MS_DEG_TO_RAD * ts->label->angle;
1942
0
        }
1943
0
      } else {
1944
0
        lar->angles[lar->num_label_points] = 0;
1945
0
      }
1946
0
      lar->num_label_points++;
1947
1948
0
      point_position = left_point_position;
1949
0
      n++;
1950
0
    } while (n < 2); /* we place the right point then the left point. */
1951
1952
0
    right_point_position -= point_distance;
1953
0
    left_point_position += point_distance;
1954
0
  }
1955
1956
0
  return MS_SUCCESS;
1957
0
}
1958
1959
/* Calculate the labelpath for each line if repeatdistance is enabled, else the
1960
 * labelpath of the longest line segment */
1961
int msPolylineLabelPath(mapObj *map, imageObj *image, shapeObj *p,
1962
                        textSymbolObj *ts, labelObj *label,
1963
0
                        struct label_follow_result *lfr) {
1964
0
  struct polyline_lengths pll;
1965
0
  int i, ret = MS_SUCCESS;
1966
0
  double minfeaturesize = -1;
1967
0
  assert(ts->annotext);
1968
0
  lfr->num_follow_labels = lfr->lar.num_label_points = 0;
1969
1970
  /* we first offset the line if we want an offsetted label */
1971
0
  if (label->offsetx != 0 && IS_PERPENDICULAR_OFFSET(label->offsety)) {
1972
0
    double offset;
1973
0
    if (label->offsetx > 0) {
1974
0
      offset = label->offsetx + label->size / 2;
1975
0
    } else {
1976
0
      offset = label->offsetx - label->size / 2;
1977
0
    }
1978
0
    if (label->offsety == MS_LABEL_PERPENDICULAR_TOP_OFFSET &&
1979
0
        p->numlines > 0 && p->line[0].numpoints > 0) {
1980
      /* is the line mostly left-to-right or right-to-left ?
1981
       * FIXME this should be done line by line, by stepping through
1982
       * shape->lines, however the OffsetPolyline function works on shapeObjs,
1983
       * not lineObjs we only check the first line
1984
       */
1985
0
      if (p->line[0].point[0].x <
1986
0
          p->line[0].point[p->line[0].numpoints - 1].x) {
1987
        /* line is left to right */
1988
0
        offset = -offset;
1989
0
      }
1990
0
    }
1991
0
    p = msOffsetPolyline(p, offset, MS_STYLE_SINGLE_SIDED_OFFSET);
1992
0
    if (!p)
1993
0
      return MS_FAILURE;
1994
0
  }
1995
1996
  /* compute line lengths, in order to:
1997
    - extract the longest line if we're not repeating
1998
    - check that each line is longer than the text length if using
1999
    minfeaturesize auto
2000
    - check that each line is long enough if using minfeaturesize
2001
   */
2002
0
  msPolylineComputeLineSegments(p, &pll);
2003
2004
0
  if (label->autominfeaturesize) {
2005
0
    if (!ts->textpath) {
2006
0
      if (MS_UNLIKELY(MS_FAILURE == msComputeTextPath(map, ts))) {
2007
0
        return MS_FAILURE;
2008
0
      }
2009
0
    }
2010
0
    if (!ts->textpath)
2011
0
      return MS_FAILURE;
2012
0
    minfeaturesize = ts->textpath->bounds.bbox.maxx;
2013
0
  } else if (label->minfeaturesize) {
2014
0
    minfeaturesize = label->minfeaturesize * image->resolutionfactor;
2015
0
  }
2016
2017
0
  if (label->repeatdistance > 0) {
2018
0
    for (i = 0; i < p->numlines; i++) {
2019
0
      if (pll.ll[i].total_length > minfeaturesize) {
2020
0
        ret = msLineLabelPath(map, image, &p->line[i], ts, &pll.ll[i], lfr,
2021
0
                              label);
2022
0
      }
2023
0
    }
2024
0
  } else {
2025
0
    i = pll.longest_line_index;
2026
0
    if (pll.ll[i].total_length > minfeaturesize) {
2027
0
      ret =
2028
0
          msLineLabelPath(map, image, &p->line[i], ts, &pll.ll[i], lfr, label);
2029
0
    }
2030
0
  }
2031
2032
  /* freeing memory: allocated by msPolylineComputeLineSegments */
2033
0
  for (i = 0; i < p->numlines; i++) {
2034
0
    free(pll.ll[i].segment_lengths);
2035
0
  }
2036
0
  free(pll.ll);
2037
2038
0
  if (IS_PERPENDICULAR_OFFSET(label->offsety) && label->offsetx != 0) {
2039
0
    msFreeShape(p);
2040
0
    msFree(p);
2041
0
  }
2042
0
  return ret;
2043
0
}
2044
2045
static double compute_retry_offset(textSymbolObj *ts, int fail_idx,
2046
                                   double retried_offset, double max_dec_offset,
2047
0
                                   double max_inc_offset) {
2048
  /* fail_idx: the glyph index in the textpath that originally failed (i.e.
2049
   * before any displacement was tested */
2050
  /* retried_offset: the last offset that was tried and is failing, so we can
2051
   * return the next one (or 0 if none left)*/
2052
0
  int inc = 1, dec_found = 0, inc_found = 0;
2053
0
  double inc_offset = 0, dec_offset = 0;
2054
2055
0
  retried_offset = fabs(retried_offset);
2056
0
  do {
2057
0
    if (fail_idx - inc - 1 >= 0) {
2058
0
      dec_offset += ts->textpath->glyphs[fail_idx - inc].glyph->metrics.advance;
2059
0
      if (dec_offset > max_dec_offset)
2060
0
        break;
2061
0
      if (dec_offset > retried_offset &&
2062
0
          msIsGlyphASpace(&ts->textpath->glyphs[fail_idx - inc - 1])) {
2063
0
        dec_found = 1;
2064
0
        dec_offset +=
2065
0
            ts->textpath->glyphs[fail_idx - inc - 1].glyph->metrics.advance /
2066
0
            2.0;
2067
0
        break;
2068
0
      }
2069
0
    }
2070
0
    inc++;
2071
0
  } while (dec_offset < max_dec_offset && fail_idx - inc > 0);
2072
2073
0
  if (!dec_found) {
2074
    /* try the starting position */
2075
0
    dec_offset += ts->textpath->glyphs[0].glyph->metrics.advance;
2076
0
    if (dec_offset > retried_offset && dec_offset < max_dec_offset) {
2077
0
      dec_found = 1;
2078
0
    }
2079
0
  }
2080
2081
0
  inc = 1;
2082
0
  inc_offset = ts->textpath->glyphs[fail_idx].glyph->metrics.advance;
2083
0
  do {
2084
0
    if (fail_idx + inc < ts->textpath->numglyphs - 1) {
2085
0
      if (inc_offset > retried_offset &&
2086
0
          msIsGlyphASpace(&ts->textpath->glyphs[fail_idx + inc])) {
2087
0
        inc_offset +=
2088
0
            ts->textpath->glyphs[fail_idx + inc].glyph->metrics.advance / 2;
2089
0
        if (inc_offset < max_inc_offset)
2090
0
          inc_found = 1;
2091
0
        break;
2092
0
      }
2093
0
      inc_offset += ts->textpath->glyphs[fail_idx + inc].glyph->metrics.advance;
2094
0
    }
2095
0
    inc++;
2096
0
  } while (inc_offset < max_inc_offset &&
2097
0
           fail_idx + inc < ts->textpath->numglyphs - 1);
2098
0
  if (!inc_found) {
2099
0
    inc_offset += ts->textpath->glyphs[ts->textpath->numglyphs - 1]
2100
0
                      .glyph->metrics.advance;
2101
0
    if (inc_offset > retried_offset && inc_offset < max_inc_offset) {
2102
0
      inc_found = 1;
2103
0
    }
2104
0
  }
2105
2106
0
  if (!inc_found && !dec_found)
2107
0
    return 0.0;
2108
0
  if (inc_found && dec_found) {
2109
0
    if (inc_offset < dec_offset)
2110
0
      return -inc_offset;
2111
0
    else
2112
0
      return dec_offset;
2113
0
  }
2114
0
  if (inc_found)
2115
0
    return -inc_offset;
2116
0
  else
2117
0
    return dec_offset;
2118
0
}
2119
/*
2120
 * Calculate a series of label points for each character in the label for a
2121
 * given line. The resultant series of points is stored in *labelpath,
2122
 * which is added to the labelpaths array. Note that the points and bounds
2123
 * are allocated in this function.  The polyline must be converted to image
2124
 * coordinates before calling this function.
2125
 */
2126
int msLineLabelPath(mapObj *map, imageObj *img, lineObj *p, textSymbolObj *ts,
2127
                    struct line_lengths *ll, struct label_follow_result *lfr,
2128
0
                    labelObj *label) {
2129
0
  double distance_along_segment;
2130
0
  double repeat_distance = label->repeatdistance * ts->resolutionfactor;
2131
0
  double segment_length, fwd_line_length, rev_line_length, text_length,
2132
0
      text_start_length, label_buffer, text_end_length, cur_label_position,
2133
0
      first_label_position;
2134
0
  double max_retry_offset;
2135
2136
0
  int j, k, l, inc, final_j, label_repeat;
2137
0
  double direction;
2138
0
  rectObj bbox;
2139
0
  lineObj *bounds;
2140
0
  double t;
2141
0
  double cx, cy; /* centre of a character, x & y values. */
2142
2143
0
  double theta;
2144
0
  double dx, dy, w, cos_t, sin_t;
2145
2146
0
  const double letterspacing = 1.00;
2147
  /* As per RFC 60, if label->maxoverlapangle == 0 then fall back on pre-6.0
2148
     behavior which was to use maxoverlapangle = 0.4*MS_PI ( 40% of 180 degrees
2149
     ) */
2150
0
  double maxoverlapangle = 0.4 * MS_PI;
2151
2152
0
  if (p->numpoints < 2) { /* degenerate */
2153
0
    return MS_FAILURE;
2154
0
  }
2155
0
  if (p->numpoints == 2) /* use the regular angled text algorithm */
2156
0
    return msLineLabelPoint(map, p, ts, ll, &lfr->lar, label,
2157
0
                            img->resolutionfactor);
2158
2159
0
  if (!ts->textpath) {
2160
0
    if (MS_UNLIKELY(MS_FAILURE == msComputeTextPath(map, ts))) {
2161
0
      return MS_FAILURE;
2162
0
    }
2163
0
  }
2164
0
  if (!ts->textpath) {
2165
0
    return MS_FAILURE;
2166
0
  }
2167
2168
  /* skip the label and use the normal algorithm if it has fewer than 2
2169
   * characters */
2170
0
  if (ts->textpath->numglyphs < 2)
2171
0
    return msLineLabelPoint(map, p, ts, ll, &lfr->lar, label,
2172
0
                            img->resolutionfactor);
2173
2174
0
  text_length = letterspacing * ts->textpath->bounds.bbox.maxx;
2175
2176
  /*
2177
  ** if the text length is way longer than the line, skip adding the
2178
  ** label if it isn't forced (long extrapolated labels tend to be ugly)
2179
  */
2180
0
  if (text_length > 1.5 * ll->total_length) {
2181
0
    if (ts->label->force == MS_FALSE) {
2182
0
      return MS_SUCCESS;
2183
0
    } else {
2184
0
      repeat_distance = 0; /* disable repetition */
2185
0
    }
2186
0
  }
2187
2188
  /* We compute the number of labels we can repeat in the line:
2189
     - space occupied by one label
2190
     - plus N times space occupied by one label + repeat_distance interspacing
2191
   */
2192
0
  label_buffer =
2193
0
      text_length + repeat_distance; /* distance occupied by one label +
2194
                                        inter-repeat spacing */
2195
0
  if (repeat_distance > 0 && ll->total_length > text_length) {
2196
0
    label_repeat = 1 + (int)((ll->total_length - text_length) / label_buffer);
2197
0
    max_retry_offset = repeat_distance / 2.1;
2198
0
  } else {
2199
0
    label_repeat = 1;
2200
0
    max_retry_offset = (ll->total_length - text_length) / 2.1;
2201
0
  }
2202
  /* compute the starting point of where we'll be placing the first label */
2203
0
  if (label_repeat % 2) {
2204
    /* odd number of labels: account for the center label that will be centered
2205
     * on the line
2206
     * ----llll_____llll_____llll----
2207
     */
2208
0
    first_label_position = (ll->total_length - text_length) / 2.0 -
2209
0
                           (label_repeat / 2) * label_buffer;
2210
0
  } else {
2211
    /* even number of labels:
2212
     * repeat_distance is centered on the line
2213
     * space for one label
2214
     * eventually space for label_repeat*2-1 labels+spacing
2215
     * ----llll_____llll----
2216
     * --llll_____llll_____llll_____llll--
2217
     */
2218
0
    first_label_position = (ll->total_length - repeat_distance) / 2.0 -
2219
0
                           text_length - (label_repeat / 2 - 1) * label_buffer;
2220
0
  }
2221
0
  if (label->maxoverlapangle > 0)
2222
0
    maxoverlapangle = label->maxoverlapangle * MS_DEG_TO_RAD; /* radian */
2223
2224
0
  cur_label_position = first_label_position;
2225
0
  for (l = 0; l < label_repeat; l++) {
2226
2227
0
    double retry_offset = 0.0;
2228
0
    int first_retry_idx = 0;
2229
    // max_dec_retry_offset = max_inc_retry_offset = max_retry_offset;
2230
0
    textPathObj *tp = (textPathObj *)msSmallCalloc(1, sizeof(textPathObj));
2231
    /* copy the textPath, we will be overriding the copy's positions and angles
2232
     */
2233
0
    msCopyTextPath(tp, ts->textpath);
2234
0
    tp->bounds.poly = (lineObj *)msSmallMalloc(sizeof(lineObj));
2235
0
    bounds = tp->bounds.poly;
2236
2237
    /*
2238
    ** The bounds will have two points for each character plus an endpoint:
2239
    ** the UL corners of each bbox will be tied together and the LL corners
2240
    ** will be tied together.
2241
    */
2242
0
    bounds->numpoints = 2 * tp->numglyphs + 1;
2243
0
    bounds->point =
2244
0
        (pointObj *)msSmallMalloc(sizeof(pointObj) * bounds->numpoints);
2245
2246
0
    do {
2247
0
      int overlap_collision_detected = 0;
2248
0
      text_start_length = cur_label_position + retry_offset;
2249
0
      text_end_length = 0;
2250
2251
0
      for (k = 0; k < tp->numglyphs; k++) {
2252
0
        tp->glyphs[k].pnt.x = 0;
2253
0
        tp->glyphs[k].pnt.y = 0;
2254
0
      }
2255
2256
      /* the points start at (line_length - text_length) / 2 in order to be
2257
       * centred */
2258
      /* text_start_length = (line_length - text_length) / 2.0; */
2259
2260
0
      j = 0;
2261
0
      fwd_line_length = 0;
2262
0
      if (text_start_length >= 0.0) {
2263
0
        while (fwd_line_length < text_start_length)
2264
0
          fwd_line_length += ll->segment_lengths[j++];
2265
2266
0
        j--;
2267
0
      }
2268
0
      final_j = p->numpoints - 1;
2269
0
      rev_line_length = 0;
2270
0
      if (text_start_length + text_length <= ll->total_length) {
2271
0
        text_end_length = ll->total_length - (text_start_length + text_length);
2272
0
        while (rev_line_length < text_end_length) {
2273
0
          rev_line_length += ll->segment_lengths[final_j - 1];
2274
0
          final_j--;
2275
0
        }
2276
0
        final_j++;
2277
0
      }
2278
2279
0
      if (final_j == 0)
2280
0
        final_j = 1;
2281
2282
      /* determine if the line is mostly left to right or right to left,
2283
       see bug 1620 discussion by Steve Woodbridge */
2284
0
      direction = p->point[final_j].x - p->point[j].x;
2285
2286
0
      if (direction > 0) {
2287
0
        inc = 1; /* j is already correct */
2288
2289
        /* length of the segment containing the starting point */
2290
0
        segment_length = ll->segment_lengths[j];
2291
2292
        /* determine how far along the segment we need to go */
2293
0
        if (text_start_length < 0.0)
2294
0
          t = text_start_length / segment_length;
2295
0
        else
2296
0
          t = 1 - (fwd_line_length - text_start_length) / segment_length;
2297
0
      } else {
2298
0
        j = final_j;
2299
0
        inc = -1;
2300
2301
        /* length of the segment containing the starting point */
2302
0
        segment_length = ll->segment_lengths[j - 1];
2303
0
        if (text_start_length < 0.0)
2304
0
          t = text_start_length / segment_length;
2305
0
        else
2306
0
          t = 1 - (rev_line_length - text_end_length) / segment_length;
2307
0
      }
2308
2309
0
      distance_along_segment = t * segment_length; /* starting point */
2310
2311
0
      theta = 0;
2312
0
      k = 0;
2313
0
      w = 0;
2314
0
      while (k < tp->numglyphs) {
2315
0
        double x, y;
2316
2317
0
        x = t * (p->point[j + inc].x - p->point[j].x) + p->point[j].x;
2318
0
        y = t * (p->point[j + inc].y - p->point[j].y) + p->point[j].y;
2319
0
        tp->glyphs[k].pnt.x = x;
2320
0
        tp->glyphs[k].pnt.y = y;
2321
2322
0
        w = letterspacing * tp->glyphs[k].glyph->metrics.advance;
2323
2324
        /* add the character's width to the distance along the line */
2325
0
        distance_along_segment += w;
2326
2327
        /* if we still have segments left and we've past the current segment,
2328
         * move to the next one */
2329
0
        if (inc == 1 && j < p->numpoints - 2) {
2330
2331
0
          while (j < p->numpoints - 2 &&
2332
0
                 distance_along_segment > ll->segment_lengths[j]) {
2333
0
            distance_along_segment -= ll->segment_lengths[j];
2334
0
            j += inc; /* move to next segment */
2335
0
          }
2336
2337
0
          segment_length = ll->segment_lengths[j];
2338
2339
0
        } else if (inc == -1 && j > 1) {
2340
2341
0
          while (j > 1 && distance_along_segment > ll->segment_lengths[j - 1]) {
2342
0
            distance_along_segment -= ll->segment_lengths[j - 1];
2343
0
            j += inc; /* move to next segment */
2344
0
          }
2345
2346
0
          segment_length = ll->segment_lengths[j - 1];
2347
0
        }
2348
2349
        /* Recalculate interpolation parameter */
2350
0
        t = distance_along_segment / segment_length;
2351
2352
0
        k++;
2353
0
      }
2354
2355
      /* pre-calc the character's centre y value.  Used for rotation adjustment.
2356
       */
2357
0
      cy = -ts->textpath->glyph_size / 2.0;
2358
#ifdef USE_KERNEL
2359
      tp->glyphs[0].pnt.x /= kernel_normal;
2360
      tp->glyphs[0].pnt.y /= kernel_normal;
2361
#endif
2362
2363
      /* Average the points and calculate each angle */
2364
0
      overlap_collision_detected = 0;
2365
0
      for (k = 1; k <= tp->numglyphs; k++) {
2366
0
        double anglediff;
2367
0
        if (k < tp->numglyphs) {
2368
#ifdef USE_KERNEL
2369
          tp->glyphs[k].pnt.x /= kernel_normal;
2370
          tp->glyphs[k].pnt.y /= kernel_normal;
2371
#endif
2372
0
          dx = tp->glyphs[k].pnt.x - tp->glyphs[k - 1].pnt.x;
2373
0
          dy = tp->glyphs[k].pnt.y - tp->glyphs[k - 1].pnt.y;
2374
0
        } else {
2375
          /* Handle the last character */
2376
0
          dx = t * (p->point[j + inc].x - p->point[j].x) + p->point[j].x -
2377
0
               tp->glyphs[k - 1].pnt.x;
2378
0
          dy = t * (p->point[j + inc].y - p->point[j].y) + p->point[j].y -
2379
0
               tp->glyphs[k - 1].pnt.y;
2380
0
        }
2381
2382
0
        if (dx || dy || k == 1) {
2383
2384
0
          theta = -atan2(dy, dx);
2385
2386
0
          if (maxoverlapangle > 0 && k > 1) {
2387
            /* no use testing for overlap if glyph or previous glyph is a space
2388
             */
2389
0
            if (!msIsGlyphASpace(&tp->glyphs[k - 1]) &&
2390
0
                !msIsGlyphASpace(&tp->glyphs[k - 2])) {
2391
              /* If the difference between the last char angle and the current
2392
               one is greater than the MAXOVERLAPANGLE value (set at 80% of
2393
               180deg by default) , bail the label */
2394
0
              anglediff = fabs(theta - tp->glyphs[k - 2].rot);
2395
0
              anglediff = MS_MIN(anglediff, MS_2PI - anglediff);
2396
0
              if (anglediff > maxoverlapangle) {
2397
0
                double max_dec_retry_offset;
2398
0
                double max_inc_retry_offset;
2399
0
                if (label_repeat == 1) {
2400
0
                  max_dec_retry_offset = max_inc_retry_offset =
2401
0
                      first_label_position;
2402
0
                } else if (l == 0) {
2403
0
                  if (direction > 0) {
2404
0
                    max_inc_retry_offset =
2405
0
                        MS_MIN(first_label_position, max_retry_offset);
2406
0
                    max_dec_retry_offset = max_retry_offset;
2407
0
                  } else {
2408
0
                    max_inc_retry_offset = max_retry_offset;
2409
0
                    max_dec_retry_offset =
2410
0
                        MS_MIN(first_label_position, max_retry_offset);
2411
0
                  }
2412
0
                } else if (l == label_repeat - 1) {
2413
0
                  if (direction > 0) {
2414
0
                    max_dec_retry_offset =
2415
0
                        MS_MIN(first_label_position, max_retry_offset);
2416
0
                    max_inc_retry_offset = max_retry_offset;
2417
0
                  } else {
2418
0
                    max_dec_retry_offset = max_retry_offset;
2419
0
                    max_inc_retry_offset =
2420
0
                        MS_MIN(first_label_position, max_retry_offset);
2421
0
                  }
2422
0
                } else {
2423
0
                  max_dec_retry_offset = max_inc_retry_offset =
2424
0
                      max_retry_offset;
2425
0
                }
2426
2427
0
                overlap_collision_detected = 1;
2428
0
                if (retry_offset == 0.0) {
2429
0
                  first_retry_idx = k - 1;
2430
0
                }
2431
0
                retry_offset = compute_retry_offset(
2432
0
                    ts, first_retry_idx, retry_offset, max_dec_retry_offset,
2433
0
                    max_inc_retry_offset);
2434
0
                if (retry_offset != 0.0) { /* offsetted position to try */
2435
0
                  if (direction < 0) {
2436
0
                    retry_offset = -retry_offset;
2437
0
                  }
2438
0
                }
2439
0
                break;
2440
0
              }
2441
0
            }
2442
0
          }
2443
0
        } else {
2444
          /*
2445
           * handle the 0-advance case, as the complex text shaping may have
2446
           * placed multiple glyphs at the same position
2447
           */
2448
0
          theta = tp->glyphs[k - 2].rot;
2449
0
        }
2450
2451
        /* msDebug("s: %c (x,y): (%0.2f,%0.2f) t: %0.2f\n", string[k-1],
2452
         * labelpath->path.point[k-1].x, labelpath->path.point[k-1].y, theta);
2453
         */
2454
2455
0
        tp->glyphs[k - 1].rot = theta;
2456
2457
        /* Move the previous point so that when the character is rotated and
2458
         placed it is centred on the line */
2459
0
        cos_t = cos(theta);
2460
0
        sin_t = sin(theta);
2461
2462
0
        w = letterspacing * tp->glyphs[k - 1].glyph->metrics.advance;
2463
2464
0
        cx = 0; /* Center the character vertically only */
2465
2466
0
        dx = -(cx * cos_t + cy * sin_t);
2467
0
        dy = -(cy * cos_t - cx * sin_t);
2468
2469
0
        tp->glyphs[k - 1].pnt.x += dx;
2470
0
        tp->glyphs[k - 1].pnt.y += dy;
2471
2472
        /* Calculate the bounds */
2473
0
        bbox.minx = 0;
2474
0
        bbox.maxx = w;
2475
0
        bbox.maxy = 0;
2476
0
        bbox.miny = -ts->textpath->glyph_size;
2477
2478
        /* Add the label buffer to the bounds */
2479
0
        bbox.maxx += ts->label->buffer * ts->resolutionfactor;
2480
0
        bbox.maxy += ts->label->buffer * ts->resolutionfactor;
2481
0
        bbox.minx -= ts->label->buffer * ts->resolutionfactor;
2482
0
        bbox.miny -= ts->label->buffer * ts->resolutionfactor;
2483
2484
0
        if (k < tp->numglyphs) {
2485
          /* Transform the bbox too.  We take the UL and LL corners and rotate
2486
           then translate them. */
2487
0
          bounds->point[k - 1].x =
2488
0
              (bbox.minx * cos_t + bbox.maxy * sin_t) + tp->glyphs[k - 1].pnt.x;
2489
0
          bounds->point[k - 1].y =
2490
0
              (bbox.maxy * cos_t - bbox.minx * sin_t) + tp->glyphs[k - 1].pnt.y;
2491
2492
          /* Start at end and work towards the half way point */
2493
0
          bounds->point[bounds->numpoints - k - 1].x =
2494
0
              (bbox.minx * cos_t + bbox.miny * sin_t) + tp->glyphs[k - 1].pnt.x;
2495
0
          bounds->point[bounds->numpoints - k - 1].y =
2496
0
              (bbox.miny * cos_t - bbox.minx * sin_t) + tp->glyphs[k - 1].pnt.y;
2497
2498
0
        } else {
2499
2500
          /* This is the last character in the string so we take the UR and LR
2501
           corners of the bbox */
2502
0
          bounds->point[k - 1].x =
2503
0
              (bbox.maxx * cos_t + bbox.maxy * sin_t) + tp->glyphs[k - 1].pnt.x;
2504
0
          bounds->point[k - 1].y =
2505
0
              (bbox.maxy * cos_t - bbox.maxx * sin_t) + tp->glyphs[k - 1].pnt.y;
2506
2507
0
          bounds->point[bounds->numpoints - k - 1].x =
2508
0
              (bbox.maxx * cos_t + bbox.miny * sin_t) + tp->glyphs[k - 1].pnt.x;
2509
0
          bounds->point[bounds->numpoints - k - 1].y =
2510
0
              (bbox.miny * cos_t - bbox.maxx * sin_t) + tp->glyphs[k - 1].pnt.y;
2511
0
        }
2512
0
      }
2513
0
      if (overlap_collision_detected) {
2514
0
        continue;
2515
0
      }
2516
2517
      /* Close the bounds */
2518
0
      bounds->point[bounds->numpoints - 1].x = bounds->point[0].x;
2519
0
      bounds->point[bounds->numpoints - 1].y = bounds->point[0].y;
2520
2521
      /* compute the bounds */
2522
0
      fastComputeBounds(bounds, &tp->bounds.bbox);
2523
2524
0
      {
2525
0
        textPathObj *tmptp;
2526
0
        textSymbolObj *tsnew;
2527
0
        lfr->num_follow_labels++;
2528
0
        lfr->follow_labels = (textSymbolObj **)msSmallRealloc(
2529
0
            lfr->follow_labels,
2530
0
            lfr->num_follow_labels * sizeof(textSymbolObj *));
2531
0
        tmptp = ts->textpath;
2532
0
        ts->textpath = NULL;
2533
0
        lfr->follow_labels[lfr->num_follow_labels - 1] =
2534
0
            (textSymbolObj *)msSmallMalloc(sizeof(textSymbolObj));
2535
0
        tsnew = lfr->follow_labels[lfr->num_follow_labels - 1];
2536
0
        initTextSymbol(tsnew);
2537
0
        msCopyTextSymbol(tsnew, ts);
2538
0
        ts->textpath = tmptp;
2539
0
        tsnew->textpath = tp;
2540
0
        tp = NULL;
2541
0
        tsnew->textpath->absolute = 1;
2542
0
      }
2543
2544
0
      cur_label_position += label_buffer;
2545
0
      retry_offset = 0;
2546
0
    } while (retry_offset);
2547
2548
0
    if (tp) {
2549
0
      freeTextPath(tp);
2550
0
      free(tp);
2551
0
    }
2552
0
  }
2553
0
  return MS_SUCCESS;
2554
0
}
2555
2556
/* ===========================================================================
2557
   Pretty printing of primitive objects
2558
   ======================================================================== */
2559
2560
void msRectToFormattedString(rectObj *rect, char *format, char *buffer,
2561
0
                             int buffer_length) {
2562
0
  snprintf(buffer, buffer_length, format, rect->minx, rect->miny, rect->maxx,
2563
0
           rect->maxy);
2564
0
}
2565
2566
void msPointToFormattedString(pointObj *point, const char *format, char *buffer,
2567
0
                              int buffer_length) {
2568
0
  snprintf(buffer, buffer_length, format, point->x, point->y, point->z,
2569
0
           point->m);
2570
0
}
2571
2572
/* Returns true if a shape contains only degenerate parts */
2573
0
int msIsDegenerateShape(shapeObj *shape) {
2574
0
  int i;
2575
0
  int non_degenerate_parts = 0;
2576
0
  for (i = 0; i < shape->numlines; i++) { /* e.g. part */
2577
2578
    /* skip degenerate parts, really should only happen with pixel output */
2579
0
    if ((shape->type == MS_SHAPE_LINE && shape->line[i].numpoints < 2) ||
2580
0
        (shape->type == MS_SHAPE_POLYGON && shape->line[i].numpoints < 3))
2581
0
      continue;
2582
2583
0
    non_degenerate_parts++;
2584
0
  }
2585
0
  return (non_degenerate_parts == 0);
2586
0
}
2587
2588
0
shapeObj *msRings2Shape(shapeObj *shape, int outer) {
2589
0
  shapeObj *shape2;
2590
0
  int i, rv, *outerList = NULL;
2591
2592
0
  if (!shape)
2593
0
    return NULL;
2594
0
  if (shape->type != MS_SHAPE_POLYGON)
2595
0
    return NULL;
2596
2597
0
  outer = (outer) ? 1 : 0; // set explicitly to 1 or 0
2598
2599
0
  shape2 = (shapeObj *)malloc(sizeof(shapeObj));
2600
0
  MS_CHECK_ALLOC(shape2, sizeof(shapeObj), NULL);
2601
0
  msInitShape(shape2);
2602
0
  shape2->type = shape->type;
2603
2604
0
  outerList = msGetOuterList(shape);
2605
0
  if (!outerList) {
2606
0
    msFreeShape(shape2);
2607
0
    free(shape2);
2608
0
    return NULL;
2609
0
  }
2610
2611
0
  for (i = 0; i < shape->numlines; i++) {
2612
0
    if (outerList[i] == outer) { // else inner
2613
0
      rv = msAddLine(shape2, &(shape->line[i]));
2614
0
      if (rv != MS_SUCCESS) {
2615
0
        msFreeShape(shape2);
2616
0
        free(shape2);
2617
0
        free(outerList);
2618
0
        return NULL;
2619
0
      }
2620
0
    }
2621
0
  }
2622
2623
0
  free(outerList); // clean up
2624
0
  return shape2;
2625
0
}
2626
2627
0
shapeObj *msDensify(shapeObj *shape, double tolerance) {
2628
0
  int i, j, k, l; // counters
2629
0
  int n;
2630
0
  shapeObj *shape2;
2631
0
  lineObj line;
2632
0
  double distance, length, c;
2633
2634
0
  if (!shape)
2635
0
    return NULL;
2636
0
  if (shape->type != MS_SHAPE_POLYGON && shape->type != MS_SHAPE_LINE)
2637
0
    return NULL;
2638
0
  if (tolerance <= 0)
2639
0
    return NULL; // must be positive
2640
2641
0
  shape2 = (shapeObj *)malloc(sizeof(shapeObj));
2642
0
  MS_CHECK_ALLOC(shape2, sizeof(shapeObj), NULL);
2643
0
  msInitShape(shape2);
2644
0
  shape2->type = shape->type; // POLGYON or LINE
2645
2646
0
  for (i = 0; i < shape->numlines; i++) {
2647
2648
0
    line.numpoints = shape->line[i].numpoints;
2649
0
    line.point = (pointObj *)malloc(
2650
0
        sizeof(pointObj) *
2651
0
        line.numpoints); // best case we don't have to add any points
2652
0
    MS_CHECK_ALLOC(line.point, sizeof(pointObj) * line.numpoints, NULL);
2653
2654
0
    for (j = 0, l = 0; j < shape->line[i].numpoints - 1; j++, l++) {
2655
0
      line.point[l] = shape->line[i].point[j];
2656
2657
0
      distance = msDistancePointToPoint(&(shape->line[i].point[j]),
2658
0
                                        &(shape->line[i].point[j + 1]));
2659
0
      if (distance > tolerance) {
2660
0
        n = (int)floor(distance / tolerance); // number of new points, n+1 is
2661
                                              // the number of new segments
2662
0
        length = distance / (n + 1);          // segment length
2663
2664
0
        line.numpoints += n;
2665
0
        line.point =
2666
0
            (pointObj *)realloc(line.point, sizeof(pointObj) * line.numpoints);
2667
0
        MS_CHECK_ALLOC(line.point, sizeof(pointObj) * line.numpoints, NULL);
2668
2669
0
        for (k = 0; k < n; k++) {
2670
0
          c = (k + 1) * length / distance;
2671
0
          l++;
2672
0
          line.point[l].x =
2673
0
              shape->line[i].point[j].x +
2674
0
              c * (shape->line[i].point[j + 1].x - shape->line[i].point[j].x);
2675
0
          line.point[l].y =
2676
0
              shape->line[i].point[j].y +
2677
0
              c * (shape->line[i].point[j + 1].y - shape->line[i].point[j].y);
2678
0
        }
2679
0
      }
2680
0
    }
2681
0
    line.point[l] = shape->line[i].point[j];
2682
2683
0
    msAddLineDirectly(shape2, &line);
2684
0
  }
2685
2686
0
  return shape2;
2687
0
}