Coverage Report

Created: 2026-04-10 07:04

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
2.82k
shapeObj *msShapeFromWKT(const char *string) {
67
#ifdef USE_GEOS
68
  return msGEOSShapeFromWKT(string);
69
#else
70
2.82k
  return msOGRShapeFromWKT(string);
71
2.82k
#endif
72
2.82k
}
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.70M
void msInitShape(shapeObj *shape) {
135
  /* spatial component */
136
1.70M
  shape->line = NULL;
137
1.70M
  shape->numlines = 0;
138
1.70M
  shape->type = MS_SHAPE_NULL;
139
1.70M
  shape->bounds.minx = shape->bounds.miny = -1;
140
1.70M
  shape->bounds.maxx = shape->bounds.maxy = -1;
141
142
  /* attribute component */
143
1.70M
  shape->values = NULL;
144
1.70M
  shape->numvalues = 0;
145
146
1.70M
  shape->geometry = NULL;
147
1.70M
  shape->renderer_cache = NULL;
148
149
  /* annotation component */
150
1.70M
  shape->text = NULL;
151
152
  /* bookkeeping component */
153
1.70M
  shape->classindex = 0; /* default class */
154
1.70M
  shape->tileindex = shape->index = shape->resultindex = -1;
155
156
1.70M
  shape->scratch = MS_FALSE; /* not a temporary/scratch shape */
157
1.70M
}
158
159
1.54k
int msCopyShape(const shapeObj *from, shapeObj *to) {
160
1.54k
  int i;
161
162
1.54k
  if (!from || !to)
163
0
    return (-1);
164
165
1.68k
  for (i = 0; i < from->numlines; i++)
166
137
    msAddLine(to, &(from->line[i])); /* copy each line */
167
168
1.54k
  to->type = from->type;
169
170
1.54k
  to->bounds.minx = from->bounds.minx;
171
1.54k
  to->bounds.miny = from->bounds.miny;
172
1.54k
  to->bounds.maxx = from->bounds.maxx;
173
1.54k
  to->bounds.maxy = from->bounds.maxy;
174
175
1.54k
  if (from->text)
176
96
    to->text = msStrdup(from->text);
177
178
1.54k
  to->classindex = from->classindex;
179
1.54k
  to->index = from->index;
180
1.54k
  to->tileindex = from->tileindex;
181
1.54k
  to->resultindex = from->resultindex;
182
183
1.54k
  if (from->values) {
184
1.02k
    if (to->values)
185
0
      msFreeCharArray(to->values, to->numvalues);
186
1.02k
    to->values = (char **)msSmallMalloc(sizeof(char *) * from->numvalues);
187
1.79k
    for (i = 0; i < from->numvalues; i++)
188
769
      to->values[i] = msStrdup(from->values[i]);
189
1.02k
    to->numvalues = from->numvalues;
190
1.02k
  }
191
192
1.54k
  to->geometry = NULL; /* GEOS code will build automatically if necessary */
193
1.54k
  to->scratch = from->scratch;
194
195
1.54k
  return (0);
196
1.54k
}
197
198
570k
void msFreeShape(shapeObj *shape) {
199
570k
  int c;
200
201
570k
  if (!shape)
202
808
    return; /* for safety */
203
204
787k
  for (c = 0; c < shape->numlines; c++)
205
217k
    free(shape->line[c].point);
206
207
569k
  if (shape->line)
208
216k
    free(shape->line);
209
569k
  if (shape->values)
210
1.77k
    msFreeCharArray(shape->values, shape->numvalues);
211
569k
  if (shape->text)
212
204
    free(shape->text);
213
214
#ifdef USE_GEOS
215
  msGEOSFreeGeometry(shape);
216
#endif
217
218
569k
  msInitShape(shape); /* now reset */
219
569k
}
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
957
int msAddLine(shapeObj *p, const lineObj *new_line) {
409
957
  lineObj lineCopy;
410
411
957
  lineCopy.numpoints = new_line->numpoints;
412
957
  lineCopy.point = (pointObj *)malloc(new_line->numpoints * sizeof(pointObj));
413
957
  MS_CHECK_ALLOC(lineCopy.point, new_line->numpoints * sizeof(pointObj),
414
957
                 MS_FAILURE);
415
416
957
  if (new_line->point)
417
150
    memcpy(lineCopy.point, new_line->point,
418
150
           sizeof(pointObj) * new_line->numpoints);
419
420
  // cppcheck-suppress memleak
421
957
  return msAddLineDirectly(p, &lineCopy);
422
957
}
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
2.01k
int msAddLineDirectly(shapeObj *p, lineObj *new_line) {
429
2.01k
  int c;
430
431
2.01k
  if (p->numlines == 0) {
432
1.30k
    p->line = (lineObj *)malloc(sizeof(lineObj));
433
1.30k
  } else {
434
713
    lineObj *newline =
435
713
        (lineObj *)realloc(p->line, (p->numlines + 1) * sizeof(lineObj));
436
713
    if (!newline) {
437
0
      free(p->line);
438
0
    }
439
713
    p->line = newline;
440
713
  }
441
2.01k
  if (!p->line) {
442
0
    free(new_line->point);
443
0
    new_line->point = NULL;
444
0
    new_line->numpoints = 0;
445
0
  }
446
2.01k
  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
2.01k
  c = p->numlines;
450
2.01k
  p->line[c].numpoints = new_line->numpoints;
451
2.01k
  p->line[c].point = new_line->point;
452
453
  /* strip points reference off the passed in lineObj */
454
2.01k
  new_line->point = NULL;
455
2.01k
  new_line->numpoints = 0;
456
457
  /* Update the polygon information */
458
2.01k
  p->numlines++;
459
460
2.01k
  return (MS_SUCCESS);
461
2.01k
}
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
0
    line.point = (pointObj *)msSmallMalloc(
691
0
        sizeof(pointObj) * 2 * shape->line[j].numpoints +
692
0
        1); /* worst case scenario, +1 allows us to duplicate the 1st and last
693
               point */
694
0
    line.numpoints = 0;
695
696
0
    for (i = 0; i < shape->line[j].numpoints - 1; i++) {
697
698
0
      x1 = shape->line[j].point[i].x;
699
0
      y1 = shape->line[j].point[i].y;
700
0
      x2 = shape->line[j].point[i + 1].x;
701
0
      y2 = shape->line[j].point[i + 1].y;
702
703
0
      deltax = x2 - x1;
704
0
      if (deltax == 0) { /* bump off of the vertical */
705
0
        deltax = (x1 > rect.minx) ? -NEARZERO : NEARZERO;
706
0
      }
707
0
      deltay = y2 - y1;
708
0
      if (deltay == 0) { /* bump off of the horizontal */
709
0
        deltay = (y1 > rect.miny) ? -NEARZERO : NEARZERO;
710
0
      }
711
712
0
      if (deltax > 0) { /*  points to right */
713
0
        xin = rect.minx;
714
0
        xout = rect.maxx;
715
0
      } else {
716
0
        xin = rect.maxx;
717
0
        xout = rect.minx;
718
0
      }
719
0
      if (deltay > 0) { /*  points up */
720
0
        yin = rect.miny;
721
0
        yout = rect.maxy;
722
0
      } else {
723
0
        yin = rect.maxy;
724
0
        yout = rect.miny;
725
0
      }
726
727
0
      tinx = (xin - x1) / deltax;
728
0
      tiny = (yin - y1) / deltay;
729
730
0
      if (tinx < tiny) { /* hits x first */
731
0
        tin1 = tinx;
732
0
        tin2 = tiny;
733
0
      } else { /* hits y first */
734
0
        tin1 = tiny;
735
0
        tin2 = tinx;
736
0
      }
737
738
0
      if (1 >= tin1) {
739
0
        if (0 < tin1) {
740
0
          line.point[line.numpoints].x = xin;
741
0
          line.point[line.numpoints].y = yin;
742
0
          line.numpoints++;
743
0
        }
744
0
        if (1 >= tin2) {
745
0
          toutx = (xout - x1) / deltax;
746
0
          touty = (yout - y1) / deltay;
747
748
0
          tout = (toutx < touty) ? toutx : touty;
749
750
0
          if (0 < tin2 || 0 < tout) {
751
0
            if (tin2 <= tout) {
752
0
              if (0 < tin2) {
753
0
                if (tinx > tiny) {
754
0
                  line.point[line.numpoints].x = xin;
755
0
                  line.point[line.numpoints].y = y1 + tinx * deltay;
756
0
                  line.numpoints++;
757
0
                } else {
758
0
                  line.point[line.numpoints].x = x1 + tiny * deltax;
759
0
                  line.point[line.numpoints].y = yin;
760
0
                  line.numpoints++;
761
0
                }
762
0
              }
763
0
              if (1 > tout) {
764
0
                if (toutx < touty) {
765
0
                  line.point[line.numpoints].x = xout;
766
0
                  line.point[line.numpoints].y = y1 + toutx * deltay;
767
0
                  line.numpoints++;
768
0
                } else {
769
0
                  line.point[line.numpoints].x = x1 + touty * deltax;
770
0
                  line.point[line.numpoints].y = yout;
771
0
                  line.numpoints++;
772
0
                }
773
0
              } else {
774
0
                line.point[line.numpoints].x = x2;
775
0
                line.point[line.numpoints].y = y2;
776
0
                line.numpoints++;
777
0
              }
778
0
            } else {
779
0
              if (tinx > tiny) {
780
0
                line.point[line.numpoints].x = xin;
781
0
                line.point[line.numpoints].y = yout;
782
0
                line.numpoints++;
783
0
              } else {
784
0
                line.point[line.numpoints].x = xout;
785
0
                line.point[line.numpoints].y = yin;
786
0
                line.numpoints++;
787
0
              }
788
0
            }
789
0
          }
790
0
        }
791
0
      }
792
0
    }
793
794
0
    if (line.numpoints > 0) {
795
0
      line.point[line.numpoints].x = line.point[0].x; /* force closure */
796
0
      line.point[line.numpoints].y = line.point[0].y;
797
0
      line.numpoints++;
798
0
      msAddLineDirectly(&tmp, &line);
799
0
    } else {
800
0
      free(line.point);
801
0
    }
802
0
  } /* next line */
803
804
0
  for (i = 0; i < shape->numlines; i++)
805
0
    free(shape->line[i].point);
806
0
  free(shape->line);
807
0
  shape->numlines = 0;
808
0
  shape->line = nullptr;
809
810
0
  std::swap(shape->line, tmp.line);
811
0
  std::swap(shape->numlines, tmp.numlines);
812
813
0
  msComputeBounds(shape);
814
815
0
  return;
