Logo Search packages:      
Sourcecode: mapserver version File versions  Download package

mapscale.c

/******************************************************************************
 *
 * Project:  MapServer
 * Purpose:  Scale object rendering. 
 * Author:   Steve Lime and the MapServer team.
 *
 ******************************************************************************
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in 
 * all copies of this Software or works derived from this Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ******************************************************************************
 *
 * $Log: mapscale.c,v $
 * Revision 1.52  2006/08/26 22:04:13  novak
 * Enable support for TrueType fonts (bug 1882)
 *
 * Revision 1.51  2006/03/27 05:48:03  sdlime
 * Fixed symbol initialization error with embedded scalebars and legends. (bug 1725)
 *
 * Revision 1.50  2006/02/18 20:59:13  sdlime
 * Initial code for curved labels. (bug 1620)
 *
 * Revision 1.49  2005/09/27 15:27:18  sean
 * Fixed typo to prevent extra scalebar layer creation (bug 1480)
 *
 * Revision 1.48  2005/06/14 16:03:34  dan
 * Updated copyright date to 2005
 *
 * Revision 1.47  2005/02/18 03:06:47  dan
 * Turned all C++ (//) comments into C comments (bug 1238)
 *
 * Revision 1.46  2005/01/11 00:24:07  frank
 * added labelObj arg to msAddLabel()
 *
 * Revision 1.45  2004/11/05 19:22:19  frank
 * ensure state is initialized in msDrawScalebar() (all cases)
 *
 * Revision 1.44  2004/10/21 04:30:56  frank
 * Added standardized headers.  Added MS_CVSID().
 *
 */

#include "map.h"

MS_CVSID("$Id: mapscale.c,v 1.52 2006/08/26 22:04:13 novak Exp $")

#define VMARGIN 3 /* buffer around the scalebar */
#define HMARGIN 3
#define VSPACING .8 /* spacing (% of font height) between scalebar and text */
#define VSLOP 5 /* makes things fit a bit better vertically */

/*
** Match this with with unit enumerations is map.h
*/
static char *unitText[5]={"in", "ft", "mi", "m", "km"};
double inchesPerUnit[6]={1, 12, 63360.0, 39.3701, 39370.1, 4374754};


static double roundInterval(double d)
{
  if(d<.001)
    return(MS_NINT(d*10000)/10000.0);
  if(d<.01)
    return(MS_NINT(d*1000)/1000.0);
  if(d<.1)
    return(MS_NINT(d*100)/100.0);
  if(d<1)
    return(MS_NINT(d*10)/10.0);
  if(d<100)
    return(MS_NINT(d));
  if(d<1000)
    return(MS_NINT(d/10) * 10);
  if(d<10000)
    return(MS_NINT(d/100) * 100);
  if(d<100000)
    return(MS_NINT(d/1000) * 1000);
  if(d<1000000)
    return(MS_NINT(d/10000) * 10000);

  return(-1);
}

/*
** Calculate the approximate scale based on a few parameters. Note that this assumes the scale is
** the same in the x direction as in the y direction, so run msAdjustExtent(...) first.
*/
int msCalculateScale(rectObj extent, int units, int width, int height, double resolution, double *scale)
{
  double md, gd, center_y;

  /* if((extent.maxx == extent.minx) || (extent.maxy == extent.miny))   */
  if(!MS_VALID_EXTENT(extent)) {
    msSetError(MS_MISCERR, "Invalid image extent, minx=%lf, miny=%lf, maxx=%lf, maxy=%lf.", "msCalculateScale()", extent.minx, extent.miny, extent.maxx, extent.maxy);
    return(MS_FAILURE);
  }
  
  if((width <= 0) || (height <= 0)) {
    msSetError(MS_MISCERR, "Invalid image width or height.", "msCalculateScale()");
    return(MS_FAILURE);
  }

  switch (units) {
  case(MS_DD):
  case(MS_METERS):    
  case(MS_KILOMETERS):
  case(MS_MILES):
  case(MS_INCHES):  
  case(MS_FEET):
    center_y = (extent.miny+extent.maxy)/2.0;
    md = width/(resolution*msInchesPerUnit(units, center_y)); /* was (width-1) */
    gd = extent.maxx - extent.minx;
    *scale = gd/md;
    break;
  default:
    *scale = -1; /* this is not an error */
    break;
  }

  return(MS_SUCCESS);
}

