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