816
0
}
817
818
/*
819
** offsets a point relative to an image position
820
*/
821
0
void msOffsetPointRelativeTo(pointObj *point, layerObj *layer) {
822
0
  double x = 0, y = 0;
823
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
824
0
    return;
825
826
0
  if (layer->transform == MS_TRUE)
827
0
    return; /* nothing to do */
828
829
0
  if (layer->units == MS_PERCENTAGES) {
830
0
    point->x *= (layer->map->width - 1);
831
0
    point->y *= (layer->map->height - 1);
832
0
  }
833
834
0
  if (layer->transform == MS_FALSE || layer->transform == MS_UL)
835
0
    return; /* done */
836
837
0
  switch (layer->transform) {
838
0
  case MS_UC:
839
0
    x = (layer->map->width - 1) / 2;
840
0
    y = 0;
841
0
    break;
842
0
  case MS_UR:
843
0
    x = layer->map->width - 1;
844
0
    y = 0;
845
0
    break;
846
0
  case MS_CL:
847
0
    x = 0;
848
0
    y = layer->map->height / 2;
849
0
    break;
850
0
  case MS_CC:
851
0
    x = layer->map->width / 2;
852
0
    y = layer->map->height / 2;
853
0
    break;
854
0
  case MS_CR:
855
0
    x = layer->map->width - 1;
856
0
    y = layer->map->height / 2;
857
0
    break;
858
0
  case MS_LL:
859
0
    x = 0;
860
0
    y = layer->map->height - 1;
861
0
    break;
862
0
  case MS_LC:
863
0
    x = layer->map->width / 2;
864
0
    y = layer->map->height - 1;
865
0
    break;
866
0
  case MS_LR:
867
0
    x = layer->map->width - 1;
868
0
    y = layer->map->height - 1;
869
0
    break;
870
0
  }
871
872
0
  point->x += x;
873
0
  point->y += y;
874
875
0
  return;
876
0
}
877
878
/*
879
** offsets a shape relative to an image position
880
*/
881
0
void msOffsetShapeRelativeTo(shapeObj *shape, layerObj *layer) {
882
0
  int i, j;
883
0
  double x = 0, y = 0;
884
885
0
  if (layer->transform == MS_TRUE)
886
0
    return; /* nothing to do */
887
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
888
0
    return;
889
890
0
  if (layer->units == MS_PERCENTAGES) {
891
0
    for (i = 0; i < shape->numlines; i++) {
892
0
      for (j = 0; j < shape->line[i].numpoints; j++) {
893
0
        shape->line[i].point[j].x *= (layer->map->width - 1);
894
0
        shape->line[i].point[j].y *= (layer->map->height - 1);
895
0
      }
896
0
    }
897
0
  }
898
899
0
  if (layer->transform == MS_FALSE || layer->transform == MS_UL)
900
0
    return; /* done */
901
902
0
  switch (layer->transform) {
903
0
  case MS_UC:
904
0
    x = (layer->map->width - 1) / 2;
905
0
    y = 0;
906
0
    break;
907
0
  case MS_UR:
908
0
    x = layer->map->width - 1;
909
0
    y = 0;
910
0
    break;
911
0
  case MS_CL:
912
0
    x = 0;
913
0
    y = layer->map->height / 2;
914
0
    break;
915
0
  case MS_CC:
916
0
    x = layer->map->width / 2;
917
0
    y = layer->map->height / 2;
918
0
    break;
919
0
  case MS_CR:
920
0
    x = layer->map->width - 1;
921
0
    y = layer->map->height / 2;
922
0
    break;
923
0
  case MS_LL:
924
0
    x = 0;
925
0
    y = layer->map->height - 1;
926
0
    break;
927
0
  case MS_LC:
928
0
    x = layer->map->width / 2;
929
0
    y = layer->map->height - 1;
930
0
    break;
931
0
  case MS_LR:
932
0
    x = layer->map->width - 1;
933
0
    y = layer->map->height - 1;
934
0
    break;
935
0
  }
936
937
0
  for (i = 0; i < shape->numlines; i++) {
938
0
    for (j = 0; j < shape->line[i].numpoints; j++) {
939
0
      shape->line[i].point[j].x += x;
940
0
      shape->line[i].point[j].y += y;
941
0
    }
942
0
  }
943
944
0
  return;
945
0
}
946
947
void msTransformShapeSimplify(shapeObj *shape, rectObj extent,
948
0
                              double cellsize) {
949
0
  int i, j, k, beforelast; /* loop counters */
950
0
  double dx, dy;
951
0
  pointObj *point;
952
0
  double inv_cs = 1.0 / cellsize; /* invert and multiply much faster */
953
0
  int ok = 0;
954
0
  if (shape->numlines == 0)
955
0
    return; /* nothing to transform */
956
957
0
  if (shape->type == MS_SHAPE_LINE) {
958
    /*
959
     * loop through the shape's lines, and do naive simplification
960
     * to discard the points that are too close to one another.
961
     * currently the threshold is to discard points if they fall
962
     * less than a pixel away from their predecessor.
963
     * the simplified line is guaranteed to contain at
964
     * least its first and last point
965
     */
966
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
967
0
      if (shape->line[i].numpoints < 2) {
968
0
        shape->line[i].numpoints = 0;
969
0
        continue; /*skip degenerate lines*/
970
0
      }
971
0
      point = shape->line[i].point;
972
      /*always keep first point*/
973
0
      point[0].x = MS_MAP2IMAGE_X_IC_DBL(point[0].x, extent.minx, inv_cs);
974
0
      point[0].y = MS_MAP2IMAGE_Y_IC_DBL(point[0].y, extent.maxy, inv_cs);
975
0
      beforelast = shape->line[i].numpoints - 1;
976
0
      for (j = 1, k = 1; j < beforelast;
977
0
           j++) { /*loop from second point to first-before-last point*/
978
0
        point[k].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
979
0
        point[k].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
980
0
        dx = (point[k].x - point[k - 1].x);
981
0
        dy = (point[k].y - point[k - 1].y);
982
0
        if (dx * dx + dy * dy > 1)
983
0
          k++;
984
0
      }
985
      /* try to keep last point */
986
0
      point[k].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
987
0
      point[k].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
988
      /* discard last point if equal to the one before it */
989
0
      if (point[k].x != point[k - 1].x || point[k].y != point[k - 1].y) {
990
0
        shape->line[i].numpoints = k + 1;
991
0
      } else {
992
0
        shape->line[i].numpoints = k;
993
0
      }
994
      /* skip degenerate line once more */
995
0
      if (shape->line[i].numpoints < 2) {
996
0
        shape->line[i].numpoints = 0;
997
0
      } else {
998
0
        ok = 1; /* we have at least one line with more than two points */
999
0
      }
1000
0
    }
1001
0
  } else if (shape->type == MS_SHAPE_POLYGON) {
1002
    /*
1003
     * loop through the shape's lines, and do naive simplification
1004
     * to discard the points that are too close to one another.
1005
     * currently the threshold is to discard points if they fall
1006
     * less than a pixel away from their predecessor.
1007
     * the simplified polygon is guaranteed to contain at
1008
     * least its first, second and last point
1009
     */
1010
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1011
0
      if (shape->line[i].numpoints < 4) {
1012
0
        shape->line[i].numpoints = 0;
1013
0
        continue; /*skip degenerate lines*/
1014
0
      }
1015
0
      point = shape->line[i].point;
1016
      /*always keep first and second point*/
1017
0
      point[0].x = MS_MAP2IMAGE_X_IC_DBL(point[0].x, extent.minx, inv_cs);
1018
0
      point[0].y = MS_MAP2IMAGE_Y_IC_DBL(point[0].y, extent.maxy, inv_cs);
1019
0
      point[1].x = MS_MAP2IMAGE_X_IC_DBL(point[1].x, extent.minx, inv_cs);
1020
0
      point[1].y = MS_MAP2IMAGE_Y_IC_DBL(point[1].y, extent.maxy, inv_cs);
1021
0
      beforelast = shape->line[i].numpoints - 2;
1022
0
      for (j = 2, k = 2; j < beforelast;
1023
0
           j++) { /*loop from second point to second-before-last point*/
1024
0
        point[k].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
1025
0
        point[k].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
1026
0
        dx = (point[k].x - point[k - 1].x);
1027
0
        dy = (point[k].y - point[k - 1].y);
1028
0
        if (dx * dx + dy * dy > 1)
1029
0
          k++;
1030
0
      }
1031
      /*always keep last two points (the last point is the repetition of the
1032
       * first one */
1033
0
      point[k].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
1034
0
      point[k].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
1035
0
      point[k + 1].x =
1036
0
          MS_MAP2IMAGE_X_IC_DBL(point[j + 1].x, extent.minx, inv_cs);
1037
0
      point[k + 1].y =
1038
0
          MS_MAP2IMAGE_Y_IC_DBL(point[j + 1].y, extent.maxy, inv_cs);
1039
0
      shape->line[i].numpoints = k + 2;
1040
0
      ok = 1;
1041
0
    }
1042
0
  } else { /* only for untyped shapes, as point layers don't go through this
1043
              function */
1044
0
    for (i = 0; i < shape->numlines; i++) {
1045
0
      point = shape->line[i].point;
1046
0
      for (j = 0; j < shape->line[i].numpoints; j++) {
1047
0
        point[j].x = MS_MAP2IMAGE_X_IC_DBL(point[j].x, extent.minx, inv_cs);
1048
0
        point[j].y = MS_MAP2IMAGE_Y_IC_DBL(point[j].y, extent.maxy, inv_cs);
1049
0
      }
1050
0
    }
1051
0
    ok = 1;
1052
0
  }
1053
0
  if (!ok) {
1054
0
    for (i = 0; i < shape->numlines; i++) {
1055
0
      free(shape->line[i].point);
1056
0
    }
1057
0
    shape->numlines = 0;
1058
0
  }
1059
0
}
1060
1061
/**
1062
 * Generic function to transform the shape coordinates to output coordinates
1063
 */
1064
void msTransformShape(shapeObj *shape, rectObj extent, double cellsize,
1065
0
                      imageObj *image) {
1066
0
  if (image != NULL && MS_RENDERER_PLUGIN(image->format)) {
1067
0
    rendererVTableObj *renderer = MS_IMAGE_RENDERER(image);
1068
0
    if (renderer->transform_mode == MS_TRANSFORM_SNAPTOGRID) {
1069
0
      msTransformShapeToPixelSnapToGrid(shape, extent, cellsize,
1070
0
                                        renderer->approximation_scale);
1071
0
    } else if (renderer->transform_mode == MS_TRANSFORM_SIMPLIFY) {
1072
0
      msTransformShapeSimplify(shape, extent, cellsize);
1073
0
    } else if (renderer->transform_mode == MS_TRANSFORM_ROUND) {
1074
0
      msTransformShapeToPixelRound(shape, extent, cellsize);
1075
0
    } else if (renderer->transform_mode == MS_TRANSFORM_FULLRESOLUTION) {
1076
0
      msTransformShapeToPixelDoublePrecision(shape, extent, cellsize);
1077
0
    } else if (renderer->transform_mode == MS_TRANSFORM_NONE) {
1078
      /* nothing to do */
1079
0
      return;
1080
0
    }
1081
    /* unknown, do nothing */
1082
0
    return;
1083
0
  }
1084
0
  msTransformShapeToPixelRound(shape, extent, cellsize);
1085
0
}
1086
1087
void msTransformShapeToPixelSnapToGrid(shapeObj *shape, rectObj extent,
1088
                                       double cellsize,
1089
0
                                       double grid_resolution) {
1090
0
  int i, j, k; /* loop counters */
1091
0
  double inv_cs;
1092
0
  if (shape->numlines == 0)
1093
0
    return;
1094
0
  inv_cs = 1.0 / cellsize; /* invert and multiply much faster */
1095
1096
0
  if (shape->type == MS_SHAPE_LINE ||
1097
0
      shape->type == MS_SHAPE_POLYGON) {    /* remove duplicate vertices */
1098
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1099
0
      int snap = 1;
1100
0
      const double x0 = MS_MAP2IMAGE_X_IC_SNAP(
1101
0
          shape->line[i].point[0].x, extent.minx, inv_cs, grid_resolution);
1102
0
      const double y0 = MS_MAP2IMAGE_Y_IC_SNAP(
1103
0
          shape->line[i].point[0].y, extent.maxy, inv_cs, grid_resolution);
1104
1105
      /*do a quick heuristic: will we risk having a degenerate shape*/
1106
0
      if (shape->type == MS_SHAPE_LINE) {
1107
        /*a line is degenerate if it has a single pixel. we check that the first
1108
         * and last pixel are different*/
1109
0
        const double x1 = MS_MAP2IMAGE_X_IC_SNAP(
1110
0
            shape->line[i].point[shape->line[i].numpoints - 1].x, extent.minx,
1111
0
            inv_cs, grid_resolution);
1112
0
        const double y1 = MS_MAP2IMAGE_Y_IC_SNAP(
1113
0
            shape->line[i].point[shape->line[i].numpoints - 1].y, extent.maxy,
1114
0
            inv_cs, grid_resolution);
1115
0
        if (x0 == x1 && y0 == y1) {
1116
0
          snap = 0;
1117
0
        }
1118
0
      } else /* if(shape->type == MS_SHAPE_POLYGON) */ {
1119
0
        const double x1 = MS_MAP2IMAGE_X_IC_SNAP(
1120
0
            shape->line[i].point[shape->line[i].numpoints / 3].x, extent.minx,
1121
0
            inv_cs, grid_resolution);
1122
0
        const double y1 = MS_MAP2IMAGE_Y_IC_SNAP(
1123
0
            shape->line[i].point[shape->line[i].numpoints / 3].y, extent.maxy,
1124
0
            inv_cs, grid_resolution);
1125
0
        const double x2 = MS_MAP2IMAGE_X_IC_SNAP(
1126
0
            shape->line[i].point[shape->line[i].numpoints / 3 * 2].x,
1127
0
            extent.minx, inv_cs, grid_resolution);
1128
0
        const double y2 = MS_MAP2IMAGE_Y_IC_SNAP(
1129
0
            shape->line[i].point[shape->line[i].numpoints / 3 * 2].y,
1130
0
            extent.maxy, inv_cs, grid_resolution);
1131
0
        if ((x0 == x1 && y0 == y1) || (x0 == x2 && y0 == y2) ||
1132
0
            (x1 == x2 && y1 == y2)) {
1133
0
          snap = 0;
1134
0
        }
1135
0
      }
1136
1137
0
      if (snap) {
1138
0
        shape->line[i].point[0].x = x0;
1139
0
        shape->line[i].point[0].y = y0;
1140
0
        for (j = 1, k = 1; j < shape->line[i].numpoints; j++) {
1141
0
          shape->line[i].point[k].x = MS_MAP2IMAGE_X_IC_SNAP(
1142
0
              shape->line[i].point[j].x, extent.minx, inv_cs, grid_resolution);
1143
0
          shape->line[i].point[k].y = MS_MAP2IMAGE_Y_IC_SNAP(
1144
0
              shape->line[i].point[j].y, extent.maxy, inv_cs, grid_resolution);
1145
0
          if (shape->line[i].point[k].x != shape->line[i].point[k - 1].x ||
1146
0
              shape->line[i].point[k].y != shape->line[i].point[k - 1].y)
1147
0
            k++;
1148
0
        }
1149
0
        shape->line[i].numpoints = k;
1150
0
      } else {
1151
0
        if (shape->type == MS_SHAPE_LINE) {
1152
0
          shape->line[i].point[0].x = MS_MAP2IMAGE_X_IC_DBL(
1153
0
              shape->line[i].point[0].x, extent.minx, inv_cs);
1154
0
          shape->line[i].point[0].y = MS_MAP2IMAGE_Y_IC_DBL(
1155
0
              shape->line[i].point[0].y, extent.maxy, inv_cs);
1156
0
          shape->line[i].point[1].x = MS_MAP2IMAGE_X_IC_DBL(
1157
0
              shape->line[i].point[shape->line[i].numpoints - 1].x, extent.minx,
1158
0
              inv_cs);
1159
0
          shape->line[i].point[1].y = MS_MAP2IMAGE_Y_IC_DBL(
1160
0
              shape->line[i].point[shape->line[i].numpoints - 1].y, extent.maxy,
1161
0
              inv_cs);
1162
0
          shape->line[i].numpoints = 2;
1163
0
        } else {
1164
0
          for (j = 0; j < shape->line[i].numpoints; j++) {
1165
0
            shape->line[i].point[j].x = MS_MAP2IMAGE_X_IC_DBL(
1166
0
                shape->line[i].point[j].x, extent.minx, inv_cs);
1167
0
            shape->line[i].point[j].y = MS_MAP2IMAGE_Y_IC_DBL(
1168
0
                shape->line[i].point[j].y, extent.maxy, inv_cs);
1169
0
          }
1170
0
        }
1171
0
      }
1172
0
    }
1173
0
  } else {                                  /* points or untyped shapes */
1174
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1175
0
      for (j = 1; j < shape->line[i].numpoints; j++) {
1176
0
        shape->line[i].point[j].x = MS_MAP2IMAGE_X_IC_DBL(
1177
0
            shape->line[i].point[j].x, extent.minx, inv_cs);
1178
0
        shape->line[i].point[j].y = MS_MAP2IMAGE_Y_IC_DBL(
1179
0
            shape->line[i].point[j].y, extent.maxy, inv_cs);
1180
0
      }
1181
0
    }