double msInchesPerUnit(int units, double center_lat)
{
  double lat_adj = 1.0, ipu = 1.0;

  switch (units) {
  case(MS_METERS):    
  case(MS_KILOMETERS):
  case(MS_MILES):
  case(MS_INCHES):  
  case(MS_FEET):
    ipu = inchesPerUnit[units]; 
    break;
  case(MS_DD):
    /* With geographical (DD) coordinates, we adjust the inchesPerUnit
     * based on the latitude of the center of the view. For this we assume
     * we have a perfect sphere and just use cos(lat) in our calculation.
     */
#ifdef ENABLE_VARIABLE_INCHES_PER_DEGREE
    if (center_lat != 0.0)
    {
        double cos_lat;
        cos_lat = cos(MS_PI*center_lat/180.0);
        lat_adj = sqrt(1+cos_lat*cos_lat)/sqrt(2.0);
    }
#endif
    ipu = inchesPerUnit[units]*lat_adj;
    break;
  default:
    break;
  }

  return ipu;
}


#define X_STEP_SIZE 5

/* TODO : the will be msDrawScalebarGD */
imageObj *msDrawScalebar(mapObj *map)
{
  int status;
  gdImagePtr img=NULL;
  char label[32];
  double i, msx;
  int j;
  int isx, sx, sy, ox, oy, state, dsx;
  pointObj p;
  gdFontPtr fontPtr = NULL;
  imageObj      *image = NULL;
  outputFormatObj *format = NULL;
  int iFreeGDFont = 0;

  if(map->units == -1) {
    msSetError(MS_MISCERR, "Map units not set.", "msDrawScalebar()");
    return(NULL);
  }
/*
 *  Allow scalebars to use TrueType fonts for labels (jnovak@novacell.com)
 *
 *  A string containing the ten decimal digits is rendered to compute an average cell size 
 *  for each number, which is used later to place labels on the scalebar.
 */
  if( map->scalebar.label.type == MS_TRUETYPE )
  {
#ifdef USE_GD_FT
      int bbox[8];
    char *error   = NULL;
    char *font    = NULL;
    char szTestString[] = "0123456789";

    fontPtr = (gdFontPtr) malloc( sizeof( gdFont ) );

      if(!fontPtr) {
      msSetError(MS_TTFERR, "fontPtr allocation failed.", "msDrawScalebar()");
      return(NULL);
    };

    if(! map->fontset.filename ) {
      msSetError(MS_TTFERR, "No fontset defined.", "msDrawScalebar()");
        free( fontPtr );
      return(NULL);
    }

    if(! map->scalebar.label.font) {
      msSetError(MS_TTFERR, "No TrueType font defined.", "msDrawScalebar()");
        free( fontPtr );
      return(NULL);
    }

    font = msLookupHashTable(&(map->fontset.fonts), map->scalebar.label.font);
    if(!font) {
      msSetError(MS_TTFERR, "Requested font (%s) not found.", "msDrawScalebar()", map->scalebar.label.font);
        free( fontPtr );
      return(NULL);
    }

    error = gdImageStringFT( NULL, bbox, map->scalebar.label.outlinecolor.pen, font, map->scalebar.label.size, 0, 0, 0, szTestString );

    if(error) {
      msSetError(MS_TTFERR, "gdImageStringFT returned error %d.", "msDrawScalebar()", error );
        free( fontPtr );
      return(NULL);
    }

      iFreeGDFont = 1;
      fontPtr->w  = (bbox[2] - bbox[0]) / strlen( szTestString );
      fontPtr->h  = (bbox[3] - bbox[5]);
#else
    msSetError(MS_TTFERR, "TrueType font support required.", "msDrawScalebar()");
    return(NULL);
#endif
  }
  else
    fontPtr = msGetBitmapFont(map->scalebar.label.size);

  if(!fontPtr) return(NULL);

  map->cellsize = msAdjustExtent(&(map->extent), map->width, map->height);
  status = msCalculateScale(map->extent, map->units, map->width, map->height, map->resolution, &map->scale);
  if(status != MS_SUCCESS)
  {   
    if( iFreeGDFont ) free( fontPtr );
      return(NULL);
  }
  dsx = map->scalebar.width - 2*HMARGIN;
  do {
    msx = (map->cellsize * dsx)/(msInchesPerUnit(map->scalebar.units,0)/msInchesPerUnit(map->units,0));
    i = roundInterval(msx/map->scalebar.intervals);
    sprintf(label, "%g", map->scalebar.intervals*i); /* last label */
    isx = MS_NINT((i/(msInchesPerUnit(map->units,0)/msInchesPerUnit(map->scalebar.units,0)))/map->cellsize);  
    sx = (map->scalebar.intervals*isx) + MS_NINT((1.5 + strlen(label)/2.0 + strlen(unitText[map->scalebar.units]))*fontPtr->w);

    if(sx <= (map->scalebar.width - 2*HMARGIN)) break; /* it will fit */

    dsx -= X_STEP_SIZE; /* change the desired size in hopes that it will fit in user supplied width */
  } while(1);

  sy = (2*VMARGIN) + MS_NINT(VSPACING*fontPtr->h) + fontPtr->h + map->scalebar.height - VSLOP;

  /*Ensure we have an image format representing the options for the scalebar.*/
  msApplyOutputFormat( &format, map->outputformat, 
                       map->scalebar.transparent, 
                       map->scalebar.interlace, 
                       MS_NOOVERRIDE );

   /* create image */
  image = msImageCreateGD(map->scalebar.width, sy, format,
                          map->web.imagepath, map->web.imageurl);

  /* drop this reference to output format */
  msApplyOutputFormat( &format, NULL, 
                       MS_NOOVERRIDE, MS_NOOVERRIDE, MS_NOOVERRIDE );

  /* did we succeed in creating the image? */
  if(image)
    img = image->img.gd;
  else {
    msSetError(MS_GDERR, "Unable to initialize image.", "msDrawScalebar()");
      
    if( iFreeGDFont ) free( fontPtr );

    return(NULL);
  }

  msImageInitGD( image, &(map->scalebar.imagecolor));
  
  if (map->outputformat->imagemode == MS_IMAGEMODE_RGB || map->outputformat->imagemode == MS_IMAGEMODE_RGBA) gdImageAlphaBlending(image->img.gd, 1);

  ox = MS_NINT((map->scalebar.width - sx)/2.0 + fontPtr->w/2.0); /* center the computed scalebar */
  oy = VMARGIN;

  /* turn RGB colors into indexed values */
  msImageSetPenGD(img, &(map->scalebar.backgroundcolor));
  msImageSetPenGD(img, &(map->scalebar.color));
  msImageSetPenGD(img, &(map->scalebar.outlinecolor));

  switch(map->scalebar.style) {
  case(0):
    
    state = 1; /* 1 means filled */
    for(j=0; j<map->scalebar.intervals; j++) {
      if(state == 1)
      gdImageFilledRectangle(img, ox + j*isx, oy, ox + (j+1)*isx, oy + map->scalebar.height, map->scalebar.color.pen);
      else
      if(map->scalebar.backgroundcolor.pen >= 0) gdImageFilledRectangle(img, ox + j*isx, oy, ox + (j+1)*isx, oy + map->scalebar.height, map->scalebar.backgroundcolor.pen);

      if(map->scalebar.outlinecolor.pen >= 0)
      gdImageRectangle(img, ox + j*isx, oy, ox + (j+1)*isx, oy + map->scalebar.height, map->scalebar.outlinecolor.pen);

      sprintf(label, "%g", j*i);
      map->scalebar.label.position = MS_CC;
      p.x = ox + j*isx; /* + MS_NINT(fontPtr->w/2); */
      p.y = oy + map->scalebar.height + MS_NINT(VSPACING*fontPtr->h);
      /* TODO */
      msDrawLabel(image, p, label, &(map->scalebar.label), &(map->fontset), 1.0);

      state = -state;
    }
    sprintf(label, "%g", j*i);
    ox = ox + j*isx - MS_NINT((strlen(label)*fontPtr->w)/2.0);
    sprintf(label, "%g %s", j*i, unitText[map->scalebar.units]);
    map->scalebar.label.position = MS_CR;
    p.x = ox; /* + MS_NINT(fontPtr->w/2); */
    p.y = oy + map->scalebar.height + MS_NINT(VSPACING*fontPtr->h);
    /* TODO */
    msDrawLabel(image, p, label, &(map->scalebar.label), &(map->fontset), 1.0);
    break;

  case(1):

    gdImageLine(img, ox, oy, ox + isx*map->scalebar.intervals, oy, map->scalebar.color.pen); /* top line */

    state = 1; /* 1 means filled */
    for(j=0; j<map->scalebar.intervals; j++) {

      gdImageLine(img, ox + j*isx, oy, ox + j*isx, oy + map->scalebar.height, map->scalebar.color.pen); /* tick */
      
      sprintf(label, "%g", j*i);
      map->scalebar.label.position = MS_CC;
      p.x = ox + j*isx; /* + MS_NINT(fontPtr->w/2); */
      p.y = oy + map->scalebar.height + MS_NINT(VSPACING*fontPtr->h);
      /* TODO */
      msDrawLabel(image, p, label, &(map->scalebar.label), &(map->fontset), 1.0);

      state = -state;
    }
    
    gdImageLine(img, ox + j*isx, oy, ox + j*isx, oy + map->scalebar.height, map->scalebar.color.pen); /* last tick */

    sprintf(label, "%g", j*i);
    ox = ox + j*isx - MS_NINT((strlen(label)*fontPtr->w)/2.0);
    sprintf(label, "%g %s", j*i, unitText[map->scalebar.units]);
    map->scalebar.label.position = MS_CR;
    p.x = ox; /* + MS_NINT(fontPtr->w/2); */
    p.y = oy + map->scalebar.height + MS_NINT(VSPACING*fontPtr->h);
    /* TODO */
    msDrawLabel(image, p, label, &(map->scalebar.label), &(map->fontset), 1.0);
    break;
  default:
    msSetError(MS_MISCERR, "Unsupported scalebar style.", "msDrawScalebar()");
    return(NULL);

    if( iFreeGDFont ) free( fontPtr );
    break;
  }

  msClearScalebarPenValues( &(map->scalebar));

  if( iFreeGDFont ) free( fontPtr );

  return(image);

}

