/src/MapServer/src/mapgraticule.c
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: Graticule Renderer |
6 | | * Author: John Novak, Novacell Technologies (jnovak@novacell.com) |
7 | | * |
8 | | ********************************************************************** |
9 | | * Copyright (c) 2003, John Novak, Novacell Technologies |
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 OR |
22 | | * 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 | | ****************************************************************************/ |
28 | | |
29 | | #include "mapserver.h" |
30 | | #include <assert.h> |
31 | | #include "mapproject.h" |
32 | | |
33 | | /********************************************************************************************************************** |
34 | | * |
35 | | */ |
36 | | typedef enum { posBottom = 1, posTop, posLeft, posRight } msGraticulePosition; |
37 | | |
38 | | typedef enum { |
39 | | lpDefault = 0, |
40 | | lpDDMMSS = 1, |
41 | | lpDDMM, |
42 | | lpDD |
43 | | } msLabelProcessingType; |
44 | | |
45 | | void DefineAxis(int iTickCountTarget, double *Min, double *Max, double *Inc); |
46 | | static int _AdjustLabelPosition(layerObj *pLayer, shapeObj *pShape, |
47 | | msGraticulePosition ePosition); |
48 | | static void _FormatLabel(layerObj *pLayer, shapeObj *pShape, |
49 | | double dDataToFormat); |
50 | | |
51 | | int msGraticuleLayerInitItemInfo(layerObj *layer); |
52 | | |
53 | 0 | #define MAPGRATICULE_ARC_SUBDIVISION_DEFAULT (256) |
54 | 0 | #define MAPGRATICULE_ARC_MINIMUM (16) |
55 | 0 | #define MAPGRATICULE_FORMAT_STRING_DEFAULT "%5.2g" |
56 | 0 | #define MAPGRATICULE_FORMAT_STRING_DDMMSS "%3d %02d %02d" |
57 | 0 | #define MAPGRATICULE_FORMAT_STRING_DDMM "%3d %02d" |
58 | 0 | #define MAPGRATICULE_FORMAT_STRING_DD "%3d" |
59 | | |
60 | | /********************************************************************************************************************** |
61 | | * |
62 | | */ |
63 | 0 | int msGraticuleLayerOpen(layerObj *layer) { |
64 | 0 | graticuleObj *pInfo = layer->grid; |
65 | |
|
66 | 0 | if (pInfo == NULL) |
67 | 0 | return MS_FAILURE; |
68 | | |
69 | 0 | pInfo->dincrementlatitude = 15.0; |
70 | 0 | pInfo->dincrementlongitude = 15.0; |
71 | 0 | pInfo->dwhichlatitude = -90.0; |
72 | 0 | pInfo->dwhichlongitude = -180.0; |
73 | 0 | pInfo->bvertical = 1; |
74 | |
|
75 | 0 | if (layer->numclasses == 0) |
76 | 0 | msDebug("GRID layer has no classes, nothing will be rendered.\n"); |
77 | |
|
78 | 0 | if (layer->numclasses > 0 && layer->class[0] -> numlabels > 0) |
79 | 0 | pInfo->blabelaxes = 1; |
80 | 0 | else |
81 | 0 | pInfo->blabelaxes = 0; |
82 | |
|
83 | 0 | if (pInfo->labelformat == NULL) { |
84 | 0 | pInfo->labelformat = |
85 | 0 | (char *)msSmallMalloc(strlen(MAPGRATICULE_FORMAT_STRING_DEFAULT) + 1); |
86 | 0 | pInfo->ilabeltype = (int)lpDefault; |
87 | 0 | strcpy(pInfo->labelformat, MAPGRATICULE_FORMAT_STRING_DEFAULT); |
88 | 0 | } else if (strcmp(pInfo->labelformat, "DDMMSS") == 0) { |
89 | 0 | msFree(pInfo->labelformat); |
90 | 0 | pInfo->labelformat = |
91 | 0 | (char *)msSmallMalloc(strlen(MAPGRATICULE_FORMAT_STRING_DDMMSS) + 1); |
92 | 0 | pInfo->ilabeltype = (int)lpDDMMSS; |
93 | 0 | strcpy(pInfo->labelformat, MAPGRATICULE_FORMAT_STRING_DDMMSS); |
94 | 0 | } else if (strcmp(pInfo->labelformat, "DDMM") == 0) { |
95 | 0 | msFree(pInfo->labelformat); |
96 | 0 | pInfo->labelformat = |
97 | 0 | (char *)msSmallMalloc(strlen(MAPGRATICULE_FORMAT_STRING_DDMM) + 1); |
98 | 0 | pInfo->ilabeltype = (int)lpDDMM; |
99 | 0 | strcpy(pInfo->labelformat, MAPGRATICULE_FORMAT_STRING_DDMM); |
100 | 0 | } else if (strcmp(pInfo->labelformat, "DD") == 0) { |
101 | 0 | msFree(pInfo->labelformat); |
102 | 0 | pInfo->labelformat = |
103 | 0 | (char *)msSmallMalloc(strlen(MAPGRATICULE_FORMAT_STRING_DD) + 1); |
104 | 0 | pInfo->ilabeltype = (int)lpDD; |
105 | 0 | strcpy(pInfo->labelformat, MAPGRATICULE_FORMAT_STRING_DD); |
106 | 0 | } |
107 | |
|
108 | 0 | return MS_SUCCESS; |
109 | 0 | } |
110 | | |
111 | | /********************************************************************************************************************** |
112 | | * Return MS_TRUE if layer is open, MS_FALSE otherwise. |
113 | | */ |
114 | 0 | int msGraticuleLayerIsOpen(layerObj *layer) { |
115 | 0 | if (layer->grid) |
116 | 0 | return MS_TRUE; |
117 | | |
118 | 0 | return MS_FALSE; |
119 | 0 | } |
120 | | |
121 | | /********************************************************************************************************************** |
122 | | * |
123 | | */ |
124 | 0 | int msGraticuleLayerClose(layerObj *layer) { |
125 | 0 | (void)layer; |
126 | 0 | return MS_SUCCESS; |
127 | 0 | } |
128 | | |
129 | | /********************************************************************************************************************** |
130 | | * |
131 | | */ |
132 | 0 | int msGraticuleLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery) { |
133 | 0 | (void)isQuery; |
134 | 0 | graticuleObj *pInfo = layer->grid; |
135 | 0 | int iAxisTickCount = 0; |
136 | 0 | rectObj rectMapCoordinates; |
137 | |
|
138 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
139 | 0 | return MS_FAILURE; |
140 | | |
141 | 0 | pInfo->dstartlatitude = rect.miny; |
142 | 0 | pInfo->dstartlongitude = rect.minx; |
143 | 0 | pInfo->dendlatitude = rect.maxy; |
144 | 0 | pInfo->dendlongitude = rect.maxx; |
145 | 0 | pInfo->bvertical = 1; |
146 | 0 | pInfo->extent = rect; |
147 | |
|
148 | 0 | if (pInfo->minincrement > 0.0) { |
149 | 0 | pInfo->dincrementlongitude = pInfo->minincrement; |
150 | 0 | pInfo->dincrementlatitude = pInfo->minincrement; |
151 | 0 | } else if (pInfo->maxincrement > 0.0) { |
152 | 0 | pInfo->dincrementlongitude = pInfo->maxincrement; |
153 | 0 | pInfo->dincrementlatitude = pInfo->maxincrement; |
154 | 0 | } else { |
155 | 0 | pInfo->dincrementlongitude = 0; |
156 | 0 | pInfo->dincrementlatitude = 0; |
157 | 0 | } |
158 | |
|
159 | 0 | if (pInfo->maxarcs > 0) |
160 | 0 | iAxisTickCount = (int)pInfo->maxarcs; |
161 | 0 | else if (pInfo->minarcs > 0) |
162 | 0 | iAxisTickCount = (int)pInfo->minarcs; |
163 | |
|
164 | 0 | DefineAxis(iAxisTickCount, &pInfo->dstartlongitude, &pInfo->dendlongitude, |
165 | 0 | &pInfo->dincrementlongitude); |
166 | 0 | DefineAxis(iAxisTickCount, &pInfo->dstartlatitude, &pInfo->dendlatitude, |
167 | 0 | &pInfo->dincrementlatitude); |
168 | |
|
169 | 0 | pInfo->dwhichlatitude = pInfo->dstartlatitude; |
170 | 0 | pInfo->dwhichlongitude = pInfo->dstartlongitude; |
171 | |
|
172 | 0 | if (pInfo->minincrement > 0.0 && pInfo->maxincrement > 0.0 && |
173 | 0 | pInfo->minincrement == pInfo->maxincrement) { |
174 | 0 | pInfo->dincrementlongitude = pInfo->minincrement; |
175 | 0 | pInfo->dincrementlatitude = pInfo->minincrement; |
176 | 0 | } else if (pInfo->minincrement > 0.0) { |
177 | 0 | pInfo->dincrementlongitude = pInfo->minincrement; |
178 | 0 | pInfo->dincrementlatitude = pInfo->minincrement; |
179 | 0 | } else if (pInfo->maxincrement > 0.0) { |
180 | 0 | pInfo->dincrementlongitude = pInfo->maxincrement; |
181 | 0 | pInfo->dincrementlatitude = pInfo->maxincrement; |
182 | 0 | } |
183 | | |
184 | | /* |
185 | | * If using PROJ, project rect back into map system, and generate rect corner |
186 | | * points in native system. These lines will be used when generating labels to |
187 | | * get correct placement at arc/rect edge intersections. |
188 | | */ |
189 | 0 | rectMapCoordinates = layer->map->extent; |
190 | |
|
191 | 0 | layer->project = |
192 | 0 | msProjectionsDiffer(&(layer->projection), &(layer->map->projection)); |
193 | 0 | if (layer->project && strstr(layer->map->projection.args[0], "epsg:3857") && |
194 | 0 | msProjIsGeographicCRS(&(layer->projection))) { |
195 | 0 | if (rectMapCoordinates.minx < -20037508) |
196 | 0 | rectMapCoordinates.minx = -20037508; |
197 | 0 | if (rectMapCoordinates.maxx > 20037508) |
198 | 0 | rectMapCoordinates.maxx = 20037508; |
199 | 0 | } |
200 | |
|
201 | 0 | msFree(pInfo->pboundinglines); |
202 | 0 | pInfo->pboundinglines = (lineObj *)msSmallMalloc(sizeof(lineObj) * 4); |
203 | 0 | msFree(pInfo->pboundingpoints); |
204 | 0 | pInfo->pboundingpoints = (pointObj *)msSmallMalloc(sizeof(pointObj) * 8); |
205 | |
|
206 | 0 | { |
207 | | |
208 | | /* |
209 | | * top |
210 | | */ |
211 | 0 | pInfo->pboundinglines[0].numpoints = 2; |
212 | 0 | pInfo->pboundinglines[0].point = &pInfo->pboundingpoints[0]; |
213 | 0 | pInfo->pboundinglines[0].point[0].x = rectMapCoordinates.minx; |
214 | 0 | pInfo->pboundinglines[0].point[0].y = rectMapCoordinates.maxy; |
215 | 0 | pInfo->pboundinglines[0].point[1].x = rectMapCoordinates.maxx; |
216 | 0 | pInfo->pboundinglines[0].point[1].y = rectMapCoordinates.maxy; |
217 | |
|
218 | 0 | if (layer->project) |
219 | 0 | msProjectLine(&layer->map->projection, &layer->projection, |
220 | 0 | &pInfo->pboundinglines[0]); |
221 | | |
222 | | /* |
223 | | * bottom |
224 | | */ |
225 | 0 | pInfo->pboundinglines[1].numpoints = 2; |
226 | 0 | pInfo->pboundinglines[1].point = &pInfo->pboundingpoints[2]; |
227 | 0 | pInfo->pboundinglines[1].point[0].x = rectMapCoordinates.minx; |
228 | 0 | pInfo->pboundinglines[1].point[0].y = rectMapCoordinates.miny; |
229 | 0 | pInfo->pboundinglines[1].point[1].x = rectMapCoordinates.maxx; |
230 | 0 | pInfo->pboundinglines[1].point[1].y = rectMapCoordinates.miny; |
231 | |
|
232 | 0 | if (layer->project) |
233 | 0 | msProjectLine(&layer->map->projection, &layer->projection, |
234 | 0 | &pInfo->pboundinglines[1]); |
235 | | |
236 | | /* |
237 | | * left |
238 | | */ |
239 | 0 | pInfo->pboundinglines[2].numpoints = 2; |
240 | 0 | pInfo->pboundinglines[2].point = &pInfo->pboundingpoints[4]; |
241 | 0 | pInfo->pboundinglines[2].point[0].x = rectMapCoordinates.minx; |
242 | 0 | pInfo->pboundinglines[2].point[0].y = rectMapCoordinates.miny; |
243 | 0 | pInfo->pboundinglines[2].point[1].x = rectMapCoordinates.minx; |
244 | 0 | pInfo->pboundinglines[2].point[1].y = rectMapCoordinates.maxy; |
245 | |
|
246 | 0 | if (layer->project) |
247 | 0 | msProjectLine(&layer->map->projection, &layer->projection, |
248 | 0 | &pInfo->pboundinglines[2]); |
249 | | |
250 | | /* |
251 | | * right |
252 | | */ |
253 | 0 | pInfo->pboundinglines[3].numpoints = 2; |
254 | 0 | pInfo->pboundinglines[3].point = &pInfo->pboundingpoints[6]; |
255 | 0 | pInfo->pboundinglines[3].point[0].x = rectMapCoordinates.maxx; |
256 | 0 | pInfo->pboundinglines[3].point[0].y = rectMapCoordinates.miny; |
257 | 0 | pInfo->pboundinglines[3].point[1].x = rectMapCoordinates.maxx; |
258 | 0 | pInfo->pboundinglines[3].point[1].y = rectMapCoordinates.maxy; |
259 | |
|
260 | 0 | if (layer->project) |
261 | 0 | msProjectLine(&layer->map->projection, &layer->projection, |
262 | 0 | &pInfo->pboundinglines[3]); |
263 | 0 | } |
264 | |
|
265 | 0 | return MS_SUCCESS; |
266 | 0 | } |
267 | | |
268 | | /********************************************************************************************************************** |
269 | | * |
270 | | */ |
271 | 0 | int msGraticuleLayerNextShape(layerObj *layer, shapeObj *shape) { |
272 | 0 | graticuleObj *pInfo = layer->grid; |
273 | |
|
274 | 0 | if (pInfo->minsubdivides <= 0.0 || pInfo->maxsubdivides <= 0.0) |
275 | 0 | pInfo->minsubdivides = pInfo->maxsubdivides = |
276 | 0 | MAPGRATICULE_ARC_SUBDIVISION_DEFAULT; |
277 | |
|
278 | 0 | shape->numlines = 1; |
279 | 0 | shape->type = MS_SHAPE_LINE; |
280 | 0 | shape->line = (lineObj *)msSmallMalloc(sizeof(lineObj)); |
281 | 0 | shape->line->numpoints = (int)pInfo->maxsubdivides; |
282 | 0 | shape->line->point = NULL; |
283 | | |
284 | | /* |
285 | | * Subdivide and draw current arc, rendering the arc labels first |
286 | | */ |
287 | 0 | if (pInfo->bvertical) { |
288 | 0 | int iPointIndex; |
289 | 0 | double dArcDelta = (pInfo->dendlatitude - pInfo->dstartlatitude) / |
290 | 0 | (double)shape->line->numpoints; |
291 | 0 | double dArcPosition = pInfo->dstartlatitude + dArcDelta; |
292 | 0 | double dStartY, dDeltaX; |
293 | |
|
294 | 0 | switch (pInfo->ilabelstate) { |
295 | 0 | case 0: |
296 | 0 | if (!pInfo->blabelaxes) { /* Bottom */ |
297 | 0 | pInfo->ilabelstate++; |
298 | 0 | msFreeShape(shape); |
299 | 0 | return MS_SUCCESS; |
300 | 0 | } |
301 | | |
302 | 0 | dDeltaX = (pInfo->dwhichlongitude - pInfo->pboundinglines[1].point[0].x) / |
303 | 0 | (pInfo->pboundinglines[1].point[1].x - |
304 | 0 | pInfo->pboundinglines[1].point[0].x); |
305 | 0 | if (dDeltaX < 0) |
306 | 0 | dDeltaX = dDeltaX * -1; |
307 | |
|
308 | 0 | dStartY = (pInfo->pboundinglines[1].point[1].y - |
309 | 0 | pInfo->pboundinglines[1].point[0].y) * |
310 | 0 | dDeltaX + |
311 | 0 | pInfo->pboundinglines[1].point[0].y; |
312 | 0 | shape->line->numpoints = (int)2; |
313 | 0 | shape->line->point = (pointObj *)msSmallMalloc(sizeof(pointObj) * 2); |
314 | |
|
315 | 0 | shape->line->point[0].x = pInfo->dwhichlongitude; |
316 | 0 | shape->line->point[0].y = dStartY; |
317 | 0 | shape->line->point[1].x = pInfo->dwhichlongitude; |
318 | 0 | shape->line->point[1].y = dStartY + dArcDelta; |
319 | |
|
320 | 0 | _FormatLabel(layer, shape, shape->line->point[0].x); |
321 | 0 | if (_AdjustLabelPosition(layer, shape, posBottom) != MS_SUCCESS) { |
322 | 0 | msFreeShape(shape); |
323 | 0 | pInfo->ilabelstate++; |
324 | 0 | return MS_SUCCESS; |
325 | 0 | } |
326 | | |
327 | 0 | pInfo->ilabelstate++; |
328 | 0 | return MS_SUCCESS; |
329 | | |
330 | 0 | case 1: |
331 | 0 | if (!pInfo->blabelaxes) { /* Top */ |
332 | 0 | pInfo->ilabelstate++; |
333 | 0 | msFreeShape(shape); |
334 | 0 | return MS_SUCCESS; |
335 | 0 | } |
336 | | |
337 | 0 | dDeltaX = (pInfo->dwhichlongitude - pInfo->pboundinglines[0].point[0].x) / |
338 | 0 | (pInfo->pboundinglines[0].point[1].x - |
339 | 0 | pInfo->pboundinglines[0].point[0].x); |
340 | 0 | if (dDeltaX < 0) |
341 | 0 | dDeltaX = dDeltaX * -1; |
342 | |
|
343 | 0 | dStartY = (pInfo->pboundinglines[0].point[1].y - |
344 | 0 | pInfo->pboundinglines[0].point[0].y) * |
345 | 0 | dDeltaX + |
346 | 0 | pInfo->pboundinglines[0].point[1].y; |
347 | 0 | shape->line->numpoints = (int)2; |
348 | 0 | shape->line->point = (pointObj *)msSmallMalloc(sizeof(pointObj) * 2); |
349 | |
|
350 | 0 | shape->line->point[0].x = pInfo->dwhichlongitude; |
351 | 0 | shape->line->point[0].y = dStartY - dArcDelta; |
352 | 0 | shape->line->point[1].x = pInfo->dwhichlongitude; |
353 | 0 | shape->line->point[1].y = dStartY; |
354 | |
|
355 | 0 | _FormatLabel(layer, shape, shape->line->point[0].x); |
356 | 0 | if (_AdjustLabelPosition(layer, shape, posTop) != MS_SUCCESS) { |
357 | 0 | msFreeShape(shape); |
358 | 0 | pInfo->ilabelstate++; |
359 | 0 | return MS_SUCCESS; |
360 | 0 | } |
361 | | |
362 | 0 | pInfo->ilabelstate++; |
363 | 0 | return MS_SUCCESS; |
364 | | |
365 | 0 | case 2: |
366 | 0 | shape->line->numpoints = (int)shape->line->numpoints + 1; |
367 | 0 | shape->line->point = |
368 | 0 | (pointObj *)msSmallMalloc(sizeof(pointObj) * shape->line->numpoints); |
369 | |
|
370 | 0 | shape->line->point[0].x = pInfo->dwhichlongitude; |
371 | 0 | shape->line->point[0].y = pInfo->dstartlatitude; |
372 | |
|
373 | 0 | for (iPointIndex = 1; iPointIndex < shape->line->numpoints; |
374 | 0 | iPointIndex++) { |
375 | 0 | shape->line->point[iPointIndex].x = pInfo->dwhichlongitude; |
376 | 0 | shape->line->point[iPointIndex].y = dArcPosition; |
377 | |
|
378 | 0 | dArcPosition += dArcDelta; |
379 | 0 | } |
380 | |
|
381 | 0 | pInfo->ilabelstate = 0; |
382 | |
|
383 | 0 | pInfo->dwhichlongitude += pInfo->dincrementlongitude; |
384 | 0 | break; |
385 | | |
386 | 0 | default: |
387 | 0 | pInfo->ilabelstate = 0; |
388 | 0 | break; |
389 | 0 | } |
390 | |
|
391 | 0 | } else { /*horizontal*/ |
392 | 0 | int iPointIndex; |
393 | 0 | double dArcDelta = (pInfo->dendlongitude - pInfo->dstartlongitude) / |
394 | 0 | (double)shape->line->numpoints; |
395 | 0 | double dArcPosition = pInfo->dstartlongitude + dArcDelta; |
396 | 0 | double dStartX, dDeltaY; |
397 | |
|
398 | 0 | switch (pInfo->ilabelstate) { |
399 | 0 | case 0: |
400 | 0 | if (!pInfo->blabelaxes) { /* Left side */ |
401 | 0 | pInfo->ilabelstate++; |
402 | 0 | msFreeShape(shape); |
403 | 0 | return MS_SUCCESS; |
404 | 0 | } |
405 | | |
406 | 0 | dDeltaY = (pInfo->dwhichlatitude - pInfo->pboundinglines[2].point[0].y) / |
407 | 0 | (pInfo->pboundinglines[2].point[1].y - |
408 | 0 | pInfo->pboundinglines[2].point[0].y); |
409 | 0 | if (dDeltaY < 0) |
410 | 0 | dDeltaY = dDeltaY * -1; |
411 | |
|
412 | 0 | dStartX = (pInfo->pboundinglines[2].point[1].x - |
413 | 0 | pInfo->pboundinglines[2].point[0].x) * |
414 | 0 | dDeltaY + |
415 | 0 | pInfo->pboundinglines[2].point[0].x; |
416 | 0 | shape->line->numpoints = (int)2; |
417 | 0 | shape->line->point = (pointObj *)msSmallMalloc(sizeof(pointObj) * 2); |
418 | |
|
419 | 0 | shape->line->point[0].x = dStartX; |
420 | 0 | shape->line->point[0].y = pInfo->dwhichlatitude; |
421 | 0 | shape->line->point[1].x = dStartX + dArcDelta; |
422 | 0 | shape->line->point[1].y = pInfo->dwhichlatitude; |
423 | |
|
424 | 0 | _FormatLabel(layer, shape, shape->line->point[0].y); |
425 | 0 | if (_AdjustLabelPosition(layer, shape, posLeft) != MS_SUCCESS) { |
426 | 0 | msFreeShape(shape); |
427 | 0 | pInfo->ilabelstate++; |
428 | 0 | return MS_SUCCESS; |
429 | 0 | } |
430 | | |
431 | 0 | pInfo->ilabelstate++; |
432 | 0 | return MS_SUCCESS; |
433 | | |
434 | 0 | case 1: |
435 | 0 | if (!pInfo->blabelaxes) { /* Right side */ |
436 | 0 | pInfo->ilabelstate++; |
437 | 0 | msFreeShape(shape); |
438 | 0 | return MS_SUCCESS; |
439 | 0 | } |
440 | | |
441 | 0 | dDeltaY = (pInfo->dwhichlatitude - pInfo->pboundinglines[3].point[0].y) / |
442 | 0 | (pInfo->pboundinglines[3].point[1].y - |
443 | 0 | pInfo->pboundinglines[3].point[0].y); |
444 | 0 | if (dDeltaY < 0) |
445 | 0 | dDeltaY = dDeltaY * -1; |
446 | |
|
447 | 0 | dStartX = (pInfo->pboundinglines[3].point[1].x - |
448 | 0 | pInfo->pboundinglines[3].point[0].x) * |
449 | 0 | dDeltaY + |
450 | 0 | pInfo->pboundinglines[3].point[0].x; |
451 | 0 | shape->line->numpoints = (int)2; |
452 | 0 | shape->line->point = (pointObj *)msSmallMalloc(sizeof(pointObj) * 2); |
453 | |
|
454 | 0 | shape->line->point[0].x = dStartX - dArcDelta; |
455 | 0 | shape->line->point[0].y = pInfo->dwhichlatitude; |
456 | 0 | shape->line->point[1].x = dStartX; |
457 | 0 | shape->line->point[1].y = pInfo->dwhichlatitude; |
458 | |
|
459 | 0 | _FormatLabel(layer, shape, shape->line->point[0].y); |
460 | 0 | if (_AdjustLabelPosition(layer, shape, posRight) != MS_SUCCESS) { |
461 | 0 | msFreeShape(shape); |
462 | 0 | pInfo->ilabelstate++; |
463 | 0 | return MS_SUCCESS; |
464 | 0 | } |
465 | | |
466 | 0 | pInfo->ilabelstate++; |
467 | 0 | return MS_SUCCESS; |
468 | | |
469 | 0 | case 2: |
470 | 0 | shape->line->numpoints = (int)shape->line->numpoints + 1; |
471 | 0 | shape->line->point = |
472 | 0 | (pointObj *)msSmallMalloc(sizeof(pointObj) * shape->line->numpoints); |
473 | |
|
474 | 0 | shape->line->point[0].x = pInfo->dstartlongitude; |
475 | 0 | shape->line->point[0].y = pInfo->dwhichlatitude; |
476 | |
|
477 | 0 | for (iPointIndex = 1; iPointIndex < shape->line->numpoints; |
478 | 0 | iPointIndex++) { |
479 | 0 | shape->line->point[iPointIndex].x = dArcPosition; |
480 | 0 | shape->line->point[iPointIndex].y = pInfo->dwhichlatitude; |
481 | |
|
482 | 0 | dArcPosition += dArcDelta; |
483 | 0 | } |
484 | |
|
485 | 0 | pInfo->ilabelstate = 0; |
486 | 0 | pInfo->dwhichlatitude += pInfo->dincrementlatitude; |
487 | 0 | break; |
488 | | |
489 | 0 | default: |
490 | 0 | pInfo->ilabelstate = 0; |
491 | 0 | break; |
492 | 0 | } |
493 | 0 | } |
494 | | |
495 | | /* |
496 | | * Increment and move to next arc |
497 | | */ |
498 | | |
499 | 0 | if (pInfo->bvertical && pInfo->dwhichlongitude > pInfo->dendlongitude) { |
500 | 0 | pInfo->dwhichlatitude = pInfo->dstartlatitude; |
501 | 0 | pInfo->bvertical = 0; |
502 | 0 | } |
503 | |
|
504 | 0 | if (pInfo->dwhichlatitude > pInfo->dendlatitude) { |
505 | | /* free the lineObj and pointObj that have been erroneously allocated |
506 | | * beforehand */ |
507 | 0 | msFreeShape(shape); |
508 | 0 | return MS_DONE; |
509 | 0 | } |
510 | | |
511 | 0 | return MS_SUCCESS; |
512 | 0 | } |
513 | | |
514 | | /********************************************************************************************************************** |
515 | | * |
516 | | */ |
517 | 0 | int msGraticuleLayerGetItems(layerObj *layer) { |
518 | 0 | char **ppItemName = (char **)msSmallMalloc(sizeof(char *)); |
519 | |
|
520 | 0 | *ppItemName = (char *)msSmallMalloc(64); /* why is this necessary? */ |
521 | 0 | strcpy(*ppItemName, "Graticule"); |
522 | |
|
523 | 0 | layer->numitems = 1; |
524 | 0 | layer->items = ppItemName; |
525 | |
|
526 | 0 | return MS_SUCCESS; |
527 | 0 | } |
528 | | |
529 | | /********************************************************************************************************************** |
530 | | * |
531 | | */ |
532 | 0 | int msGraticuleLayerInitItemInfo(layerObj *layer) { |
533 | 0 | (void)layer; |
534 | 0 | return MS_SUCCESS; |
535 | 0 | } |
536 | | |
537 | | /********************************************************************************************************************** |
538 | | * |
539 | | */ |
540 | 0 | void msGraticuleLayerFreeItemInfo(layerObj *layer) { (void)layer; } |
541 | | |
542 | | /********************************************************************************************************************** |
543 | | * |
544 | | */ |
545 | | int msGraticuleLayerGetShape(layerObj *layer, shapeObj *shape, |
546 | 0 | resultObj *record) { |
547 | 0 | (void)layer; |
548 | 0 | (void)shape; |
549 | 0 | (void)record; |
550 | 0 | return MS_FAILURE; |
551 | 0 | } |
552 | | |
553 | | /********************************************************************************************************************** |
554 | | * |
555 | | */ |
556 | 0 | int msGraticuleLayerGetExtent(layerObj *layer, rectObj *extent) { |
557 | 0 | graticuleObj *pInfo = layer->grid; |
558 | |
|
559 | 0 | if (pInfo) { |
560 | 0 | *extent = pInfo->extent; |
561 | 0 | return MS_SUCCESS; |
562 | 0 | } |
563 | | |
564 | 0 | return MS_FAILURE; |
565 | 0 | } |
566 | | |
567 | | /********************************************************************************************************************** |
568 | | * |
569 | | */ |
570 | | int msGraticuleLayerGetAutoStyle(mapObj *map, layerObj *layer, classObj *c, |
571 | 0 | shapeObj *shape) { |
572 | 0 | (void)map; |
573 | 0 | (void)layer; |
574 | 0 | (void)c; |
575 | 0 | (void)shape; |
576 | 0 | return MS_SUCCESS; |
577 | 0 | } |
578 | | |
579 | | /************************************************************************/ |
580 | | /* msGraticuleLayerFreeIntersectionPoints */ |
581 | | /* */ |
582 | | /* Free intersection object. */ |
583 | | /************************************************************************/ |
584 | 0 | void msGraticuleLayerFreeIntersectionPoints(graticuleIntersectionObj *psValue) { |
585 | 0 | if (psValue) { |
586 | 0 | for (int i = 0; i < psValue->nTop; i++) |
587 | 0 | msFree(psValue->papszTopLabels[i]); |
588 | 0 | msFree(psValue->papszTopLabels); |
589 | 0 | msFree(psValue->pasTop); |
590 | |
|
591 | 0 | for (int i = 0; i < psValue->nBottom; i++) |
592 | 0 | msFree(psValue->papszBottomLabels[i]); |
593 | 0 | msFree(psValue->papszBottomLabels); |
594 | 0 | msFree(psValue->pasBottom); |
595 | |
|
596 | 0 | for (int i = 0; i < psValue->nLeft; i++) |
597 | 0 | msFree(psValue->papszLeftLabels[i]); |
598 | 0 | msFree(psValue->papszLeftLabels); |
599 | 0 | msFree(psValue->pasLeft); |
600 | |
|
601 | 0 | for (int i = 0; i < psValue->nRight; i++) |
602 | 0 | msFree(psValue->papszRightLabels[i]); |
603 | 0 | msFree(psValue->papszRightLabels); |
604 | 0 | msFree(psValue->pasRight); |
605 | |
|
606 | 0 | msFree(psValue); |
607 | 0 | } |
608 | 0 | } |
609 | | |
610 | | /************************************************************************/ |
611 | | /* msGraticuleLayerInitIntersectionPoints */ |
612 | | /* */ |
613 | | /* init intersection object. */ |
614 | | /************************************************************************/ |
615 | | static void |
616 | 0 | msGraticuleLayerInitIntersectionPoints(graticuleIntersectionObj *psValue) { |
617 | 0 | if (psValue) { |
618 | 0 | psValue->nTop = 0; |
619 | 0 | psValue->pasTop = NULL; |
620 | 0 | psValue->papszTopLabels = NULL; |
621 | 0 | psValue->nBottom = 0; |
622 | 0 | psValue->pasBottom = NULL; |
623 | 0 | psValue->papszBottomLabels = NULL; |
624 | 0 | psValue->nLeft = 0; |
625 | 0 | psValue->pasLeft = NULL; |
626 | 0 | psValue->papszLeftLabels = NULL; |
627 | 0 | psValue->nRight = 0; |
628 | 0 | psValue->pasRight = NULL; |
629 | 0 | psValue->papszRightLabels = NULL; |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | | /************************************************************************/ |
634 | | /* msGraticuleLayerGetIntersectionPoints */ |
635 | | /* */ |
636 | | /* Utility function thar returns all intersection positions and */ |
637 | | /* labels (4 sides of the map) for a grid layer. */ |
638 | | /************************************************************************/ |
639 | | graticuleIntersectionObj * |
640 | 0 | msGraticuleLayerGetIntersectionPoints(mapObj *map, layerObj *layer) { |
641 | |
|
642 | 0 | shapeObj shapegrid, tmpshape; |
643 | 0 | rectObj searchrect; |
644 | 0 | int status; |
645 | 0 | pointObj oFirstPoint; |
646 | 0 | pointObj oLastPoint; |
647 | 0 | lineObj oLineObj; |
648 | 0 | rectObj cliprect; |
649 | 0 | graticuleObj *pInfo = NULL; |
650 | 0 | double dfTmp; |
651 | 0 | int i = 0; |
652 | |
|
653 | 0 | if (layer->connectiontype != MS_GRATICULE) |
654 | 0 | return NULL; |
655 | | |
656 | 0 | pInfo = layer->grid; |
657 | | |
658 | | /*set cellsize if bnot already set*/ |
659 | 0 | if (map->cellsize == 0) |
660 | 0 | map->cellsize = msAdjustExtent(&(map->extent), map->width, map->height); |
661 | |
|
662 | 0 | if (layer->transform == MS_TRUE) |
663 | 0 | searchrect = map->extent; |
664 | 0 | else { |
665 | 0 | searchrect.minx = searchrect.miny = 0; |
666 | 0 | searchrect.maxx = map->width - 1; |
667 | 0 | searchrect.maxy = map->height - 1; |
668 | 0 | } |
669 | |
|
670 | 0 | if ((map->projection.numargs > 0) && (layer->projection.numargs > 0)) |
671 | 0 | msProjectRect(&map->projection, &layer->projection, |
672 | 0 | &searchrect); /* project the searchrect to source coords */ |
673 | |
|
674 | 0 | status = msLayerOpen(layer); |
675 | 0 | if (status != MS_SUCCESS) |
676 | 0 | return NULL; |
677 | | |
678 | 0 | status = msLayerWhichShapes(layer, searchrect, MS_FALSE); |
679 | 0 | if (status == MS_DONE) { /* no overlap */ |
680 | 0 | msLayerClose(layer); |
681 | 0 | return NULL; |
682 | 0 | } else if (status != MS_SUCCESS) { |
683 | 0 | msLayerClose(layer); |
684 | 0 | return NULL; |
685 | 0 | } |
686 | | |
687 | | /* step through the target shapes */ |
688 | 0 | msInitShape(&shapegrid); |
689 | 0 | cliprect.minx = map->extent.minx - map->cellsize; |
690 | 0 | cliprect.miny = map->extent.miny - map->cellsize; |
691 | 0 | cliprect.maxx = map->extent.maxx + map->cellsize; |
692 | 0 | cliprect.maxy = map->extent.maxy + map->cellsize; |
693 | | |
694 | | /* clip using the layer projection */ |
695 | | /* msProjectRect(&map->projection , &layer->projection, &cliprect); */ |
696 | |
|
697 | 0 | graticuleIntersectionObj *psValues = |
698 | 0 | (graticuleIntersectionObj *)msSmallMalloc( |
699 | 0 | sizeof(graticuleIntersectionObj)); |
700 | 0 | msGraticuleLayerInitIntersectionPoints(psValues); |
701 | |
|
702 | 0 | while ((status = msLayerNextShape(layer, &shapegrid)) == MS_SUCCESS) { |
703 | | /*don't really need a class here*/ |
704 | | /* |
705 | | shapegrid.classindex = msShapeGetClass(layer, &shapegrid, map->scaledenom, |
706 | | NULL, 0); if((shapegrid.classindex == -1) || |
707 | | (layer->class[shapegrid.classindex]->status == MS_OFF)) { |
708 | | msFreeShape(&shapegrid); |
709 | | continue; |
710 | | } |
711 | | */ |
712 | |
|
713 | 0 | msInitShape(&tmpshape); |
714 | 0 | msCopyShape(&shapegrid, &tmpshape); |
715 | | /* status = msDrawShape(map, layer, &tmpshape, image, -1); */ |
716 | |
|
717 | 0 | if (layer->project) { |
718 | 0 | if (layer->reprojectorLayerToMap == NULL) { |
719 | 0 | layer->reprojectorLayerToMap = |
720 | 0 | msProjectCreateReprojector(&layer->projection, &map->projection); |
721 | 0 | } |
722 | 0 | if (layer->reprojectorLayerToMap) |
723 | 0 | msProjectShapeEx(layer->reprojectorLayerToMap, &shapegrid); |
724 | 0 | } |
725 | |
|
726 | 0 | msClipPolylineRect(&shapegrid, cliprect); |
727 | |
|
728 | 0 | msTransformShapeToPixelRound(&shapegrid, map->extent, map->cellsize); |
729 | |
|
730 | 0 | if (shapegrid.numlines <= 0 || |
731 | 0 | shapegrid.line[0].numpoints < |
732 | 0 | 2) { /* once clipped the shape didn't need to be drawn */ |
733 | 0 | msFreeShape(&shapegrid); |
734 | 0 | msFreeShape(&tmpshape); |
735 | 0 | continue; |
736 | 0 | } |
737 | | |
738 | 0 | { |
739 | 0 | int iTmpLine = 0; |
740 | 0 | int nNumPoints = 0; |
741 | | /*grid code seems to return lines that can double cross the extenst??*/ |
742 | | /*creating a more than one clipped shape. Take the shape that has the most |
743 | | points, which should be the most likley to be correct*/ |
744 | |
|
745 | 0 | if (shapegrid.numlines > 1) { |
746 | 0 | for (i = 0; i < shapegrid.numlines; i++) { |
747 | 0 | if (shapegrid.line[i].numpoints > nNumPoints) { |
748 | 0 | nNumPoints = shapegrid.line[i].numpoints; |
749 | 0 | iTmpLine = i; |
750 | 0 | } |
751 | 0 | } |
752 | 0 | } |
753 | | /* get the first and last point*/ |
754 | 0 | oFirstPoint.x = shapegrid.line[iTmpLine].point[0].x; |
755 | 0 | oFirstPoint.y = shapegrid.line[iTmpLine].point[0].y; |
756 | 0 | oLineObj = shapegrid.line[iTmpLine]; |
757 | 0 | oLastPoint.x = oLineObj.point[oLineObj.numpoints - 1].x; |
758 | 0 | oLastPoint.y = oLineObj.point[oLineObj.numpoints - 1].y; |
759 | |
|
760 | 0 | if (pInfo->bvertical) { /*vertical*/ |
761 | | /*SHAPES ARE DRAWN FROM BOTTOM TO TOP.*/ |
762 | | /*Normally lines are drawn FROM BOTTOM TO TOP but not always for some |
763 | | reason, so make sure that firstpoint < lastpoint in y, We are in pixel |
764 | | coordinates so y increases as we we go down*/ |
765 | 0 | if (oFirstPoint.y < oLastPoint.y) { |
766 | 0 | dfTmp = oFirstPoint.x; |
767 | 0 | oFirstPoint.x = oLastPoint.x; |
768 | 0 | oLastPoint.x = dfTmp; |
769 | 0 | dfTmp = oFirstPoint.y; |
770 | 0 | oFirstPoint.y = oLastPoint.y; |
771 | 0 | oLastPoint.y = dfTmp; |
772 | 0 | } |
773 | | |
774 | | /*first point should cross the BOTTOM base where y== map->height*/ |
775 | |
|
776 | 0 | if (abs((int)oFirstPoint.y - map->height) <= 1) { |
777 | 0 | char *pszLabel = NULL; |
778 | 0 | oFirstPoint.y = map->height; |
779 | | |
780 | | /*validate point is in map width/height*/ |
781 | 0 | if (oFirstPoint.x < 0 || oFirstPoint.x > map->width) |
782 | 0 | continue; |
783 | | |
784 | | /*validate point is in map width/height*/ |
785 | 0 | if (oLastPoint.x < 0 || oLastPoint.x > map->width) |
786 | 0 | continue; |
787 | | |
788 | 0 | if (shapegrid.text) |
789 | 0 | pszLabel = msStrdup(shapegrid.text); |
790 | 0 | else { |
791 | 0 | _FormatLabel( |
792 | 0 | layer, &tmpshape, |
793 | 0 | tmpshape.line[0].point[tmpshape.line[0].numpoints - 1].x); |
794 | 0 | if (tmpshape.text) |
795 | 0 | pszLabel = msStrdup(tmpshape.text); |
796 | 0 | else |
797 | 0 | pszLabel = msStrdup("unknown"); |
798 | 0 | } |
799 | | /*validate that the value is not already in the array*/ |
800 | 0 | if (psValues->nBottom > 0) { |
801 | | /* if (psValues->pasBottom[psValues->nBottom-1].x == oFirstPoint.x) |
802 | | continue; */ |
803 | |
|
804 | 0 | for (i = 0; i < psValues->nBottom; i++) { |
805 | 0 | if (psValues->pasBottom[i].x == oFirstPoint.x) |
806 | 0 | break; |
807 | 0 | } |
808 | 0 | if (i < psValues->nBottom) { |
809 | 0 | msFree(pszLabel); |
810 | 0 | continue; |
811 | 0 | } |
812 | 0 | } |
813 | 0 | if (psValues->pasBottom == NULL) { |
814 | 0 | psValues->pasBottom = (pointObj *)msSmallMalloc(sizeof(pointObj)); |
815 | 0 | psValues->papszBottomLabels = |
816 | 0 | (char **)msSmallMalloc(sizeof(char *)); |
817 | 0 | psValues->nBottom = 1; |
818 | 0 | } else { |
819 | 0 | psValues->nBottom++; |
820 | 0 | psValues->pasBottom = (pointObj *)msSmallRealloc( |
821 | 0 | psValues->pasBottom, sizeof(pointObj) * psValues->nBottom); |
822 | 0 | psValues->papszBottomLabels = |
823 | 0 | (char **)msSmallRealloc(psValues->papszBottomLabels, |
824 | 0 | sizeof(char *) * psValues->nBottom); |
825 | 0 | } |
826 | |
|
827 | 0 | psValues->pasBottom[psValues->nBottom - 1].x = oFirstPoint.x; |
828 | 0 | psValues->pasBottom[psValues->nBottom - 1].y = oFirstPoint.y; |
829 | 0 | psValues->papszBottomLabels[psValues->nBottom - 1] = pszLabel; |
830 | 0 | } |
831 | | /*first point should cross the TOP base where y==0*/ |
832 | 0 | if (abs((int)oLastPoint.y) <= 1) { |
833 | 0 | char *pszLabel = NULL; |
834 | 0 | oLastPoint.y = 0; |
835 | | |
836 | | /*validate point is in map width/height*/ |
837 | 0 | if (oLastPoint.x < 0 || oLastPoint.x > map->width) |
838 | 0 | continue; |
839 | | |
840 | 0 | if (shapegrid.text) |
841 | 0 | pszLabel = msStrdup(shapegrid.text); |
842 | 0 | else { |
843 | 0 | _FormatLabel( |
844 | 0 | layer, &tmpshape, |
845 | 0 | tmpshape.line[0].point[tmpshape.line[0].numpoints - 1].x); |
846 | 0 | if (tmpshape.text) |
847 | 0 | pszLabel = msStrdup(tmpshape.text); |
848 | 0 | else |
849 | 0 | pszLabel = msStrdup("unknown"); |
850 | 0 | } |
851 | | /*validate if same value is not already there*/ |
852 | 0 | if (psValues->nTop > 0) { |
853 | | /* if (psValues->pasTop[psValues->nTop-1].x == oLastPoint.x) |
854 | | continue; */ |
855 | |
|
856 | 0 | for (i = 0; i < psValues->nTop; i++) { |
857 | 0 | if (psValues->pasTop[i].x == oLastPoint.x || |
858 | 0 | strcasecmp(pszLabel, psValues->papszTopLabels[i]) == 0) |
859 | 0 | break; |
860 | 0 | } |
861 | 0 | if (i < psValues->nTop) { |
862 | 0 | msFree(pszLabel); |
863 | 0 | continue; |
864 | 0 | } |
865 | 0 | } |
866 | | |
867 | 0 | if (psValues->pasTop == NULL) { |
868 | 0 | psValues->pasTop = (pointObj *)msSmallMalloc(sizeof(pointObj)); |
869 | 0 | psValues->papszTopLabels = (char **)msSmallMalloc(sizeof(char *)); |
870 | 0 | psValues->nTop = 1; |
871 | 0 | } else { |
872 | 0 | psValues->nTop++; |
873 | 0 | psValues->pasTop = (pointObj *)msSmallRealloc( |
874 | 0 | psValues->pasTop, sizeof(pointObj) * psValues->nTop); |
875 | 0 | psValues->papszTopLabels = (char **)msSmallRealloc( |
876 | 0 | psValues->papszTopLabels, sizeof(char *) * psValues->nTop); |
877 | 0 | } |
878 | |
|
879 | 0 | psValues->pasTop[psValues->nTop - 1].x = oLastPoint.x; |
880 | 0 | psValues->pasTop[psValues->nTop - 1].y = oLastPoint.y; |
881 | 0 | psValues->papszTopLabels[psValues->nTop - 1] = pszLabel; |
882 | 0 | } |
883 | 0 | } else { /*horizontal*/ |
884 | | /*Normally lines are drawn from left to right but not always for some |
885 | | reason, so make sure that firstpoint < lastpoint in x*/ |
886 | 0 | if (oFirstPoint.x > oLastPoint.x) { |
887 | |
|
888 | 0 | dfTmp = oFirstPoint.x; |
889 | 0 | oFirstPoint.x = oLastPoint.x; |
890 | 0 | oLastPoint.x = dfTmp; |
891 | 0 | dfTmp = oFirstPoint.y; |
892 | 0 | oFirstPoint.y = oLastPoint.y; |
893 | 0 | oLastPoint.y = dfTmp; |
894 | 0 | } |
895 | | /*first point should cross the LEFT base where x=0 axis*/ |
896 | 0 | if (abs((int)oFirstPoint.x) <= 1) { |
897 | 0 | char *pszLabel = NULL; |
898 | 0 | oFirstPoint.x = 0; |
899 | | |
900 | | /*validate point is in map width/height*/ |
901 | 0 | if (oFirstPoint.y < 0 || oFirstPoint.y > map->height) |
902 | 0 | continue; |
903 | | |
904 | 0 | if (shapegrid.text) |
905 | 0 | pszLabel = msStrdup(shapegrid.text); |
906 | 0 | else { |
907 | 0 | _FormatLabel( |
908 | 0 | layer, &tmpshape, |
909 | 0 | tmpshape.line[0].point[tmpshape.line[0].numpoints - 1].y); |
910 | 0 | if (tmpshape.text) |
911 | 0 | pszLabel = msStrdup(tmpshape.text); |
912 | 0 | else |
913 | 0 | pszLabel = msStrdup("unknown"); |
914 | 0 | } |
915 | | |
916 | | /*validate that the previous value is not the same*/ |
917 | 0 | if (psValues->nLeft > 0) { |
918 | | /* if (psValues->pasLeft[psValues->nLeft-1].y == oFirstPoint.y) |
919 | | continue; */ |
920 | |
|
921 | 0 | for (i = 0; i < psValues->nLeft; i++) { |
922 | 0 | if (psValues->pasLeft[i].y == oFirstPoint.y) |
923 | 0 | break; |
924 | 0 | } |
925 | 0 | if (i < psValues->nLeft) { |
926 | 0 | msFree(pszLabel); |
927 | 0 | continue; |
928 | 0 | } |
929 | 0 | } |
930 | 0 | if (psValues->pasLeft == NULL) { |
931 | 0 | psValues->pasLeft = (pointObj *)msSmallMalloc(sizeof(pointObj)); |
932 | 0 | psValues->papszLeftLabels = (char **)msSmallMalloc(sizeof(char *)); |
933 | 0 | psValues->nLeft = 1; |
934 | 0 | } else { |
935 | 0 | psValues->nLeft++; |
936 | 0 | psValues->pasLeft = (pointObj *)msSmallRealloc( |
937 | 0 | psValues->pasLeft, sizeof(pointObj) * psValues->nLeft); |
938 | 0 | psValues->papszLeftLabels = (char **)msSmallRealloc( |
939 | 0 | psValues->papszLeftLabels, sizeof(char *) * psValues->nLeft); |
940 | 0 | } |
941 | |
|
942 | 0 | psValues->pasLeft[psValues->nLeft - 1].x = oFirstPoint.x; |
943 | 0 | psValues->pasLeft[psValues->nLeft - 1].y = oFirstPoint.y; |
944 | 0 | psValues->papszLeftLabels[psValues->nLeft - 1] = |
945 | 0 | pszLabel; /* takes ownership of allocated pszLabel */ |
946 | 0 | } |
947 | | /*first point should cross the RIGHT base where x=map=>width axis*/ |
948 | 0 | if (abs((int)oLastPoint.x - map->width) <= 1) { |
949 | 0 | char *pszLabel = NULL; |
950 | 0 | oLastPoint.x = map->width; |
951 | | |
952 | | /*validate point is in map width/height*/ |
953 | 0 | if (oLastPoint.y < 0 || oLastPoint.y > map->height) |
954 | 0 | continue; |
955 | | |
956 | 0 | if (shapegrid.text) |
957 | 0 | pszLabel = msStrdup(shapegrid.text); |
958 | 0 | else { |
959 | 0 | _FormatLabel( |
960 | 0 | layer, &tmpshape, |
961 | 0 | tmpshape.line[0].point[tmpshape.line[0].numpoints - 1].y); |
962 | 0 | if (tmpshape.text) |
963 | 0 | pszLabel = msStrdup(tmpshape.text); |
964 | 0 | else |
965 | 0 | pszLabel = msStrdup("unknown"); |
966 | 0 | } |
967 | | |
968 | | /*validate that the previous value is not the same*/ |
969 | 0 | if (psValues->nRight > 0) { |
970 | | /* if (psValues->pasRight[psValues->nRight-1].y == oLastPoint.y) |
971 | | continue; */ |
972 | 0 | for (i = 0; i < psValues->nRight; i++) { |
973 | 0 | if (psValues->pasRight[i].y == oLastPoint.y) |
974 | 0 | break; |
975 | 0 | } |
976 | 0 | if (i < psValues->nRight) { |
977 | 0 | msFree(pszLabel); |
978 | 0 | continue; |
979 | 0 | } |
980 | 0 | } |
981 | 0 | if (psValues->pasRight == NULL) { |
982 | 0 | psValues->pasRight = (pointObj *)msSmallMalloc(sizeof(pointObj)); |
983 | 0 | psValues->papszRightLabels = (char **)msSmallMalloc(sizeof(char *)); |
984 | 0 | psValues->nRight = 1; |
985 | 0 | } else { |
986 | 0 | psValues->nRight++; |
987 | 0 | psValues->pasRight = (pointObj *)msSmallRealloc( |
988 | 0 | psValues->pasRight, sizeof(pointObj) * psValues->nRight); |
989 | 0 | psValues->papszRightLabels = (char **)msSmallRealloc( |
990 | 0 | psValues->papszRightLabels, sizeof(char *) * psValues->nRight); |
991 | 0 | } |
992 | |
|
993 | 0 | psValues->pasRight[psValues->nRight - 1].x = oLastPoint.x; |
994 | 0 | psValues->pasRight[psValues->nRight - 1].y = oLastPoint.y; |
995 | 0 | psValues->papszRightLabels[psValues->nRight - 1] = pszLabel; |
996 | 0 | } |
997 | 0 | } |
998 | 0 | msFreeShape(&shapegrid); |
999 | 0 | msFreeShape(&tmpshape); |
1000 | 0 | } |
1001 | 0 | msInitShape(&shapegrid); |
1002 | 0 | } |
1003 | 0 | msLayerClose(layer); |
1004 | 0 | return psValues; |
1005 | 0 | } |
1006 | | |
1007 | | /********************************************************************************************************************** |
1008 | | * |
1009 | | */ |
1010 | 0 | int msGraticuleLayerInitializeVirtualTable(layerObj *layer) { |
1011 | 0 | assert(layer != NULL); |
1012 | 0 | assert(layer->vtable != NULL); |
1013 | | |
1014 | 0 | layer->vtable->LayerInitItemInfo = |
1015 | 0 | msGraticuleLayerInitItemInfo; /* should use defaults for item info |
1016 | | functions */ |
1017 | 0 | layer->vtable->LayerFreeItemInfo = msGraticuleLayerFreeItemInfo; |
1018 | 0 | layer->vtable->LayerOpen = msGraticuleLayerOpen; |
1019 | 0 | layer->vtable->LayerIsOpen = msGraticuleLayerIsOpen; |
1020 | 0 | layer->vtable->LayerWhichShapes = msGraticuleLayerWhichShapes; |
1021 | 0 | layer->vtable->LayerNextShape = msGraticuleLayerNextShape; |
1022 | | /* layer->vtable->LayerResultsGetShape, use default */ |
1023 | 0 | layer->vtable->LayerGetShape = msGraticuleLayerGetShape; |
1024 | | /* layer->vtable->LayerGetShapeCount, use default */ |
1025 | 0 | layer->vtable->LayerClose = msGraticuleLayerClose; |
1026 | 0 | layer->vtable->LayerGetItems = msGraticuleLayerGetItems; |
1027 | 0 | layer->vtable->LayerGetExtent = msGraticuleLayerGetExtent; |
1028 | 0 | layer->vtable->LayerGetAutoStyle = msGraticuleLayerGetAutoStyle; |
1029 | 0 | /* layer->vtable->LayerCloseConnection, use default */; |
1030 | 0 | layer->vtable->LayerSetTimeFilter = msLayerMakePlainTimeFilter; |
1031 | | /* layer->vtable->LayerApplyFilterToLayer, use default */ |
1032 | | /* layer->vtable->LayerCreateItems, use default */ |
1033 | | /* layer->vtable->LayerGetNumFeatures, use default */ |
1034 | |
|
1035 | 0 | return MS_SUCCESS; |
1036 | 0 | } |
1037 | | |
1038 | | /********************************************************************************************************************** |
1039 | | * |
1040 | | */ |
1041 | | static void _FormatLabel(layerObj *pLayer, shapeObj *pShape, |
1042 | 0 | double dDataToFormat) { |
1043 | 0 | graticuleObj *pInfo = pLayer->grid; |
1044 | 0 | char cBuffer[32]; |
1045 | 0 | int iDegrees, iMinutes; |
1046 | |
|
1047 | 0 | switch (pInfo->ilabeltype) { |
1048 | 0 | case lpDDMMSS: |
1049 | 0 | iDegrees = (int)dDataToFormat; |
1050 | 0 | dDataToFormat = fabs(dDataToFormat - (double)iDegrees); |
1051 | 0 | iMinutes = (int)(dDataToFormat * 60.0); |
1052 | 0 | dDataToFormat = dDataToFormat - (((double)iMinutes) / 60.0); |
1053 | 0 | sprintf(cBuffer, pInfo->labelformat, iDegrees, iMinutes, |
1054 | 0 | (int)(dDataToFormat * 3600.0)); |
1055 | 0 | break; |
1056 | 0 | case lpDDMM: |
1057 | 0 | iDegrees = (int)dDataToFormat; |
1058 | 0 | dDataToFormat = fabs(dDataToFormat - (double)iDegrees); |
1059 | 0 | sprintf(cBuffer, pInfo->labelformat, iDegrees, (int)(dDataToFormat * 60.0)); |
1060 | 0 | break; |
1061 | 0 | case lpDD: |
1062 | 0 | iDegrees = (int)dDataToFormat; |
1063 | 0 | sprintf(cBuffer, pInfo->labelformat, iDegrees); |
1064 | 0 | break; |
1065 | 0 | case lpDefault: |
1066 | 0 | default: |
1067 | 0 | sprintf(cBuffer, pInfo->labelformat, dDataToFormat); |
1068 | 0 | } |
1069 | | |
1070 | 0 | pShape->text = msStrdup(cBuffer); |
1071 | 0 | } |
1072 | | |
1073 | | /********************************************************************************************************************** |
1074 | | * |
1075 | | * Move label position into display area by adjusting underlying shape line. |
1076 | | */ |
1077 | | static int _AdjustLabelPosition(layerObj *pLayer, shapeObj *pShape, |
1078 | 0 | msGraticulePosition ePosition) { |
1079 | 0 | graticuleObj *pInfo = pLayer->grid; |
1080 | 0 | rectObj rectLabel; |
1081 | 0 | pointObj ptPoint; |
1082 | 0 | double size = -1; |
1083 | 0 | char *labeltxt; |
1084 | |
|
1085 | 0 | if (pInfo == NULL || pShape == NULL) { |
1086 | 0 | msSetError(MS_MISCERR, |
1087 | 0 | "Assertion failed: Null shape or non-configured grid!, ", |
1088 | 0 | "_AdjustLabelPosition()"); |
1089 | 0 | return MS_FAILURE; |
1090 | 0 | } |
1091 | | |
1092 | 0 | assert(pLayer->class[0] -> numlabels >= 1); |
1093 | | |
1094 | 0 | if (msCheckParentPointer(pLayer->map, "map") == MS_FAILURE) |
1095 | 0 | return MS_FAILURE; |
1096 | | |
1097 | 0 | ptPoint = pShape->line->point[0]; |
1098 | |
|
1099 | 0 | if (pLayer->project) { |
1100 | 0 | if (pLayer->reprojectorLayerToMap == NULL) { |
1101 | 0 | pLayer->reprojectorLayerToMap = msProjectCreateReprojector( |
1102 | 0 | &pLayer->projection, &pLayer->map->projection); |
1103 | 0 | } |
1104 | 0 | if (pLayer->reprojectorLayerToMap) |
1105 | 0 | msProjectShapeEx(pLayer->reprojectorLayerToMap, pShape); |
1106 | | |
1107 | | /* Poor man detection of reprojection failure */ |
1108 | 0 | if (msProjIsGeographicCRS(&(pLayer->projection)) != |
1109 | 0 | msProjIsGeographicCRS(&(pLayer->map->projection))) { |
1110 | 0 | if (ptPoint.x == pShape->line->point[0].x && |
1111 | 0 | ptPoint.y == pShape->line->point[0].y) { |
1112 | 0 | return MS_FAILURE; |
1113 | 0 | } |
1114 | 0 | } |
1115 | 0 | } |
1116 | | |
1117 | 0 | if (pLayer->transform) { |
1118 | 0 | msTransformShapeToPixelRound(pShape, pLayer->map->extent, |
1119 | 0 | pLayer->map->cellsize); |
1120 | 0 | } |
1121 | |
|
1122 | 0 | size = pLayer->class[0] |
1123 | 0 | ->labels[0] |
1124 | 0 | ->size; /* TODO someday: adjust minsize/maxsize/resolution*/ |
1125 | | /* We only use the first label as there's no use (yet) in defining multiple |
1126 | | labels for GRID layers, as the only label to represent is the longitude or |
1127 | | latitude */ |
1128 | 0 | labeltxt = |
1129 | 0 | msShapeGetLabelAnnotation(pLayer, pShape, pLayer->class[0] -> labels[0]); |
1130 | 0 | assert(labeltxt); |
1131 | 0 | if (msGetStringSize(pLayer->map, pLayer->class[0] -> labels[0], size, |
1132 | 0 | labeltxt, &rectLabel) != MS_SUCCESS) { |
1133 | 0 | free(labeltxt); |
1134 | 0 | return MS_FAILURE; /* msSetError already called */ |
1135 | 0 | } |
1136 | 0 | free(labeltxt); |
1137 | |
|
1138 | 0 | switch (ePosition) { |
1139 | 0 | case posBottom: |
1140 | 0 | pShape->line->point[1].y = pLayer->map->height; |
1141 | 0 | pShape->line->point[0].y = |
1142 | 0 | pLayer->map->height - (fabs(rectLabel.maxy - rectLabel.miny) * 2 + 5); |
1143 | 0 | break; |
1144 | 0 | case posTop: |
1145 | 0 | pShape->line->point[1].y = 0; |
1146 | 0 | pShape->line->point[0].y = fabs(rectLabel.maxy - rectLabel.miny) * 2 + 5; |
1147 | 0 | break; |
1148 | 0 | case posLeft: |
1149 | 0 | pShape->line->point[0].x = 0; |
1150 | 0 | pShape->line->point[1].x = fabs(rectLabel.maxx - rectLabel.minx) * 2 + 5; |
1151 | 0 | break; |
1152 | 0 | case posRight: |
1153 | 0 | pShape->line->point[1].x = pLayer->map->width; |
1154 | 0 | pShape->line->point[0].x = |
1155 | 0 | pLayer->map->width - (fabs(rectLabel.maxx - rectLabel.minx) * 2 + 5); |
1156 | 0 | break; |
1157 | 0 | } |
1158 | | |
1159 | 0 | if (pLayer->transform) |
1160 | 0 | msTransformPixelToShape(pShape, pLayer->map->extent, pLayer->map->cellsize); |
1161 | |
|
1162 | 0 | if (pLayer->project) { |
1163 | | /* Clamp coordinates into the validity area of the projection, in the */ |
1164 | | /* particular case of EPSG:3857 (WebMercator) to longlat reprojection */ |
1165 | 0 | if (strstr(pLayer->map->projection.args[0], "epsg:3857") && |
1166 | 0 | msProjIsGeographicCRS(&(pLayer->projection))) { |
1167 | 0 | if (!pLayer->map->projection.gt.need_geotransform && |
1168 | 0 | ePosition == posLeft && pShape->line->point[0].x < -20037508) { |
1169 | 0 | pShape->line->point[1].x = |
1170 | 0 | -20037508 + (pShape->line->point[1].x - pShape->line->point[0].x); |
1171 | 0 | pShape->line->point[0].x = -20037508; |
1172 | 0 | } else if (pLayer->map->projection.gt.need_geotransform && |
1173 | 0 | ePosition == posLeft && |
1174 | 0 | pLayer->map->projection.gt.geotransform[0] + |
1175 | 0 | pShape->line->point[0].x * |
1176 | 0 | pLayer->map->projection.gt.geotransform[1] + |
1177 | 0 | pShape->line->point[0].y * |
1178 | 0 | pLayer->map->projection.gt.geotransform[2] < |
1179 | 0 | -20037508) { |
1180 | 0 | double y_tmp; |
1181 | 0 | double width = pShape->line->point[1].x - pShape->line->point[0].x; |
1182 | |
|
1183 | 0 | y_tmp = pLayer->map->projection.gt.geotransform[3] + |
1184 | 0 | pShape->line->point[0].x * |
1185 | 0 | pLayer->map->projection.gt.geotransform[4] + |
1186 | 0 | pShape->line->point[0].y * |
1187 | 0 | pLayer->map->projection.gt.geotransform[5]; |
1188 | 0 | pShape->line->point[0].x = |
1189 | 0 | pLayer->map->projection.gt.invgeotransform[0] + |
1190 | 0 | -20037508 * pLayer->map->projection.gt.invgeotransform[1] + |
1191 | 0 | y_tmp * pLayer->map->projection.gt.invgeotransform[2]; |
1192 | 0 | pShape->line->point[1].x = pShape->line->point[0].x + width; |
1193 | 0 | } |
1194 | |
|
1195 | 0 | if (!pLayer->map->projection.gt.need_geotransform && |
1196 | 0 | ePosition == posRight && pShape->line->point[1].x > 20037508) { |
1197 | 0 | pShape->line->point[0].x = |
1198 | 0 | 20037508 - (pShape->line->point[1].x - pShape->line->point[0].x); |
1199 | 0 | pShape->line->point[1].x = 20037508; |
1200 | 0 | } else if (pLayer->map->projection.gt.need_geotransform && |
1201 | 0 | ePosition == posRight && |
1202 | 0 | pLayer->map->projection.gt.geotransform[0] + |
1203 | 0 | pShape->line->point[1].x * |
1204 | 0 | pLayer->map->projection.gt.geotransform[1] + |
1205 | 0 | pShape->line->point[1].y * |
1206 | 0 | pLayer->map->projection.gt.geotransform[2] > |
1207 | 0 | 20037508) { |
1208 | 0 | double y_tmp; |
1209 | 0 | double width = pShape->line->point[1].x - pShape->line->point[0].x; |
1210 | |
|
1211 | 0 | y_tmp = pLayer->map->projection.gt.geotransform[3] + |
1212 | 0 | pShape->line->point[1].x * |
1213 | 0 | pLayer->map->projection.gt.geotransform[4] + |
1214 | 0 | pShape->line->point[1].y * |
1215 | 0 | pLayer->map->projection.gt.geotransform[5]; |
1216 | 0 | pShape->line->point[1].x = |
1217 | 0 | pLayer->map->projection.gt.invgeotransform[0] + |
1218 | 0 | 20037508 * pLayer->map->projection.gt.invgeotransform[1] + |
1219 | 0 | y_tmp * pLayer->map->projection.gt.invgeotransform[2]; |
1220 | 0 | pShape->line->point[0].x = pShape->line->point[1].x - width; |
1221 | 0 | } |
1222 | 0 | } |
1223 | |
|
1224 | 0 | if (pLayer->reprojectorMapToLayer == NULL) { |
1225 | 0 | pLayer->reprojectorMapToLayer = msProjectCreateReprojector( |
1226 | 0 | &pLayer->map->projection, &pLayer->projection); |
1227 | 0 | } |
1228 | 0 | if (pLayer->reprojectorMapToLayer) |
1229 | 0 | msProjectShapeEx(pLayer->reprojectorMapToLayer, pShape); |
1230 | 0 | } |
1231 | |
|
1232 | 0 | switch (ePosition) { |
1233 | 0 | case posBottom: |
1234 | 0 | case posTop: |
1235 | 0 | pShape->line->point[1].x = ptPoint.x; |
1236 | 0 | pShape->line->point[0].x = ptPoint.x; |
1237 | 0 | break; |
1238 | 0 | case posLeft: |
1239 | 0 | case posRight: |
1240 | 0 | pShape->line->point[1].y = ptPoint.y; |
1241 | 0 | pShape->line->point[0].y = ptPoint.y; |
1242 | 0 | break; |
1243 | 0 | } |
1244 | | |
1245 | 0 | return MS_SUCCESS; |
1246 | 0 | } |
1247 | | |
1248 | | /********************************************************************************************************************** |
1249 | | ********************************************************************************************************************** |
1250 | | * DefineAxes - Copyright (c) 2000, Michael P.D. Bramley. |
1251 | | * |
1252 | | * Permission is granted to use this code without restriction as long as |
1253 | | * this copyright notice appears in all source files. |
1254 | | * |
1255 | | * Minor tweaks to increment calculations - jnovak |
1256 | | */ |
1257 | 0 | void DefineAxis(int iTickCountTarget, double *Min, double *Max, double *Inc) { |
1258 | | /* define local variables... */ |
1259 | |
|
1260 | 0 | double Test_inc, /* candidate increment value */ |
1261 | 0 | Test_min, /* minimum scale value */ |
1262 | 0 | Test_max, /* maximum scale value */ |
1263 | 0 | Range = *Max - *Min; /* range of data */ |
1264 | |
|
1265 | 0 | int i = 0; /* counter */ |
1266 | | |
1267 | | /* don't create problems -- solve them */ |
1268 | |
|
1269 | 0 | if (Range < 0) { |
1270 | 0 | *Inc = 0; |
1271 | 0 | return; |
1272 | 0 | } |
1273 | | |
1274 | | /* handle special case of repeated values */ |
1275 | | |
1276 | 0 | else if (Range == 0) { |
1277 | 0 | *Min = ceil(*Max) - 1; |
1278 | 0 | *Max = *Min + 1; |
1279 | 0 | *Inc = 1; |
1280 | 0 | return; |
1281 | 0 | } |
1282 | | |
1283 | | /* compute candidate for increment */ |
1284 | | |
1285 | 0 | Test_inc = pow(10.0, ceil(log10(Range / 10))); |
1286 | 0 | if (*Inc > 0 && (Test_inc < *Inc || Test_inc > *Inc)) |
1287 | 0 | Test_inc = *Inc; |
1288 | | |
1289 | | /* establish maximum scale value... */ |
1290 | 0 | Test_max = ((long)(*Max / Test_inc)) * Test_inc; |
1291 | |
|
1292 | 0 | if (Test_max < *Max) |
1293 | 0 | Test_max += Test_inc; |
1294 | | |
1295 | | /* establish minimum scale value... */ |
1296 | 0 | Test_min = Test_max; |
1297 | 0 | do { |
1298 | 0 | ++i; |
1299 | 0 | Test_min -= Test_inc; |
1300 | 0 | } while (Test_min > *Min); |
1301 | | |
1302 | | /* subtracting small values can screw up the scale limits, */ |
1303 | | /* eg: if DefineAxis is called with (min,max)=(0.01, 0.1), */ |
1304 | | /* then the calculated scale is 1.0408E17 TO 0.05 BY 0.01. */ |
1305 | | /* the following if statement corrects for this... */ |
1306 | | |
1307 | | /* if(fabs(Test_min) < 1E-10) */ |
1308 | | /* Test_min = 0 ; */ |
1309 | | |
1310 | | /* adjust for too few tick marks */ |
1311 | |
|
1312 | 0 | if (iTickCountTarget <= 0) |
1313 | 0 | iTickCountTarget = MAPGRATICULE_ARC_MINIMUM; |
1314 | |
|
1315 | 0 | while (i < iTickCountTarget) { |
1316 | 0 | Test_inc /= 2; |
1317 | 0 | i *= 2; |
1318 | 0 | } |
1319 | |
|
1320 | 0 | if (i < 6 && 0) { |
1321 | 0 | Test_inc /= 2; |
1322 | 0 | if ((Test_min + Test_inc) <= *Min) |
1323 | 0 | Test_min += Test_inc; |
1324 | 0 | if ((Test_max - Test_inc) >= *Max) |
1325 | 0 | Test_max -= Test_inc; |
1326 | 0 | } |
1327 | | |
1328 | | /* pass back axis definition to caller */ |
1329 | |
|
1330 | 0 | *Min = Test_min; |
1331 | 0 | *Max = Test_max; |
1332 | 0 | *Inc = Test_inc; |
1333 | 0 | } |
1334 | | |
1335 | | /********************************************************************************************************************** |
1336 | | **********************************************************************************************************************/ |