1182
0
  }
1183
0
}
1184
1185
void msTransformShapeToPixelRound(shapeObj *shape, rectObj extent,
1186
0
                                  double cellsize) {
1187
0
  int i, j, k; /* loop counters */
1188
0
  double inv_cs;
1189
0
  if (shape->numlines == 0)
1190
0
    return;
1191
0
  inv_cs = 1.0 / cellsize; /* invert and multiply much faster */
1192
0
  if (shape->type == MS_SHAPE_LINE ||
1193
0
      shape->type == MS_SHAPE_POLYGON) {    /* remove duplicate vertices */
1194
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1195
0
      shape->line[i].point[0].x =
1196
0
          MS_MAP2IMAGE_X_IC(shape->line[i].point[0].x, extent.minx, inv_cs);
1197
0
      ;
1198
0
      shape->line[i].point[0].y =
1199
0
          MS_MAP2IMAGE_Y_IC(shape->line[i].point[0].y, extent.maxy, inv_cs);
1200
0
      for (j = 1, k = 1; j < shape->line[i].numpoints; j++) {
1201
0
        shape->line[i].point[k].x =
1202
0
            MS_MAP2IMAGE_X_IC(shape->line[i].point[j].x, extent.minx, inv_cs);
1203
0
        shape->line[i].point[k].y =
1204
0
            MS_MAP2IMAGE_Y_IC(shape->line[i].point[j].y, extent.maxy, inv_cs);
1205
0
        if (shape->line[i].point[k].x != shape->line[i].point[k - 1].x ||
1206
0
            shape->line[i].point[k].y != shape->line[i].point[k - 1].y)
1207
0
          k++;
1208
0
      }
1209
0
      shape->line[i].numpoints = k;
1210
0
    }
1211
0
  } else {                                  /* points or untyped shapes */
1212
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1213
0
      for (j = 0; j < shape->line[i].numpoints; j++) {
1214
0
        shape->line[i].point[j].x =
1215
0
            MS_MAP2IMAGE_X_IC(shape->line[i].point[j].x, extent.minx, inv_cs);
1216
0
        shape->line[i].point[j].y =
1217
0
            MS_MAP2IMAGE_Y_IC(shape->line[i].point[j].y, extent.maxy, inv_cs);
1218
0
      }
1219
0
    }
1220
0
  }
1221
0
}
1222
1223
void msTransformShapeToPixelDoublePrecision(shapeObj *shape, rectObj extent,
1224
0
                                            double cellsize) {
1225
0
  int i, j;                       /* loop counters */
1226
0
  double inv_cs = 1.0 / cellsize; /* invert and multiply much faster */
1227
0
  for (i = 0; i < shape->numlines; i++) {
1228
0
    for (j = 0; j < shape->line[i].numpoints; j++) {
1229
0
      shape->line[i].point[j].x =
1230
0
          MS_MAP2IMAGE_X_IC_DBL(shape->line[i].point[j].x, extent.minx, inv_cs);
1231
0
      shape->line[i].point[j].y =
1232
0
          MS_MAP2IMAGE_Y_IC_DBL(shape->line[i].point[j].y, extent.maxy, inv_cs);
1233
0
    }
1234
0
  }
1235
0
}
1236
1237
/*
1238
** Converts from map coordinates to image coordinates
1239
*/
1240
0
void msTransformPixelToShape(shapeObj *shape, rectObj extent, double cellsize) {
1241
0
  int i, j; /* loop counters */
1242
1243
0
  if (shape->numlines == 0)
1244
0
    return; /* nothing to transform */
1245
1246
0
  if (shape->type == MS_SHAPE_LINE ||
1247
0
      shape->type == MS_SHAPE_POLYGON) { /* remove co-linear vertices */
1248
1249
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1250
0
      for (j = 0; j < shape->line[i].numpoints; j++) {
1251
0
        shape->line[i].point[j].x =
1252
0
            MS_IMAGE2MAP_X(shape->line[i].point[j].x, extent.minx, cellsize);
1253
0
        shape->line[i].point[j].y =
1254
0
            MS_IMAGE2MAP_Y(shape->line[i].point[j].y, extent.maxy, cellsize);
1255
0
      }
1256
0
    }
1257
0
  } else { /* points or untyped shapes */
1258
1259
0
    for (i = 0; i < shape->numlines; i++) { /* for each part */
1260
0
      for (j = 1; j < shape->line[i].numpoints; j++) {
1261
0
        shape->line[i].point[j].x =
1262
0
            MS_IMAGE2MAP_X(shape->line[i].point[j].x, extent.minx, cellsize);
1263
0
        shape->line[i].point[j].y =
1264
0
            MS_IMAGE2MAP_Y(shape->line[i].point[j].y, extent.maxy, cellsize);
1265
0
      }
1266
0
    }
1267
0
  }
1268
1269
0
  return;
