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