int msEmbedScalebar(mapObj *map, gdImagePtr img)
{
  int s,l;
  pointObj point;
  imageObj *image = NULL;

  s = msGetSymbolIndex(&(map->symbolset), "scalebar", MS_FALSE);
  if(s == -1) {
    s = map->symbolset.numsymbols;
    map->symbolset.numsymbols++;
    initSymbol(&(map->symbolset.symbol[s]));
  } else {
    if(map->symbolset.symbol[s].img) 
      gdImageDestroy(map->symbolset.symbol[s].img);
  }
  
  image = msDrawScalebar(map);
  map->symbolset.symbol[s].img =  image->img.gd; /* TODO  */
  if(!map->symbolset.symbol[s].img) return(-1); /* something went wrong creating scalebar */

  map->symbolset.symbol[s].type = MS_SYMBOL_PIXMAP; /* intialize a few things */
  map->symbolset.symbol[s].name = strdup("scalebar");  
  map->symbolset.symbol[s].sizex = map->symbolset.symbol[s].img->sx;
  map->symbolset.symbol[s].sizey = map->symbolset.symbol[s].img->sy;

  /* in 8 bit, mark 0 as being transparent.  In 24bit hopefully already
     have transparency enabled ... */
  if(map->scalebar.transparent == MS_ON 
     && !gdImageTrueColor(image->img.gd ) )
    gdImageColorTransparent(map->symbolset.symbol[s].img, 0);

  switch(map->scalebar.position) {
  case(MS_LL):
    point.x = MS_NINT(map->symbolset.symbol[s].img->sx/2.0);
    point.y = map->height - MS_NINT(map->symbolset.symbol[s].img->sy/2.0);
    break;
  case(MS_LR):
    point.x = map->width - MS_NINT(map->symbolset.symbol[s].img->sx/2.0);
    point.y = map->height - MS_NINT(map->symbolset.symbol[s].img->sy/2.0);
    break;
  case(MS_LC):
    point.x = MS_NINT(map->width/2.0);
    point.y = map->height - MS_NINT(map->symbolset.symbol[s].img->sy/2.0);
    break;
  case(MS_UR):
    point.x = map->width - MS_NINT(map->symbolset.symbol[s].img->sx/2.0);
    point.y = MS_NINT(map->symbolset.symbol[s].img->sy/2.0);
    break;
  case(MS_UL):
    point.x = MS_NINT(map->symbolset.symbol[s].img->sx/2.0);
    point.y = MS_NINT(map->symbolset.symbol[s].img->sy/2.0);
    break;
  case(MS_UC):
    point.x = MS_NINT(map->width/2.0);
    point.y = MS_NINT(map->symbolset.symbol[s].img->sy/2.0);
    break;
  }

  l = msGetLayerIndex(map, "__embed__scalebar");
  if(l == -1) {
    l = map->numlayers;
    map->numlayers++;

    if(initLayer(&(map->layers[l]), map) == -1) return(-1);
    map->layers[l].name = strdup("__embed__scalebar");
    map->layers[l].type = MS_LAYER_ANNOTATION;

    if(initClass(&(map->layers[l].class[0])) == -1) return(-1);
    map->layers[l].numclasses = 1; /* so we make sure to free it */
    
    /* update the layer order list with the layer's index. */
    map->layerorder[l] = l;
  }

  /* to resolve bug 490 */
  map->layers[l].transparency = MS_GD_ALPHA;
  
  map->layers[l].status = MS_ON;

  map->layers[l].class[0].numstyles = 1;
  map->layers[l].class[0].styles[0].symbol = s;
  map->layers[l].class[0].styles[0].color.pen = -1;
  map->layers[l].class[0].label.force = MS_TRUE;
  map->layers[l].class[0].label.size = MS_MEDIUM; /* must set a size to have a valid label definition */

  if(map->scalebar.postlabelcache) /* add it directly to the image //TODO */
  {
    msDrawMarkerSymbolGD(&map->symbolset, img, &point, &(map->layers[l].class[0].styles[0]), 1.0);
  }
  else
    msAddLabel(map, l, 0, -1, -1, &point, NULL, " ", 1.0, NULL);

  /* Mark layer as deleted so that it doesn't interfere with html legends or */
  /* with saving maps */
  map->layers[l].status = MS_DELETE;

  /* Free image (but keep the GD image which is in the symbol cache now) */
  image->img.gd = NULL;
  msFreeImage( image );

  return(0);
}

Generated by  Doxygen 1.6.0   Back to index