1270
0
}
1271
1272
/*
1273
** Not a generic intersection test, we KNOW the lines aren't parallel or
1274
*coincident. To be used with the next
1275
** buffering code only. See code in mapsearch.c for a boolean test for
1276
*intersection.
1277
*/
1278
static pointObj generateLineIntersection(pointObj a, pointObj b, pointObj c,
1279
0
                                         pointObj d) {
1280
0
  pointObj p;
1281
0
  double r;
1282
0
  double denominator, numerator;
1283
1284
0
  if (b.x == c.x && b.y == c.y)
1285
0
    return (b);
1286
1287
0
  numerator = ((a.y - c.y) * (d.x - c.x) - (a.x - c.x) * (d.y - c.y));
1288
0
  denominator = ((b.x - a.x) * (d.y - c.y) - (b.y - a.y) * (d.x - c.x));
1289
1290
0
  r = numerator / denominator;
1291
1292
0
  p.x = MS_NINT(a.x + r * (b.x - a.x));
1293
0
  p.y = MS_NINT(a.y + r * (b.y - a.y));
1294
0
  p.z = 0;
1295
0
  p.m = 0;
1296
1297
0
  return (p);
1298
0
}
1299
1300
0
void bufferPolyline(shapeObj *p, shapeObj *op, int w) {
1301
0
  int i, j;
1302
0
  pointObj a = {0, 0, 0, 0};
1303
0
  lineObj inside, outside;
1304
0
  double angle;
1305
0
  double dx, dy;
1306
1307
0
  for (i = 0; i < p->numlines; i++) {
1308
1309
0
    inside.point =
1310
0
        (pointObj *)msSmallMalloc(sizeof(pointObj) * p->line[i].numpoints);
1311
0
    outside.point =
1312
0
        (pointObj *)msSmallMalloc(sizeof(pointObj) * p->line[i].numpoints);
1313
0
    inside.numpoints = outside.numpoints = p->line[i].numpoints;
1314
1315
0
    angle = asin(MS_ABS(p->line[i].point[1].x - p->line[i].point[0].x) /
1316
0
                 sqrt((((p->line[i].point[1].x - p->line[i].point[0].x) *
1317
0
                        (p->line[i].point[1].x - p->line[i].point[0].x)) +
1318
0
                       ((p->line[i].point[1].y - p->line[i].point[0].y) *
1319
0
                        (p->line[i].point[1].y - p->line[i].point[0].y)))));
1320
0
    if (p->line[i].point[0].x < p->line[i].point[1].x)
1321
0
      dy = sin(angle) * (w / 2);
1322
0
    else
1323
0
      dy = -sin(angle) * (w / 2);
1324
0
    if (p->line[i].point[0].y < p->line[i].point[1].y)
1325
0
      dx = -cos(angle) * (w / 2);
1326
0
    else
1327
0
      dx = cos(angle) * (w / 2);
1328
1329
0
    inside.point[0].x = p->line[i].point[0].x + dx;
1330
0
    inside.point[1].x = p->line[i].point[1].x + dx;
1331
0
    inside.point[0].y = p->line[i].point[0].y + dy;
1332
0
    inside.point[1].y = p->line[i].point[1].y + dy;
1333
1334
0
    outside.point[0].x = p->line[i].point[0].x - dx;
1335
0
    outside.point[1].x = p->line[i].point[1].x - dx;
1336
0
    outside.point[0].y = p->line[i].point[0].y - dy;
1337
0
    outside.point[1].y = p->line[i].point[1].y - dy;
1338
1339
0
    for (j = 2; j < p->line[i].numpoints; j++) {
1340
1341
0
      angle =
1342
0
          asin(MS_ABS(p->line[i].point[j].x - p->line[i].point[j - 1].x) /
1343
0
               sqrt((((p->line[i].point[j].x - p->line[i].point[j - 1].x) *
1344
0
                      (p->line[i].point[j].x - p->line[i].point[j - 1].x)) +
1345
0
                     ((p->line[i].point[j].y - p->line[i].point[j - 1].y) *
1346
0
                      (p->line[i].point[j].y - p->line[i].point[j - 1].y)))));
1347
0
      if (p->line[i].point[j - 1].x < p->line[i].point[j].x)
1348
0
        dy = sin(angle) * (w / 2);
1349
0
      else
1350
0
        dy = -sin(angle) * (w / 2);
1351
0
      if (p->line[i].point[j - 1].y < p->line[i].point[j].y)
1352
0
        dx = -cos(angle) * (w / 2);
1353
0
      else
1354
0
        dx = cos(angle) * (w / 2);
1355
1356
0
      a.x = p->line[i].point[j - 1].x + dx;
1357
0
      inside.point[j].x = p->line[i].point[j].x + dx;
1358
0
      a.y = p->line[i].point[j - 1].y + dy;
1359
0
      inside.point[j].y = p->line[i].point[j].y + dy;
1360
0
      inside.point[j - 1] = generateLineIntersection(
1361
0
          inside.point[j - 2], inside.point[j - 1], a, inside.point[j]);
1362
1363
0
      a.x = p->line[i].point[j - 1].x - dx;
1364
0
      outside.point[j].x = p->line[i].point[j].x - dx;
1365
0
      a.y = p->line[i].point[j - 1].y - dy;
1366
0
      outside.point[j].y = p->line[i].point[j].y - dy;
1367
0
      outside.point[j - 1] = generateLineIntersection(
1368
0
          outside.point[j - 2], outside.point[j - 1], a, outside.point[j]);
1369
0
    }
1370
1371
    /* need a touch of code if 1st point equals last point in p (find
1372
     * intersection) */
1373
1374
0
    msAddLine(op, &inside);
1375
0
    msAddLine(op, &outside);
1376
1377
0
    free(inside.point);
1378
0
    free(outside.point);
1379
0
  }
1380
1381
0
  return;
1382
0
}
1383
1384
0
static double getRingArea(lineObj *ring) {
1385
0
  int i;
1386
0
  double s = 0;
1387
1388
0
  for (i = 0; i < ring->numpoints - 1; i++)
1389
0
    s += (ring->point[i].x * ring->point[i + 1].y -
1390
0
          ring->point[i + 1].x * ring->point[i].y);
1391
1392
0
  return (MS_ABS(s / 2));
1393
0
}
1394
1395
0
double msGetPolygonArea(shapeObj *p) {
1396
0
  int i;
1397
0
  double area = 0;
1398
1399
0
  if (!p)
1400
0
    return 0;
1401
1402
0
  for (i = 0; i < p->numlines; i++) {
1403
0
    if (msIsOuterRing(p, i))
1404
0
      area += getRingArea(&(p->line[i]));
1405
0
    else
1406
0
      area -= getRingArea(&(p->line[i])); /* hole */
1407
0
  }
1408
1409
0
  return area;
1410
0
}
1411
1412
/*
1413
** Computes the center of gravity for a polygon based on it's largest outer ring
1414
*only.
1415
*/
1416
0
static int getPolygonCenterOfGravity(shapeObj *p, pointObj *lp) {
1417
0
  double sx = 0, sy = 0; /* sums */
1418
0
  double largestArea = 0;
1419
1420
0
  for (int i = 0; i < p->numlines; i++) {
1421
0
    double tsx = 0;
1422
0
    double tsy = 0;
1423
0
    double s = 0; /* reset the ring sums */
1424
0
    for (int j = 0; j < p->line[i].numpoints - 1; j++) {
1425
0
      double a = p->line[i].point[j].x * p->line[i].point[j + 1].y -
1426
0
                 p->line[i].point[j + 1].x * p->line[i].point[j].y;
1427
0
      s += a;
1428
0
      tsx += (p->line[i].point[j].x + p->line[i].point[j + 1].x) * a;
1429
0
      tsy += (p->line[i].point[j].y + p->line[i].point[j + 1].y) * a;
1430
0
    }
1431
0
    double area = MS_ABS(s / 2);
1432
1433
0
    if (area > largestArea) {
1434
0
      largestArea = area;
1435
0
      sx = s > 0 ? tsx : -tsx;
1436
0
      sy = s > 0 ? tsy : -tsy;
1437
0
    }
1438
0
  }
1439
0
  if (largestArea == 0) /*degenerate polygon*/
1440
0
    return MS_FAILURE;
1441
1442
0
  lp->x = sx / (6 * largestArea);
1443
0
  lp->y = sy / (6 * largestArea);
1444
1445
0
  return MS_SUCCESS;
1446
0
}
1447
1448
int msGetPolygonCentroid(shapeObj *p, pointObj *lp, double *miny,
1449
0
                         double *maxy) {
1450
0
  int i, j;
1451
0
  double cent_weight_x = 0.0, cent_weight_y = 0.0;
1452
0
  double len, total_len = 0;
1453
1454
0
  *miny = *maxy = p->line[0].point[0].y;
1455
0
  for (i = 0; i < p->numlines; i++) {
1456
0
    for (j = 1; j < p->line[i].numpoints; j++) {
1457
0
      *miny = MS_MIN(*miny, p->line[i].point[j].y);
1458
0
      *maxy = MS_MAX(*maxy, p->line[i].point[j].y);
1459
0
      len = msDistancePointToPoint(&(p->line[i].point[j - 1]),
1460
0
                                   &(p->line[i].point[j]));
1461
0
      cent_weight_x +=
1462
0
          len * ((p->line[i].point[j - 1].x + p->line[i].point[j].x) / 2);
1463
0
      cent_weight_y +=
1464
0
          len * ((p->line[i].point[j - 1].y + p->line[i].point[j].y) / 2);
1465
0
      total_len += len;
1466
0
    }
1467
0
  }
1468
1469
0
  if (total_len == 0)
1470
0
    return (MS_FAILURE);
1471
1472
0
  lp->x = cent_weight_x / total_len;
1473
0
  lp->y = cent_weight_y / total_len;
1474
1475
0
  return (MS_SUCCESS);
1476
0
}
1477
1478
/*
1479
** Find a label point in a polygon.
1480
*/
1481
0
int msPolygonLabelPoint(shapeObj *p, pointObj *lp, double min_dimension) {
1482
0
  double slope;
1483
0
  pointObj *point1 = NULL, *point2 = NULL, cp;
1484
0
  int i, j, nfound;
1485
0
  double x, y, *intersect, temp;
1486
0
  double min, max;
1487
0
  int wrong_order, n;
1488
0
  double len, max_len = 0;
1489
0
  double minx, maxx, maxy, miny;
1490
1491
#ifdef notdef
1492
  int method = 2;
1493
#endif
1494
1495
0
  msComputeBounds(p);
1496
0
  minx = p->bounds.minx;
1497
0
  miny = p->bounds.miny;
1498
0
  maxx = p->bounds.maxx;
1499
0
  maxy = p->bounds.maxy;
1500
1501
0
  if (min_dimension > 0)
1502
0
    if (MS_MIN(maxx - minx, maxy - miny) < min_dimension)
1503
0
      return (MS_FAILURE);
1504
1505
0
  cp.x = (maxx + minx) / 2.0;
1506
0
  cp.y = (maxy + miny) / 2.0;
1507
1508
#ifdef notdef
1509
  switch (method) {
1510
  case 0: /* MBR */
1511
    lp->x = cp.x;
1512
    lp->y = cp.y;
1513
    break;
1514
  case 1: /* centroid */
1515
    if (msGetPolygonCentroid(p, lp, &miny, &maxy) != MS_SUCCESS)
1516
      return (MS_FAILURE);
1517
    break;
1518
  case 2: /* center of gravity */
1519
    if (getPolygonCenterOfGravity(p, lp) != MS_SUCCESS)
1520
      return (MS_FAILURE);
1521
    break;
1522
  }
1523
#else
1524
0
  if (getPolygonCenterOfGravity(p, lp) != MS_SUCCESS)
1525
0
    return (MS_FAILURE);
1526
0
#endif
1527
1528
0
  if (msIntersectPointPolygon(lp, p) == MS_TRUE) {
1529
0
    double dist, min_dist = -1;
1530
1531
    /* compute a distance to the polygon */
1532
0
    for (j = 0; j < p->numlines; j++) {
1533
0
      for (i = 1; i < p->line[j].numpoints; i++) {
1534
0
        dist = msSquareDistancePointToSegment(lp, &(p->line[j].point[i - 1]),
1535
0
                                              &(p->line[j].point[i]));
1536
0
        if ((dist < min_dist) || (min_dist < 0))
1537
0
          min_dist = dist;
1538
0
      }
1539
0
    }
1540
0
    min_dist = sqrt(min_dist);
1541
1542
0
    if (min_dist > .1 * MS_MAX(maxx - minx, maxy - miny))
1543
0
      return (MS_SUCCESS); /* point is not too close to the edge */
1544
0
  }
1545
1546
  /* printf("label: %s\n", p->text);
1547
     printf("    bbox: %g %g %g %g\n",minx, miny, maxx, maxy);
1548
     printf("    center: %g %g\n", cp.x, cp.y);
1549
     printf("    center of gravity: %g %g\n", lp->x, lp->y);
1550
     printf("    dx: %g, dy: %g\n", lp->x-cp.x, lp->y-cp.y);
1551
     printf("    distance to parent shape: %g\n", min_dist);
1552
     return MS_SUCCESS; */
1553
1554
0
  n = 0;
1555
0
  for (j = 0; j < p->numlines; j++) /* count total number of points */
1556
0
    n += p->line[j].numpoints;
1557
0
  intersect = (double *)calloc(n, sizeof(double));
1558
0
  MS_CHECK_ALLOC(intersect, n * sizeof(double), MS_FAILURE);
1559
1560
0
  if (MS_ABS((int)lp->x - (int)cp.x) >
1561
0
      MS_ABS((int)lp->y - (int)cp.y)) { /* center horizontally, fix y */
1562
1563
0
    y = lp->y;
1564
1565
    /* need to find a y that won't intersect any vertices exactly */
1566
0
    max = y - 1; /* first initializing min, max to be any 2 pnts on either side
1567
                    of y */
1568
0
    min = y + 1;
1569
0
    for (j = 0; j < p->numlines; j++) {
1570
0
      if ((min < y) && (max >= y))
1571
0
        break;
1572
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1573
0
        if ((min < y) && (max >= y))
1574
0
          break;
1575
0
        if (p->line[j].point[i].y < y)
1576
0
          min = p->line[j].point[i].y;
1577
0
        if (p->line[j].point[i].y >= y)
1578
0
          max = p->line[j].point[i].y;
1579
0
      }
1580
0
    }
1581
1582
0
    n = 0;
1583
0
    for (j = 0; j < p->numlines; j++) {
1584
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1585
0
        if ((p->line[j].point[i].y < y) &&
1586
0
            ((y - p->line[j].point[i].y) < (y - min)))
1587
0
          min = p->line[j].point[i].y;
1588
0
        if ((p->line[j].point[i].y >= y) &&
1589
0
            ((p->line[j].point[i].y - y) < (max - y)))
1590
0
          max = p->line[j].point[i].y;
1591
0
      }
1592
0
    }
1593
1594
0
    if (min == max) {
1595
0
      msFree(intersect);
1596
0
      return (MS_FAILURE);
1597
0
    } else
1598
0
      y = (max + min) / 2.0;
1599
1600
0
    nfound = 0;
1601
0
    for (j = 0; j < p->numlines; j++) { /* for each line */
1602
1603
0
      point1 = &(p->line[j].point[p->line[j].numpoints - 1]);
1604
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1605
0
        point2 = &(p->line[j].point[i]);
1606
1607
0
        if (EDGE_CHECK(point1->y, y, point2->y) == CLIP_MIDDLE) {
1608
1609
0
          if (point1->y == point2->y)
1610
0
            continue; /* ignore horizontal edges */
1611
0
          else
1612
0
            slope = (point2->x - point1->x) / (point2->y - point1->y);
1613
1614
0
          x = point1->x + (y - point1->y) * slope;
1615
0
          intersect[nfound++] = x;
1616
0
        } /* end checking this edge */
1617
1618
0
        point1 = point2; /* next edge */
1619
0
      }
1620
0
    } /* finished line */
1621
1622
    /* sort the intersections */
1623
0
    do {
1624
0
      wrong_order = 0;
1625
0
      for (i = 0; i < nfound - 1; i++) {
1626
0
        if (intersect[i] > intersect[i + 1]) {
1627
0
          wrong_order = 1;
1628
0
          SWAP(intersect[i], intersect[i + 1], temp);
1629
0
        }
1630
0
      }
1631
0
    } while (wrong_order);
1632
1633
    /* find longest span */
1634
0
    for (i = 0; i < nfound; i += 2) {
1635
0
      len = fabs(intersect[i] - intersect[i + 1]);
1636
0
      if (len > max_len) {
1637
0
        max_len = len;
1638
0
        lp->x = (intersect[i] + intersect[i + 1]) / 2;
1639
0
        lp->y = y;
1640
0
      }
1641
0
    }
1642
0
  } else { /* center vertically, fix x */
1643
0
    x = lp->x;
1644
1645
    /* need to find a x that won't intersect any vertices exactly */
1646
0
    max = x - 1; /* first initializing min, max to be any 2 pnts on either side
1647
                    of x */
1648
0
    min = x + 1;
1649
0
    for (j = 0; j < p->numlines; j++) {
1650
0
      if ((min < x) && (max >= x))
1651
0
        break;
1652
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1653
0
        if ((min < x) && (max >= x))
1654
0
          break;
1655
0
        if (p->line[j].point[i].x < x)
1656
0
          min = p->line[j].point[i].x;
1657
0
        if (p->line[j].point[i].x >= x)
1658
0
          max = p->line[j].point[i].x;
1659
0
      }
1660
0
    }
1661
1662
0
    n = 0;
1663
0
    for (j = 0; j < p->numlines; j++) {
1664
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1665
0
        if ((p->line[j].point[i].x < x) &&
1666
0
            ((x - p->line[j].point[i].x) < (x - min)))
1667
0
          min = p->line[j].point[i].x;
1668
0
        if ((p->line[j].point[i].x >= x) &&
1669
0
            ((p->line[j].point[i].x - x) < (max - x)))
1670
0
          max = p->line[j].point[i].x;
1671
0
      }
1672
0
    }
1673
1674
0
    if (min == max) {
1675
0
      msFree(intersect);
1676
0
      return (MS_FAILURE);
1677
0
    } else
1678
0
      x = (max + min) / 2.0;
1679
1680
0
    nfound = 0;
1681
0
    for (j = 0; j < p->numlines; j++) { /* for each line */
1682
1683
0
      point1 = &(p->line[j].point[p->line[j].numpoints - 1]);
1684
0
      for (i = 0; i < p->line[j].numpoints; i++) {
1685
0
        point2 = &(p->line[j].point[i]);
1686
1687
0
        if (EDGE_CHECK(point1->x, x, point2->x) == CLIP_MIDDLE) {
1688
1689
0
          if (point1->x == point2->x)
1690
0
            continue; /* ignore vertical edges */
1691
0
          else if (point1->y == point2->y)
1692
0
            y = point1->y; /* for a horizontal edge we know y */
1693
0
          else {
1694
0
            slope = (point2->x - point1->x) / (point2->y - point1->y);
1695
0
            y = (x - point1->x) / slope + point1->y;
1696
0
          }
1697
1698
0
          intersect[nfound++] = y;
1699
0
        } /* end checking this edge */
1700
1701
0
        point1 = point2; /* next edge */
1702
0
      }
1703
0
    } /* finished line */
1704
1705
    /* sort the intersections */
1706
0
    do {
1707
0
      wrong_order = 0;
1708
0
      for (i = 0; i < nfound - 1; i++) {
1709
0
        if (intersect[i] > intersect[i + 1]) {
1710
0
          wrong_order = 1;
1711
0
          SWAP(intersect[i], intersect[i + 1], temp);
1712
0
        }
1713
0
      }
1714
0
    } while (wrong_order);
1715
1716
    /* find longest span */
1717
0
    for (i = 0; i < nfound; i += 2) {
1718
0
      len = fabs(intersect[i] - intersect[i + 1]);
1719
0
      if (len > max_len) {
1720
0
        max_len = len;
1721
0
        lp->y = (intersect[i] + intersect[i + 1]) / 2;
1722
0
        lp->x = x;
1723
0
      }
1724
0
    }
1725
0
  }
1726
1727
0
  free(intersect);
1728
1729
0
  if (max_len > 0)
1730
0
    return (MS_SUCCESS);
1731
0
  else
1732
0
    return (MS_FAILURE);
1733
0
}
1734
1735
/* Compute all the lineString/segment lengths and determine the longest
1736
 * lineString of a multiLineString shape: in parameter, the multiLineString to
1737
 * compute. struct polyline_lengths pll: out parameter, all line and segment
1738
 * lengths
1739
 */
1740
void msPolylineComputeLineSegments(shapeObj *shape,
1741
0
                                   struct polyline_lengths *pll) {
1742
0
  int i, j;
1743
0
  double max_line_length = -1, max_segment_length = -1, segment_length;
1744
1745
0
  pll->ll = (struct line_lengths *)msSmallMalloc(shape->numlines *
1746
0
                                                 sizeof(struct line_lengths));
1747
0
  pll->total_length = 0;
1748
0
  pll->longest_line_index = 0;
1749
1750
0
  for (i = 0; i < shape->numlines; i++) {
1751
0
    struct line_lengths *ll = &pll->ll[i];
1752
0
    double max_subline_segment_length = -1;
1753
1754
0
    if (shape->line[i].numpoints > 1) {
1755
0
      ll->segment_lengths = (double *)msSmallMalloc(
1756
0
          sizeof(double) * (shape->line[i].numpoints - 1));
1757
0
    } else {
1758
0
      ll->segment_lengths = NULL;
1759
0
    }
1760
0
    ll->total_length = 0;
1761
1762
0
    for (j = 1; j < shape->line[i].numpoints; j++) {
1763
0
      segment_length =
1764
0
          sqrt((((shape->line[i].point[j].x - shape->line[i].point[j - 1].x) *
1765
0
                 (shape->line[i].point[j].x - shape->line[i].point[j - 1].x)) +
1766
0
                ((shape->line[i].point[j].y - shape->line[i].point[j - 1].y) *
1767
0
                 (shape->line[i].point[j].y - shape->line[i].point[j - 1].y))));
1768
0
      ll->total_length += segment_length;
1769
0
      ll->segment_lengths[j - 1] = segment_length;
1770
0
      if (segment_length > max_subline_segment_length) {
1771
0
        max_subline_segment_length = segment_length;
1772
0
        ll->longest_segment_index = j;
1773
0
      }
1774
0
      if (segment_length > max_segment_length) {
1775
0
        max_segment_length = segment_length;
1776
0
        pll->longest_segment_line_index = i;
1777
0
        pll->longest_segment_point_index = j;
1778
0
      }
1779
0
    }
1780
0
    pll->total_length += ll->total_length;
1781
1782
0
    if (ll->total_length > max_line_length) {
1783
0
      max_line_length = ll->total_length;
1784
0
      pll->longest_line_index = i;
1785
0
    }
1786
0
  }
1787
0
}
1788
1789
/*
1790
** If no repeatdistance, find center of longest segment in polyline p. The
1791
*polyline must have been converted
1792
** to image coordinates before calling this function.
1793
*/
1794
int msPolylineLabelPoint(mapObj *map, shapeObj *p, textSymbolObj *ts,
1795
                         labelObj *label, struct label_auto_result *lar,
1796
0
                         double resolutionfactor) {
1797
0
  struct polyline_lengths pll;
1798
0
  int i, ret = MS_SUCCESS;
1799
0
  double minfeaturesize = -1;
1800
0
  assert(ts == NULL || ts->annotext);
1801
1802
0
  if (label && ts) {
1803
0
    if (label->autominfeaturesize) {
1804
0
      if (!ts->textpath) {
1805
0
        if (MS_UNLIKELY(MS_FAILURE == msComputeTextPath(map, ts)))
1806
0
          return MS_FAILURE;
1807
0
      }
1808
0
      if (!ts->textpath)
1809
0
        return MS_FAILURE;
1810
0
      minfeaturesize = ts->textpath->bounds.bbox.maxx;
1811
0
    } else if (label->minfeaturesize) {
1812
0
      minfeaturesize = label->minfeaturesize * resolutionfactor;
1813
0
    }
1814
0
  }
1815
1816
  /* compute line lengths, in order to:
1817
    - extract the longest line if we're not repeating
1818
    - check that each line is longer than the text length if using
1819
    minfeaturesize auto
1820
    - check that each line is long enough if using minfeaturesize
1821
   */
1822
0
  msPolylineComputeLineSegments(p, &pll);
1823
1824
0
  if (label && label->repeatdistance > 0) {
1825
0
    for (i = 0; i < p->numlines; i++) {
1826
0
      if (pll.ll[i].total_length > minfeaturesize) {
1827
0
        ret = msLineLabelPoint(map, &p->line[i], ts, &pll.ll[i], lar, label,
1828
0
                               resolutionfactor);
1829
0
      }
1830
0
    }
1831
0
  } else {
1832
0
    i = pll.longest_line_index;
1833
0
    if (pll.ll[i].total_length > minfeaturesize) {
1834
0
      ret = msLineLabelPoint(map, &p->line[i], ts, &pll.ll[i], lar, label,
1835
0
                             resolutionfactor);
1836
0
    }
1837
0
  }
1838
1839
  /* freeing memory: allocated by msPolylineComputeLineSegments */
1840
0
  for (i = 0; i < p->numlines; i++) {
1841
0
    free(pll.ll[i].segment_lengths);
1842
0
  }
1843
0
  free(pll.ll);
1844
1845
0
  return ret;
1846
0
}
1847
1848
int msLineLabelPoint(mapObj *map, lineObj *p, textSymbolObj *ts,
1849
                     struct line_lengths *ll, struct label_auto_result *lar,
1850
0
                     labelObj *label, double resolutionfactor) {
1851
0
  (void)map;
1852
0
  int j, l, n, point_repeat;
1853
0
  double t, theta, fwd_length, point_distance;
1854
0
  double center_point_position, left_point_position, right_point_position,
1855
0
      point_position;
1856
0
  double repeat_distance = -1;
1857
0
  if (label) {
1858
0
    repeat_distance = label->repeatdistance * resolutionfactor;
1859
0
  }
1860
1861
0
  if (MS_UNLIKELY(p->numpoints < 2))
1862
0
    return MS_FAILURE;
1863
0
  point_distance = 0;
1864
0
  point_repeat = 1;
1865
0
  left_point_position = right_point_position = center_point_position =
1866
0
      ll->total_length / 2.0;
1867
1868
0
  if (repeat_distance > 0) {
1869
0
    point_repeat = ll->total_length / repeat_distance;
1870
1871
0
    if (point_repeat > 1) {
1872
0
      if (point_repeat % 2 == 0)
1873
0
        point_repeat -= 1;
1874
0
      point_distance =
1875
0
          (ll->total_length / point_repeat); /* buffer allowed per point */
1876
1877
      /* initial point position */
1878
0
      left_point_position -= ((point_repeat - 1) / 2 * point_distance);
1879
0
      right_point_position += ((point_repeat - 1) / 2 * point_distance);
1880
1881
0
      point_repeat = (point_repeat - 1) / 2 + 1;
1882
0
    } else
1883
0
      point_repeat = 1;
1884
0
  }
1885
1886
0
  for (l = 0; l < point_repeat; ++l) {
1887
0
    if (l ==
1888
0
        point_repeat - 1) { /* last point to place is always the center point */
1889
0
      point_position = center_point_position;
1890
0
      n = 1;
1891
0
    } else {
1892
0
      point_position = right_point_position;
1893
0
      n = 0;
1894
0
    }
1895
1896
0
    do {
1897
0
      lar->angles = (double *)msSmallRealloc(
1898
0
          lar->angles, (lar->num_label_points + 1) * sizeof(double));
1899
0
      lar->label_points = (pointObj *)msSmallRealloc(
1900
0
          lar->label_points, (lar->num_label_points + 1) * sizeof(pointObj));
1901
1902
      /* if there is only 1 label to place... put it in the middle of the
1903
       * current segment (as old behavior) */
1904
0
      if (point_repeat == 1) {
1905
0
        j = ll->longest_segment_index;
1906
0
        lar->label_points[lar->num_label_points].x =
1907
0
            (p->point[j - 1].x + p->point[j].x) / 2.0;
1908
0
        lar->label_points[lar->num_label_points].y =
1909
0
            (p->point[j - 1].y + p->point[j].y) / 2.0;
1910
0
      } else {
1911
0
        j = 0;
1912
0
        fwd_length = 0;
1913
0
        while (fwd_length < point_position) {
1914
0
          fwd_length += ll->segment_lengths[j++];
1915
0
        }
1916
0
        assert(j > 0);
1917
1918
0
        t = 1 - (fwd_length - point_position) / ll->segment_lengths[j - 1];
1919
0
        lar->label_points[lar->num_label_points].x =
1920
0
            t * (p->point[j].x - p->point[j - 1].x) + p->point[j - 1].x;
1921
0
        lar->label_points[lar->num_label_points].y =
1922
0
            t * (p->point[j].y - p->point[j - 1].y) + p->point[j - 1].y;
1923
0
      }
1924
1925
0
      if (label && ts) {
1926
0
        if (label->anglemode != MS_NONE) {
1927
0
          theta = atan2(p->point[j].x - p->point[j - 1].x,
1928
0
                        p->point[j].y - p->point[j - 1].y);
1929
0
          if (label->anglemode == MS_AUTO2) {
1930
0
            theta -= MS_PI2;
1931
0
          } else {                                   /* AUTO, FOLLOW */
1932
0
            if (p->point[j - 1].x < p->point[j].x) { /* i.e. to the left */
1933
0
              theta -= MS_PI2;
1934
0
            } else {
1935
0
              theta += MS_PI2;
1936
0
            }
1937
0
          }
1938
0
          lar->angles[lar->num_label_points] = theta;
1939
0
        } else {
1940
0
          lar->angles[lar->num_label_points] = MS_DEG_TO_RAD * ts->label->angle;
1941
0
        }
1942
0
      } else {
1943
0
        lar->angles[lar->num_label_points] = 0;
1944
0
      }
1945
0
      lar->num_label_points++;
1946
1947
0
      point_position = left_point_position;
1948
0
      n++;
1949
0
    } while (n < 2); /* we place the right point then the left point. */
1950
1951
0
    right_point_position -= point_distance;
1952
0
    left_point_position += point_distance;
1953
0
  }
1954
1955
0
  return MS_SUCCESS;
1956
0
}
1957
1958
/* Calculate the labelpath for each line if repeatdistance is enabled, else the
1959
 * labelpath of the longest line segment */
1960
int msPolylineLabelPath(mapObj *map, imageObj *image, shapeObj *p,
1961
                        textSymbolObj *ts, labelObj *label,
1962
0
                        struct label_follow_result *lfr) {
1963
0
  struct polyline_lengths pll;
1964
0
  int i, ret = MS_SUCCESS;
1965
0
  double minfeaturesize = -1;
1966
0
  assert(ts->annotext);
1967
0
  lfr->num_follow_labels = lfr->lar.num_label_points = 0;
1968
1969
  /* we first offset the line if we want an offsetted label */
1970
0
  if (label->offsetx != 0 && IS_PERPENDICULAR_OFFSET(label->offsety)) {
1971
0
    double offset;
1972
0
    if (label->offsetx > 0) {
1973
0
      offset = label->offsetx + label->size / 2;
1974
0
    } else {
1975
0
      offset = label->offsetx - label->size / 2;
1976
0
    }
1977
0
    if (label->offsety == MS_LABEL_PERPENDICULAR_TOP_OFFSET &&
1978
0
        p->numlines > 0 && p->line[0].numpoints > 0) {
1979
      /* is the line mostly left-to-right or right-to-left ?
1980
       * FIXME this should be done line by line, by stepping through
1981
       * shape->lines, however the OffsetPolyline function works on shapeObjs,
1982
       * not lineObjs we only check the first line
1983
       */
1984
0
      if (p->line[0].point[0].x <
1985
0
          p->line[0].point[p->line[0].numpoints - 1].x) {
1986
        /* line is left to right */
1987
0
        offset = -offset;
1988
0
      }
1989
0
    }
1990
0
    p = msOffsetPolyline(p, offset, MS_STYLE_SINGLE_SIDED_OFFSET);
1991
0
    if (!p)
1992
0
      return MS_FAILURE;
1993
0
  }
1994
1995
  /* compute line lengths, in order to:
1996
    - extract the longest line if we're not repeating
1997
    - check that each line is longer than the text length if using
1998
    minfeaturesize auto
1999
    - check that each line is long enough if using minfeaturesize
2000
   */
2001
0
  msPolylineComputeLineSegments(p, &pll);
2002
2003
0
  if (label->autominfeaturesize) {
2004
0
    if (!ts->textpath) {
2005
0
      if (MS_UNLIKELY(MS_FAILURE == msComputeTextPath(map, ts))) {
2006
0
        return MS_FAILURE;
2007
0
      }
2008
0
    }
2009
0
    if (!ts->textpath)
2010
0
      return MS_FAILURE;
2011
0
    minfeaturesize = ts->textpath->bounds.bbox.maxx;
2012
0
  } else if (label->minfeaturesize) {
2013
0
    minfeaturesize = label->minfeaturesize * image->resolutionfactor;
2014
0
  }
2015
2016
0
  if (label->repeatdistance > 0) {
2017
0
    for (i = 0; i < p->numlines; i++) {
2018
0
      if (pll.ll[i].total_length > minfeaturesize) {
2019
0
        ret = msLineLabelPath(map, image, &p->line[i], ts, &pll.ll[i], lfr,
2020
0
                              label);
2021
0
      }
2022
0
    }
2023
0
  } else {
2024
0
    i = pll.longest_line_index;
2025
0
    if (pll.ll[i].total_length > minfeaturesize) {
2026
0
      ret =
2027
0
          msLineLabelPath(map, image, &p->line[i], ts, &pll.ll[i], lfr, label);
2028
0
    }
2029
0
  }
2030
2031
  /* freeing memory: allocated by msPolylineComputeLineSegments */
2032
0
  for (i = 0; i < p->numlines; i++) {
2033
0
    free(pll.ll[i].segment_lengths);
2034
0
  }
2035
0
  free(pll.ll);
2036
2037
0
  if (IS_PERPENDICULAR_OFFSET(label->offsety) && label->offsetx != 0) {
2038
0
    msFreeShape(p);
2039
0
    msFree(p);
2040
0
  }
2041
0
  return ret;
2042
0
}
2043
2044
static double compute_retry_offset(textSymbolObj *ts, int fail_idx,
2045
                                   double retried_offset, double max_dec_offset,
2046
0
                                   double max_inc_offset) {
2047
  /* fail_idx: the glyph index in the textpath that originally failed (i.e.
2048
   * before any displacement was tested */
2049
  /* retried_offset: the last offset that was tried and is failing, so we can
2050
   * return the next one (or 0 if none left)*/
2051
0
  int inc = 1, dec_found = 0, inc_found = 0;
2052
0
  double inc_offset = 0, dec_offset = 0;
2053
2054
0
  retried_offset = fabs(retried_offset);
2055
0
  do {
2056
0
    if (fail_idx - inc - 1 >= 0) {
2057
0
      dec_offset += ts->textpath->glyphs[fail_idx - inc].glyph->metrics.advance;
2058
0
      if (dec_offset > max_dec_offset)
2059
0
        break;
2060
0
      if (dec_offset > retried_offset &&
2061
0
          msIsGlyphASpace(&ts->textpath->glyphs[fail_idx - inc - 1])) {
2062
0
        dec_found = 1;
2063
0
        dec_offset +=
2064
0
            ts->textpath->glyphs[fail_idx - inc - 1].glyph->metrics.advance /
2065
0
            2.0;
2066
0
        break;
2067
0
      }
2068
0
    }
2069
0
    inc++;
2070
0
  } while (dec_offset < max_dec_offset && fail_idx - inc > 0);
2071
2072
0
  if (!dec_found) {
2073
    /* try the starting position */
2074
0
    dec_offset += ts->textpath->glyphs[0].glyph->metrics.advance;
2075
0
    if (dec_offset > retried_offset && dec_offset < max_dec_offset) {
2076
0
      dec_found = 1;
2077
0
    }
2078
0
  }
2079
2080
0
  inc = 1;
2081
0
  inc_offset = ts->textpath->glyphs[fail_idx].glyph->metrics.advance;
2082
0
  do {
2083
0
    if (fail_idx + inc < ts->textpath->numglyphs - 1) {
2084
0
      if (inc_offset > retried_offset &&
2085
0
          msIsGlyphASpace(&ts->textpath->glyphs[fail_idx + inc])) {
2086
0
        inc_offset +=
2087
0
            ts->textpath->glyphs[fail_idx + inc].glyph->metrics.advance / 2;
2088
0
        if (inc_offset < max_inc_offset)
2089
0
          inc_found = 1;
2090
0
        break;
2091
0
      }
2092
0
      inc_offset += ts->textpath->glyphs[fail_idx + inc].glyph->metrics.advance;
2093
0
    }
2094
0
    inc++;
2095
0
  } while (inc_offset < max_inc_offset &&
2096
0
           fail_idx + inc < ts->textpath->numglyphs - 1);
2097
0
  if (!inc_found) {
2098
0
    inc_offset += ts->textpath->glyphs[ts->textpath->numglyphs - 1]
2099
0
                      .glyph->metrics.advance;
2100
0
    if (inc_offset > retried_offset && inc_offset < max_inc_offset) {
2101
0
      inc_found = 1;
2102
0
    }
2103
0
  }
2104
2105
0
  if (!inc_found && !dec_found)
2106
0
    return 0.0;
2107
0
  if (inc_found && dec_found) {
2108
0
    if (inc_offset < dec_offset)
2109
0
      return -inc_offset;
2110
0
    else
2111
0
      return dec_offset;
2112
0
  }
2113
0
  if (inc_found)
2114
0
    return -inc_offset;
2115
0
  else
2116
0
    return dec_offset;
2117
0
}
2118
/*
2119
 * Calculate a series of label points for each character in the label for a
2120
 * given line. The resultant series of points is stored in *labelpath,
2121
 * which is added to the labelpaths array. Note that the points and bounds
2122
 * are allocated in this function.  The polyline must be converted to image
2123
 * coordinates before calling this function.
2124
 */
2125
int msLineLabelPath(mapObj *map, imageObj *img, lineObj *p, textSymbolObj *ts,
2126
                    struct line_lengths *ll, struct label_follow_result *lfr,
2127
0
                    labelObj *label) {
2128
0
  double distance_along_segment;
2129
0
  double repeat_distance = label->repeatdistance * ts->resolutionfactor;
2130
0
  double segment_length, fwd_line_length, rev_line_length, text_length,
2131
0
      text_start_length, label_buffer, text_end_length, cur_label_position,
2132
0
      first_label_position;
2133
0
  double max_retry_offset;
2134
2135
0
  int j, k, l, inc, final_j, label_repeat;
2136
0
  double direction;
2137
0
  rectObj bbox;
2138
0
  lineObj *bounds;
2139
0
  double t;
2140
0
  double cx, cy; /* centre of a character, x & y values. */
2141
2142
0
  double theta;
2143
0
  double dx, dy, w, cos_t, sin_t;
2144
2145
0
  const double letterspacing = 1.00;
2146
  /* As per RFC 60, if label->maxoverlapangle == 0 then fall back on pre-6.0
2147
     behavior which was to use maxoverlapangle = 0.4*MS_PI ( 40% of 180 degrees
2148
     ) */
2149
0
  double maxoverlapangle = 0.4 * MS_PI;
2150
2151
0
  if (p->numpoints < 2) { /* degenerate */
2152
0
    return MS_FAILURE;
2153
0
  }
2154
0
  if (p->numpoints == 2) /* use the regular angled text algorithm */
2155
0
    return msLineLabelPoint(map, p, ts, ll, &lfr->lar, label,
2156
0
                            img->resolutionfactor);
2157
2158
0
  if (!ts->textpath) {
2159
0
    if (MS_UNLIKELY(MS_FAILURE == msComputeTextPath(map, ts))) {
2160
0
      return MS_FAILURE;
2161
0
    }
2162
0
  }
2163
0
  if (!ts->textpath) {
2164
0
    return MS_FAILURE;
2165
0
  }
2166
2167
  /* skip the label and use the normal algorithm if it has fewer than 2
2168
   * characters */
2169
0
  if (ts->textpath->numglyphs < 2)
2170
0
    return msLineLabelPoint(map, p, ts, ll, &lfr->lar, label,
2171
0
                            img->resolutionfactor);
2172
2173
0
  text_length = letterspacing * ts->textpath->bounds.bbox.maxx;
2174
2175
  /*
2176
  ** if the text length is way longer than the line, skip adding the
2177
  ** label if it isn't forced (long extrapolated labels tend to be ugly)
2178
  */
2179
0
  if (text_length > 1.5 * ll->total_length) {
2180
0
    if (ts->label->force == MS_FALSE) {
2181
0
      return MS_SUCCESS;
2182
0
    } else {
2183
0
      repeat_distance = 0; /* disable repetition */
2184
0
    }
2185
0
  }
2186
2187
  /* We compute the number of labels we can repeat in the line:
2188
     - space occupied by one label
2189
     - plus N times space occupied by one label + repeat_distance interspacing
2190
   */
2191
0
  label_buffer =
2192
0
      text_length + repeat_distance; /* distance occupied by one label +
2193
                                        inter-repeat spacing */
2194
0
  if (repeat_distance > 0 && ll->total_length > text_length) {
2195
0
    label_repeat = 1 + (int)((ll->total_length - text_length) / label_buffer);
2196
0
    max_retry_offset = repeat_distance / 2.1;
2197
0
  } else {
2198
0
    label_repeat = 1;
2199
0
    max_retry_offset = (ll->total_length - text_length) / 2.1;
2200
0
  }
2201
  /* compute the starting point of where we'll be placing the first label */
2202
0
  if (label_repeat % 2) {
2203
    /* odd number of labels: account for the center label that will be centered
2204
     * on the line
2205
     * ----llll_____llll_____llll----
2206
     */
2207
0
    first_label_position = (ll->total_length - text_length) / 2.0 -
2208
0
                           (label_repeat / 2) * label_buffer;
2209
0
  } else {
2210
    /* even number of labels:
2211
     * repeat_distance is centered on the line
2212
     * space for one label
2213
     * eventually space for label_repeat*2-1 labels+spacing
2214
     * ----llll_____llll----
2215
     * --llll_____llll_____llll_____llll--
2216
     */
2217
0
    first_label_position = (ll->total_length - repeat_distance) / 2.0 -
2218
0
                           text_length - (label_repeat / 2 - 1) * label_buffer;
2219
0
  }
2220
0
  if (label->maxoverlapangle > 0)
2221
0
    maxoverlapangle = label->maxoverlapangle * MS_DEG_TO_RAD; /* radian */
2222
2223
0
  cur_label_position = first_label_position;
2224
0
  for (l = 0; l < label_repeat; l++) {
2225
2226
0
    double retry_offset = 0.0;
2227
0
    int first_retry_idx = 0;
2228
    // max_dec_retry_offset = max_inc_retry_offset = max_retry_offset;
2229
0
    textPathObj *tp = (textPathObj *)msSmallCalloc(1, sizeof(textPathObj));
2230
    /* copy the textPath, we will be overriding the copy's positions and angles
2231
     */
2232
0
    msCopyTextPath(tp, ts->textpath);
2233
0
    tp->bounds.poly = (lineObj *)msSmallMalloc(sizeof(lineObj));
2234
0
    bounds = tp->bounds.poly;
2235
2236
    /*
2237
    ** The bounds will have two points for each character plus an endpoint:
2238
    ** the UL corners of each bbox will be tied together and the LL corners
2239
    ** will be tied together.
2240
    */
2241
0
    bounds->numpoints = 2 * tp->numglyphs + 1;
2242
0
    bounds->point =
2243
0
        (pointObj *)msSmallMalloc(sizeof(pointObj) * bounds->numpoints);
2244
2245
0
    do {
2246
0
      int overlap_collision_detected = 0;
2247
0
      text_start_length = cur_label_position + retry_offset;
2248
0
      text_end_length = 0;
2249
2250
0
      for (k = 0; k < tp->numglyphs; k++) {
2251
0
        tp->glyphs[k].pnt.x = 0;
2252
0
        tp->glyphs[k].pnt.y = 0;
2253
0
      }
2254
2255
      /* the points start at (line_length - text_length) / 2 in order to be
2256
       * centred */
2257
      /* text_start_length = (line_length - text_length) / 2.0; */
2258
2259
0
      j = 0;
2260
0
      fwd_line_length = 0;
2261
0
      if (text_start_length >= 0.0) {
2262
0
        while (fwd_line_length < text_start_length)
2263
0
          fwd_line_length += ll->segment_lengths[j++];
2264
2265
0
        j--;
2266
0
      }
2267
0
      final_j = p->numpoints - 1;
2268
0
      rev_line_length = 0;
2269
0
      if (text_start_length + text_length <= ll->total_length) {
2270
0
        text_end_length = ll->total_length - (text_start_length + text_length);
2271
0
        while (rev_line_length < text_end_length) {
2272
0
          rev_line_length += ll->segment_lengths[final_j - 1];
2273
0
          final_j--;
2274
0
        }
2275
0
        final_j++;
2276
0
      }
2277
2278
0
      if (final_j == 0)
2279
0
        final_j = 1;
2280
2281
      /* determine if the line is mostly left to right or right to left,
2282
       see bug 1620 discussion by Steve Woodbridge */
2283
0
      direction = p->point[final_j].x - p->point[j].x;
2284
2285
0
      if (direction > 0) {
2286
0
        inc = 1; /* j is already correct */
2287
2288
        /* length of the segment containing the starting point */
2289
0
        segment_length = ll->segment_lengths[j];
2290
2291
        /* determine how far along the segment we need to go */
2292
0
        if (text_start_length < 0.0)
2293
0
          t = text_start_length / segment_length;
2294
0
        else
2295
0
          t = 1 - (fwd_line_length - text_start_length) / segment_length;
2296
0
      } else {
2297
0
        j = final_j;
2298
0
        inc = -1;
2299
2300
        /* length of the segment containing the starting point */
2301
0
        segment_length = ll->segment_lengths[j - 1];
2302
0
        if (text_start_length < 0.0)
2303
0
          t = text_start_length / segment_length;
2304
0
        else
2305
0
          t = 1 - (rev_line_length - text_end_length) / segment_length;
2306
0
      }
2307
2308
0
      distance_along_segment = t * segment_length; /* starting point */
2309
2310
0
      theta = 0;
2311
0
      k = 0;
2312
0
      w = 0;
2313
0
      while (k < tp->numglyphs) {
2314
0
        double x, y;
2315
2316
0
        x = t * (p->point[j + inc].x - p->point[j].x) + p->point[j].x;
2317
0
        y = t * (p->point[j + inc].y - p->point[j].y) + p->point[j].y;
2318
0
        tp->glyphs[k].pnt.x = x;
2319
0
        tp->glyphs[k].pnt.y = y;
2320
2321
0
        w = letterspacing * tp->glyphs[k].glyph->metrics.advance;
2322
2323
        /* add the character's width to the distance along the line */
2324
0
        distance_along_segment += w;
2325
2326
        /* if we still have segments left and we've past the current segment,
2327
         * move to the next one */
2328
0
        if (inc == 1 && j < p->numpoints - 2) {
2329
2330
0
          while (j < p->numpoints - 2 &&
2331
0
                 distance_along_segment > ll->segment_lengths[j]) {
2332
0
            distance_along_segment -= ll->segment_lengths[j];
2333
0
            j += inc; /* move to next segment */
2334
0
          }
2335
2336
0
          segment_length = ll->segment_lengths[j];
2337
2338
0
        } else if (inc == -1 && j > 1) {
2339
2340
0
          while (j > 1 && distance_along_segment > ll->segment_lengths[j - 1]) {
2341
0
            distance_along_segment -= ll->segment_lengths[j - 1];
2342
0
            j += inc; /* move to next segment */
2343
0
          }
2344
2345
0
          segment_length = ll->segment_lengths[j - 1];
2346
0
        }
2347
2348
        /* Recalculate interpolation parameter */
2349
0
        t = distance_along_segment / segment_length;
2350
2351
0
        k++;
2352
0
      }
2353
2354
      /* pre-calc the character's centre y value.  Used for rotation adjustment.
2355
       */
2356
0
      cy = -ts->textpath->glyph_size / 2.0;
2357
#ifdef USE_KERNEL
2358
      tp->glyphs[0].pnt.x /= kernel_normal;
2359
      tp->glyphs[0].pnt.y /= kernel_normal;
2360
#endif
2361
2362
      /* Average the points and calculate each angle */
2363
0
      overlap_collision_detected = 0;
2364
0
      for (k = 1; k <= tp->numglyphs; k++) {
2365
0
        double anglediff;
2366
0
        if (k < tp->numglyphs) {
2367
#ifdef USE_KERNEL
2368
          tp->glyphs[k].pnt.x /= kernel_normal;
2369
          tp->glyphs[k].pnt.y /= kernel_normal;
2370
#endif
2371
0
          dx = tp->glyphs[k].pnt.x - tp->glyphs[k - 1].pnt.x;
2372
0
          dy = tp->glyphs[k].pnt.y - tp->glyphs[k - 1].pnt.y;
2373
0
        } else {
2374
          /* Handle the last character */
2375
0
          dx = t * (p->point[j + inc].x - p->point[j].x) + p->point[j].x -
2376
0
               tp->glyphs[k - 1].pnt.x;
2377
0
          dy = t * (p->point[j + inc].y - p->point[j].y) + p->point[j].y -
2378
0
               tp->glyphs[k - 1].pnt.y;
2379
0
        }
2380
2381
0
        if (dx || dy || k == 1) {
2382
2383
0
          theta = -atan2(dy, dx);
2384
2385
0
          if (maxoverlapangle > 0 && k > 1) {
2386
            /* no use testing for overlap if glyph or previous glyph is a space
2387
             */
2388
0
            if (!msIsGlyphASpace(&tp->glyphs[k - 1]) &&
2389
0
                !msIsGlyphASpace(&tp->glyphs[k - 2])) {
2390
              /* If the difference between the last char angle and the current
2391
               one is greater than the MAXOVERLAPANGLE value (set at 80% of
2392
               180deg by default) , bail the label */
2393
0
              anglediff = fabs(theta - tp->glyphs[k - 2].rot);
2394
0
              anglediff = MS_MIN(anglediff, MS_2PI - anglediff);
2395
0
              if (anglediff > maxoverlapangle) {
2396
0
                double max_dec_retry_offset;
2397
0
                double max_inc_retry_offset;
2398
0
                if (label_repeat == 1) {
2399
0
                  max_dec_retry_offset = max_inc_retry_offset =
2400
0
                      first_label_position;
2401
0
                } else if (l == 0) {
2402
0
                  if (direction > 0) {
2403
0
                    max_inc_retry_offset =
2404
0
                        MS_MIN(first_label_position, max_retry_offset);
2405
0
                    max_dec_retry_offset = max_retry_offset;
2406
0
                  } else {
2407
0
                    max_inc_retry_offset = max_retry_offset;
2408
0
                    max_dec_retry_offset =
2409
0
                        MS_MIN(first_label_position, max_retry_offset);
2410
0
                  }
2411
0
                } else if (l == label_repeat - 1) {
2412
0
                  if (direction > 0) {
2413
0
                    max_dec_retry_offset =
2414
0
                        MS_MIN(first_label_position, max_retry_offset);
2415
0
                    max_inc_retry_offset = max_retry_offset;
2416
0
                  } else {
2417
0
                    max_dec_retry_offset = max_retry_offset;
2418
0
                    max_inc_retry_offset =
2419
0
                        MS_MIN(first_label_position, max_retry_offset);
2420
0
                  }
2421
0
                } else {
2422
0
                  max_dec_retry_offset = max_inc_retry_offset =
2423
0
                      max_retry_offset;
2424
0
                }
2425
2426
0
                overlap_collision_detected = 1;
2427
0
                if (retry_offset == 0.0) {
2428
0
                  first_retry_idx = k - 1;
2429
0
                }
2430
0
                retry_offset = compute_retry_offset(
2431
0
                    ts, first_retry_idx, retry_offset, max_dec_retry_offset,
2432
0
                    max_inc_retry_offset);
2433
0
                if (retry_offset != 0.0) { /* offsetted position to try */
2434
0
                  if (direction < 0) {
2435
0
                    retry_offset = -retry_offset;
2436
0
                  }
2437
0
                }
2438
0
                break;
2439
0
              }
2440
0
            }
2441
0
          }
2442
0
        } else {
2443
          /*
2444
           * handle the 0-advance case, as the complex text shaping may have
2445
           * placed multiple glyphs at the same position
2446
           */
2447
0
          theta = tp->glyphs[k - 2].rot;
2448
0
        }
2449
2450
        /* msDebug("s: %c (x,y): (%0.2f,%0.2f) t: %0.2f\n", string[k-1],
2451
         * labelpath->path.point[k-1].x, labelpath->path.point[k-1].y, theta);
2452
         */
2453
2454
0
        tp->glyphs[k - 1].rot = theta;
2455
2456
        /* Move the previous point so that when the character is rotated and
2457
         placed it is centred on the line */
2458
0
        cos_t = cos(theta);
2459
0
        sin_t = sin(theta);
2460
2461
0
        w = letterspacing * tp->glyphs[k - 1].glyph->metrics.advance;
2462
2463
0
        cx = 0; /* Center the character vertically only */
2464
2465
0
        dx = -(cx * cos_t + cy * sin_t);
2466
0
        dy = -(cy * cos_t - cx * sin_t);
2467
2468
0
        tp->glyphs[k - 1].pnt.x += dx;
2469
0
        tp->glyphs[k - 1].pnt.y += dy;
2470
2471
        /* Calculate the bounds */
2472
0
        bbox.minx = 0;
2473
0
        bbox.maxx = w;
2474
0
        bbox.maxy = 0;
2475
0
        bbox.miny = -ts->textpath->glyph_size;
2476
2477
        /* Add the label buffer to the bounds */
2478
0
        bbox.maxx += ts->label->buffer * ts->resolutionfactor;
2479
0
        bbox.maxy += ts->label->buffer * ts->resolutionfactor;
2480
0
        bbox.minx -= ts->label->buffer * ts->resolutionfactor;
2481
0
        bbox.miny -= ts->label->buffer * ts->resolutionfactor;
2482
2483
0
        if (k < tp->numglyphs) {
2484
          /* Transform the bbox too.  We take the UL and LL corners and rotate
2485
           then translate them. */
2486
0
          bounds->point[k - 1].x =
2487
0
              (bbox.minx * cos_t + bbox.maxy * sin_t) + tp->glyphs[k - 1].pnt.x;
2488
0
          bounds->point[k - 1].y =
2489
0
              (bbox.maxy * cos_t - bbox.minx * sin_t) + tp->glyphs[k - 1].pnt.y;
2490
2491
          /* Start at end and work towards the half way point */
2492
0
          bounds->point[bounds->numpoints - k - 1].x =
2493
0
              (bbox.minx * cos_t + bbox.miny * sin_t) + tp->glyphs[k - 1].pnt.x;
2494
0
          bounds->point[bounds->numpoints - k - 1].y =
2495
0
              (bbox.miny * cos_t - bbox.minx * sin_t) + tp->glyphs[k - 1].pnt.y;
2496
2497
0
        } else {
2498
2499
          /* This is the last character in the string so we take the UR and LR
2500
           corners of the bbox */
2501
0
          bounds->point[k - 1].x =
2502
0
              (bbox.maxx * cos_t + bbox.maxy * sin_t) + tp->glyphs[k - 1].pnt.x;
2503
0
          bounds->point[k - 1].y =
2504
0
              (bbox.maxy * cos_t - bbox.maxx * sin_t) + tp->glyphs[k - 1].pnt.y;
2505
2506
0
          bounds->point[bounds->numpoints - k - 1].x =
2507
0
              (bbox.maxx * cos_t + bbox.miny * sin_t) + tp->glyphs[k - 1].pnt.x;
2508
0
          bounds->point[bounds->numpoints - k - 1].y =
2509
0
              (bbox.miny * cos_t - bbox.maxx * sin_t) + tp->glyphs[k - 1].pnt.y;
2510
0
        }
2511
0
      }
2512
0
      if (overlap_collision_detected) {
2513
0
        continue;
2514
0
      }
2515
2516
      /* Close the bounds */
2517
0
      bounds->point[bounds->numpoints - 1].x = bounds->point[0].x;
2518
0
      bounds->point[bounds->numpoints - 1].y = bounds->point[0].y;
2519
2520
      /* compute the bounds */
2521
0
      fastComputeBounds(bounds, &tp->bounds.bbox);
2522
2523
0
      {
2524
0
        textPathObj *tmptp;
2525
0
        textSymbolObj *tsnew;
2526
0
        lfr->num_follow_labels++;
2527
0
        lfr->follow_labels = (textSymbolObj **)msSmallRealloc(
2528
0
            lfr->follow_labels,
2529
0
            lfr->num_follow_labels * sizeof(textSymbolObj *));
2530
0
        tmptp = ts->textpath;
2531
0
        ts->textpath = NULL;
2532
0
        lfr->follow_labels[lfr->num_follow_labels - 1] =
2533
0
            (textSymbolObj *)msSmallMalloc(sizeof(textSymbolObj));
2534
0
        tsnew = lfr->follow_labels[lfr->num_follow_labels - 1];
2535
0
        initTextSymbol(tsnew);
2536
0
        msCopyTextSymbol(tsnew, ts);
2537
0
        ts->textpath = tmptp;
2538
0
        tsnew->textpath = tp;
2539
0
        tp = NULL;
2540
0
        tsnew->textpath->absolute = 1;
2541
0
      }
2542
2543
0
      cur_label_position += label_buffer;
2544
0
      retry_offset = 0;
2545
0
    } while (retry_offset);
2546
2547
0
    if (tp) {
2548
0
      freeTextPath(tp);
2549
0
      free(tp);
2550
0
    }
2551
0
  }
2552
0
  return MS_SUCCESS;
2553
0
}
2554
2555
/* ===========================================================================
2556
   Pretty printing of primitive objects
2557
   ======================================================================== */
2558
2559
void msRectToFormattedString(rectObj *rect, char *format, char *buffer,
2560
0
                             int buffer_length) {
2561
0
  snprintf(buffer, buffer_length, format, rect->minx, rect->miny, rect->maxx,
2562
0
           rect->maxy);
2563
0
}
2564
2565
void msPointToFormattedString(pointObj *point, const char *format, char *buffer,
2566
0
                              int buffer_length) {
2567
0
  snprintf(buffer, buffer_length, format, point->x, point->y, point->z,
2568
0
           point->m);
2569
0
}
2570
2571
/* Returns true if a shape contains only degenerate parts */
2572
0
int msIsDegenerateShape(shapeObj *shape) {
2573
0
  int i;
2574
0
  int non_degenerate_parts = 0;
2575
0
  for (i = 0; i < shape->numlines; i++) { /* e.g. part */
2576
2577
    /* skip degenerate parts, really should only happen with pixel output */
2578
0
    if ((shape->type == MS_SHAPE_LINE && shape->line[i].numpoints < 2) ||
2579
0
        (shape->type == MS_SHAPE_POLYGON && shape->line[i].numpoints < 3))
2580
0
      continue;
2581
2582
0
    non_degenerate_parts++;
2583
0
  }
2584
0
  return (non_degenerate_parts == 0);
2585
0
}
2586
2587
0
shapeObj *msRings2Shape(shapeObj *shape, int outer) {
2588
0
  shapeObj *shape2;
2589
0
  int i, rv, *outerList = NULL;
2590
2591
0
  if (!shape)
2592
0
    return NULL;
2593
0
  if (shape->type != MS_SHAPE_POLYGON)
2594
0
    return NULL;
2595
2596
0
  outer = (outer) ? 1 : 0; // set explicitly to 1 or 0
2597
2598
0
  shape2 = (shapeObj *)malloc(sizeof(shapeObj));
2599
0
  MS_CHECK_ALLOC(shape2, sizeof(shapeObj), NULL);
2600
0
  msInitShape(shape2);
2601
0
  shape2->type = shape->type;
2602
2603
0
  outerList = msGetOuterList(shape);
2604
0
  if (!outerList) {
2605
0
    msFreeShape(shape2);
2606
0
    free(shape2);
2607
0
    return NULL;
2608
0
  }
2609
2610
0
  for (i = 0; i < shape->numlines; i++) {
2611
0
    if (outerList[i] == outer) { // else inner
2612
0
      rv = msAddLine(shape2, &(shape->line[i]));
2613
0
      if (rv != MS_SUCCESS) {
2614
0
        msFreeShape(shape2);
2615
0
        free(shape2);
2616
0
        free(outerList);
2617
0
        return NULL;
2618
0
      }
2619
0
    }
2620
0
  }
2621
2622
0
  free(outerList); // clean up
2623
0
  return shape2;
2624
0
}
2625
2626
0
shapeObj *msDensify(shapeObj *shape, double tolerance) {
2627
0
  int i, j, k, l; // counters
2628
0
  int n;
2629
0
  shapeObj *shape2;
2630
0
  lineObj line;
2631
0
  double distance, length, c;
2632
2633
0
  if (!shape)
2634
0
    return NULL;
2635
0
  if (shape->type != MS_SHAPE_POLYGON && shape->type != MS_SHAPE_LINE)
2636
0
    return NULL;
2637
0
  if (tolerance <= 0)
2638
0
    return NULL; // must be positive
2639
2640
0
  shape2 = (shapeObj *)malloc(sizeof(shapeObj));
2641
0
  MS_CHECK_ALLOC(shape2, sizeof(shapeObj), NULL);
2642
0
  msInitShape(shape2);
2643
0
  shape2->type = shape->type; // POLGYON or LINE
2644
2645
0
  for (i = 0; i < shape->numlines; i++) {
2646
2647
0
    line.numpoints = shape->line[i].numpoints;
2648
0
    line.point = (pointObj *)malloc(
2649
0
        sizeof(pointObj) *
2650
0
        line.numpoints); // best case we don't have to add any points
2651
0
    MS_CHECK_ALLOC(line.point, sizeof(pointObj) * line.numpoints, NULL);
2652
2653
0
    for (j = 0, l = 0; j < shape->line[i].numpoints - 1; j++, l++) {
2654
0
      line.point[l] = shape->line[i].point[j];
2655
2656
0
      distance = msDistancePointToPoint(&(shape->line[i].point[j]),
2657
0
                                        &(shape->line[i].point[j + 1]));
2658
0
      if (distance > tolerance) {
2659
0
        n = (int)floor(distance / tolerance); // number of new points, n+1 is
2660
                                              // the number of new segments
2661
0
        length = distance / (n + 1);          // segment length
2662
2663
0
        line.numpoints += n;
2664
0
        line.point =
2665
0
            (pointObj *)realloc(line.point, sizeof(pointObj) * line.numpoints);
2666
0
        MS_CHECK_ALLOC(line.point, sizeof(pointObj) * line.numpoints, NULL);
2667
2668
0
        for (k = 0; k < n; k++) {
2669
0
          c = (k + 1) * length / distance;
2670
0
          l++;
2671
0
          line.point[l].x =
2672
0
              shape->line[i].point[j].x +
2673
0
              c * (shape->line[i].point[j + 1].x - shape->line[i].point[j].x);
2674
0
          line.point[l].y =
2675
0
              shape->line[i].point[j].y +
2676
0
              c * (shape->line[i].point[j + 1].y - shape->line[i].point[j].y);
2677
0
        }
2678
0
      }
2679
0
    }
2680
0
    line.point[l] = shape->line[i].point[j];
2681
2682
0
    msAddLineDirectly(shape2, &line);
2683
0
  }
2684
2685
0
  return shape2;
2686
0
}