[mapserver-commits] r9816 - trunk/mapserver

svn at osgeo.org svn at osgeo.org
Sat Feb 20 13:38:16 EST 2010


Author: pramsey
Date: 2010-02-20 13:38:16 -0500 (Sat, 20 Feb 2010)
New Revision: 9816

Modified:
   trunk/mapserver/HISTORY.TXT
   trunk/mapserver/mapserv.c
   trunk/mapserver/maptile.c
   trunk/mapserver/maptile.h
Log:
Support metatiling and buffers in mode=tile (#3323)


Modified: trunk/mapserver/HISTORY.TXT
===================================================================
--- trunk/mapserver/HISTORY.TXT	2010-02-20 18:36:27 UTC (rev 9815)
+++ trunk/mapserver/HISTORY.TXT	2010-02-20 18:38:16 UTC (rev 9816)
@@ -14,6 +14,8 @@
 Current Version (SVN trunk):
 ----------------------------
 
+- Support metatiling and buffers in mode=tile (#3323)
+
 - Enhance error messages in msGetTruetypeTextBBox() (#3314)
 
 - Report parsing errors in INCUDEd files using the line number within the file (#3329)

Modified: trunk/mapserver/mapserv.c
===================================================================
--- trunk/mapserver/mapserv.c	2010-02-20 18:36:27 UTC (rev 9815)
+++ trunk/mapserver/mapserv.c	2010-02-20 18:38:16 UTC (rev 9816)
@@ -1389,14 +1389,6 @@
       setExtent(mapserv);
       checkWebScale(mapserv);
       
-      /*
-      ** We set tile extents here instead of setExtent so that all the 
-      ** non-CGI utilities don't require maptile.o in their build.
-      */
-      if( mapserv->Mode == TILE ) {
-        msTileSetExtent(mapserv);
-      }
-            
       switch(mapserv->Mode) {
       case MAP:
         if(QueryFile) {
@@ -1414,8 +1406,8 @@
         img = msDrawScalebar(mapserv->map);
         break;
       case TILE:
-        /* TODO: we may need an msDrawTile for doing "draw large then clip" tricks */
-        img = msDrawMap(mapserv->map, MS_FALSE);
+        msTileSetExtent(mapserv);
+        img = msTileDraw(mapserv);
         break;
       }
       

Modified: trunk/mapserver/maptile.c
===================================================================
--- trunk/mapserver/maptile.c	2010-02-20 18:36:27 UTC (rev 9815)
+++ trunk/mapserver/maptile.c	2010-02-20 18:38:16 UTC (rev 9816)
@@ -30,7 +30,188 @@
 #include "maptile.h"
 #include "mapproject.h"
 
+static void msTileResetMetatileLevel(mapObj *map) {
+  hashTableObj *meta = &(map->web.metadata); 
+  const char *zero = "0";
+  const char *value = NULL;
+
+  /*  Is the tile_metatile_levetl set... */
+  if((value = msLookupHashTable(meta, "tile_metatile_level")) != NULL) {
+    msRemoveHashTable(meta, "tile_metatile_level");
+    msInsertHashTable(meta, "tile_metatile_level", zero);
+  }
+  /* No tile_metatile_level value. */
+  else {
+    msInsertHashTable(meta, "tile_metatile_level", zero);
+  }
+}
+
 /************************************************************************
+ *                            msTileGetGMapCoords                       *
+ ************************************************************************/
+static int msTileGetGMapCoords(const char *coordstring, int *x, int *y, int *zoom) {
+
+  int num_coords = 0;
+  char **coords = NULL;
+  
+  if( coordstring ) {
+    coords = msStringSplit(coordstring, ' ', &(num_coords));
+    if( num_coords != 3 ) {
+      msSetError(MS_WEBERR, "Invalid number of tile coordinates (should be three).", "msTileSetup()");
+      return MS_FAILURE;
+    }
+  } 
+  else {
+    msSetError(MS_WEBERR, "Tile parameter not set.", "msTileSetup()");
+    return MS_FAILURE;
+  }
+  
+  if( x )
+    *x = strtol(coords[0], NULL, 10);
+  if( y )
+    *y = strtol(coords[1], NULL, 10);
+  if( zoom )
+    *zoom = strtol(coords[2], NULL, 10);
+ 
+  return MS_SUCCESS;
+}
+
+
+/************************************************************************
+ *                            msTileSetParams                           *
+ ************************************************************************/
+static void msTileGetParams(mapObj *map, tileParams *params) {
+
+  const char *value = NULL;
+  hashTableObj *meta = &(map->web.metadata); 
+  
+  params->tile_size = SPHEREMERC_IMAGE_SIZE;
+  
+  /* Check for tile buffer, set to buffer==0 as default */
+  if((value = msLookupHashTable(meta, "tile_map_edge_buffer")) != NULL) {
+    params->map_edge_buffer = atoi(value);
+    if(map->debug)
+      msDebug("msTileSetParams(): tile_map_edge_buffer = %d\n", params->map_edge_buffer);
+  }  
+  else
+    params->map_edge_buffer = 0;    
+
+  /* Check for metatile size, set to tile==metatile as default */
+  if((value = msLookupHashTable(meta, "tile_metatile_level")) != NULL) {
+    params->metatile_level = atoi(value);
+    /* Quietly force metatile_level to be sane */
+    if( params->metatile_level < 0 ) 
+      params->metatile_level = 0;
+    if( params->metatile_level > 2 ) 
+      params->metatile_level = 2;
+    if(map->debug)
+      msDebug("msTileSetParams(): tile_metatile_level = %d\n", params->metatile_level);
+  }  
+  else
+    params->metatile_level = 0;
+  
+}
+
+/************************************************************************
+ *                            msTileExtractSubTile                      *
+ *                                                                      * 
+ ************************************************************************/
+static imageObj* msTileExtractSubTile(const mapservObj *msObj, const imageObj *img) {
+ 
+  int width, height, mini, minj, maxi, maxj;
+  int zoom = 2;
+  imageObj* imgOut = NULL;
+  tileParams params;
+
+  /*
+  ** Load the metatiling information from the map file.
+  */
+  msTileGetParams(msObj->map, &params);
+
+  /*
+  ** Initialize values for the metatile clip area.
+  */
+  width = img->width - 2*params.map_edge_buffer;
+  height = img->height - 2*params.map_edge_buffer;
+  mini = params.map_edge_buffer;
+  minj = params.map_edge_buffer;
+  maxi = img->width - params.map_edge_buffer - 1;
+  maxj = img->height - params.map_edge_buffer - 1;
+
+  if( msObj->TileMode == TILE_GMAP ) {
+    int x, y, zoom;
+    
+    if( msObj->TileCoords ) {
+      if( msTileGetGMapCoords(msObj->TileCoords, &x, &y, &zoom) == MS_FAILURE )
+        return NULL;
+    } 
+    else {
+      msSetError(MS_WEBERR, "Tile parameter not set.", "msTileSetup()");
+      return NULL;
+    }    
+
+    if(msObj->map->debug)
+      msDebug("msTileExtractSubTile(): gmaps coords (x: %d, y: %d)\n",x,y);
+
+    /*
+    ** The bottom N bits of the coordinates give us the subtile
+    ** location relative to the metatile.
+    */
+    x = (0xffff ^ (0xffff << params.metatile_level)) & x;
+    y = (0xffff ^ (0xffff << params.metatile_level)) & y;
+
+    if(msObj->map->debug)
+      msDebug("msTileExtractSubTile(): gmaps image coords (x: %d, y: %d)\n",x,y);
+    
+    mini = mini + x * params.tile_size;
+    minj = minj + y * params.tile_size;
+
+  }
+  else if( msObj->TileMode == TILE_VE ) {
+    int tsize;
+    int i = 0;
+    char j = 0;
+  
+    if( strlen( msObj->TileCoords ) - params.metatile_level < 0 ) {
+      return(NULL);
+    }
+  
+    /* 
+    ** Process the last elements of the VE coordinate string to place the
+    ** requested tile in the context of the metatile
+    */
+    for( i = strlen( msObj->TileCoords ) - params.metatile_level; 
+         i < strlen( msObj->TileCoords ); 
+         i++ ) 
+    {
+      j = msObj->TileCoords[i];
+      tsize = width / zoom;
+      if( j == '1' || j == '3' ) mini += tsize;
+      if( j == '2' || j == '3' ) minj += tsize;
+      zoom *= 2;
+    }
+  }
+  else {
+    return(NULL); /* Huh? Should have a mode. */
+  }
+
+  imgOut = msImageCreate(params.tile_size, params.tile_size, msObj->map->outputformat, NULL, NULL, msObj->map);
+
+  /* We're going to do the image copy using gd */
+  if( imgOut->img.gd == NULL ) {
+    return NULL;
+  }
+  
+  if(msObj->map->debug)
+    msDebug("msTileExtractSubTile(): extracting (%d x %d) tile, top corner (%d, %d)\n",params.tile_size,params.tile_size,mini,minj);
+
+  gdImageCopy(imgOut->img.gd, img->img.gd, 0, 0, mini, minj, params.tile_size, params.tile_size);
+
+  return imgOut;
+}
+
+
+/************************************************************************
  *                            msTileSetup                               *
  *                                                                      * 
  *  Called from mapserv.c, this is where the fun begins                 *
@@ -40,6 +221,12 @@
 #ifdef USE_TILE_API 
 
   char *outProjStr = NULL;
+  tileParams params;
+  
+  /*
+  ** Load the metatiling information from the map file.
+  */
+  msTileGetParams(msObj->map, &params);
 
   /* 
   ** Ensure all the LAYERs have a projection. 
@@ -54,11 +241,11 @@
   if( msObj->TileMode == TILE_GMAP || msObj->TileMode == TILE_VE ) {
     outProjStr = SPHEREMERC_PROJ4;
   } else {
-    return(MS_FAILURE); /* Huh? No mode? */
+    return MS_FAILURE; /* Huh? No mode? */
   }
   if( msLoadProjectionString(&(msObj->map->projection), outProjStr) != 0 ) {
     msSetError(MS_CGIERR, "Unable to load projection string.", "msTileSetup()");
-    return(MS_FAILURE);
+    return MS_FAILURE;
   }
   
   /*
@@ -66,27 +253,22 @@
   */
   if( msObj->TileMode == TILE_GMAP ) {
 
-    int num_coords = 0;
-    char **coords = NULL;
     int x, y, zoom;
     double zoomfactor;
     
     if( msObj->TileCoords ) {
-      coords = msStringSplit(msObj->TileCoords, ' ', &(num_coords));
-      if( num_coords != 3 ) {
-        msSetError(MS_WEBERR, "Invalid number of tile coordinates (should be three).", "msTileSetup()");
-        return(MS_FAILURE);
-      }
+      if( msTileGetGMapCoords(msObj->TileCoords, &x, &y, &zoom) == MS_FAILURE )
+        return MS_FAILURE;
     } 
     else {
       msSetError(MS_WEBERR, "Tile parameter not set.", "msTileSetup()");
-      return(MS_FAILURE);
+      return MS_FAILURE;
     }
     
-    x = strtol(coords[0], NULL, 10);
-    y = strtol(coords[1], NULL, 10);
-    zoom = strtol(coords[2], NULL, 10);
-
+    if( params.metatile_level >= zoom ) {
+      msTileResetMetatileLevel(msObj->map);
+    }
+    
     zoomfactor = pow(2.0, (double)zoom);
     
     /*
@@ -109,6 +291,10 @@
       return(MS_FAILURE);
     }
 
+    if( params.metatile_level >= strlen(msObj->TileCoords) ) {
+      msTileResetMetatileLevel(msObj->map);
+    }
+
   }
   else {
     return(MS_FAILURE); /* Huh? Should have a mode. */
@@ -121,6 +307,8 @@
 #endif
 }
 
+
+
 /************************************************************************
  *                            msTileSetExtent                           *
  *                                                                      * 
@@ -131,36 +319,46 @@
 #ifdef USE_TILE_API 
   
   mapObj *map = msObj->map;
-  double	dx, dy;
+  double	dx, dy, buffer;
+  tileParams params;
+  
+  /* Read the tile-mode map file parameters */
+  msTileGetParams(msObj->map, &params);
 
   if( msObj->TileMode == TILE_GMAP ) {
-
-    int num_coords = 0;
-    char **coords = NULL;
     int x, y, zoom;
     double zoomfactor, tilesize, xmin, xmax, ymin, ymax;
     
     if( msObj->TileCoords ) {
-      coords = msStringSplit(msObj->TileCoords, ' ', &(num_coords));
-      if( num_coords != 3 ) {
-        msSetError(MS_WEBERR, "Invalid number of tile coordinates (should be three).", "msTileSetExtent()");
-        return(MS_FAILURE);
-      }
+      if( msTileGetGMapCoords(msObj->TileCoords, &x, &y, &zoom) == MS_FAILURE )
+        return MS_FAILURE;
     } 
     else {
       msSetError(MS_WEBERR, "Tile parameter not set.", "msTileSetup()");
-      return(MS_FAILURE);
+      return MS_FAILURE;
     }    
 
-    x = strtol(coords[0], NULL, 10);
-    y = strtol(coords[1], NULL, 10);
-    zoom = strtol(coords[2], NULL, 10);
+    if(map->debug)
+      msDebug("msTileSetExtent(): gmaps coords (x: %d, y: %d, z: %d)\n",x,y,zoom);
 
+    /*
+    ** If we are metatiling, adjust the zoom level appropriately,
+    ** then scale back the x/y coordinates to match the new level.
+    */
+    if( params.metatile_level > 0 ) {
+      zoom = zoom - params.metatile_level;
+      x = x >> params.metatile_level;
+      y = y >> params.metatile_level;
+    }
+
+    if(map->debug)
+      msDebug("msTileSetExtent(): gmaps metacoords (x: %d, y: %d, z: %d)\n",x,y,zoom);
+
     zoomfactor = pow(2.0, (double)zoom);
     
     /*
-     * Calculate the ground extents of the tile request.
-     */
+    ** Calculate the ground extents of the tile request.
+    */
     /* printf("X: %i  Y: %i  Z: %i\n",x,y,zoom); */
     tilesize = SPHEREMERC_GROUND_SIZE / zoomfactor;
     xmin = (x * tilesize) - (SPHEREMERC_GROUND_SIZE / 2.0);
@@ -185,7 +383,11 @@
     int i = 0;
     char j = 0;
     
-    for( i = 0; i < strlen( msObj->TileCoords ); i++ ) {
+    /* 
+    ** Walk down the VE URL string, adjusting the extent each time.
+    ** For meta-tiling cases, we stop early, to draw a larger image.
+    */
+    for( i = 0; i < strlen( msObj->TileCoords ) - params.metatile_level; i++ ) {
       j = msObj->TileCoords[i];
       tsize = SPHEREMERC_GROUND_SIZE / zoom;
       if( j == '1' || j == '3' ) minx += tsize;
@@ -204,6 +406,40 @@
   else {
     return(MS_FAILURE); /* Huh? Should have a mode. */
   }
+
+  /* 
+  ** Set the output tile size.
+  */
+  msObj->ImgCols = SPHEREMERC_IMAGE_SIZE << params.metatile_level;
+  msObj->ImgRows = SPHEREMERC_IMAGE_SIZE << params.metatile_level;
+  map->width = SPHEREMERC_IMAGE_SIZE << params.metatile_level;
+  map->height = SPHEREMERC_IMAGE_SIZE << params.metatile_level;
+
+  if(map->debug)
+    msDebug("msTileSetExtent(): base image size (%d x %d)\n",map->width,map->height);
+
+  /*
+  ** Add the gutters
+  ** First calculate ground units in the buffer at current extent 
+  */
+  buffer = params.map_edge_buffer * (map->extent.maxx - map->extent.minx) / (double)map->width;
+  /* 
+  ** Then adjust the map extents out by that amount 
+  */
+  map->extent.minx -= buffer;
+  map->extent.maxx += buffer;
+  map->extent.miny -= buffer;
+  map->extent.maxy += buffer;
+  /* 
+  ** Finally adjust the map image size by the pixel buffer 
+  */
+  map->width += 2 * params.map_edge_buffer;
+  map->height += 2 * params.map_edge_buffer;
+  msObj->ImgCols += 2 * params.map_edge_buffer;
+  msObj->ImgRows += 2 * params.map_edge_buffer;
+
+  if(map->debug)
+    msDebug("msTileSetExtent(): buffered image size (%d x %d)\n",map->width,map->height);
   
   /* 
   ** Adjust the extents inwards by 1/2 pixel so they are from
@@ -216,15 +452,32 @@
   dy = (map->extent.maxy - map->extent.miny) / map->height;
   map->extent.miny += dy*0.5;
   map->extent.maxy -= dy*0.5;
-  
+
   /* 
-  ** Set the output tile size.
+  ** Ensure the labelcache buffer is greater than the tile buffer. 
   */
-  msObj->ImgCols = SPHEREMERC_IMAGE_SIZE;
-  msObj->ImgRows = SPHEREMERC_IMAGE_SIZE;
-  map->width = SPHEREMERC_IMAGE_SIZE;
-  map->height = SPHEREMERC_IMAGE_SIZE;
+  if( params.map_edge_buffer > 0 ) {
+    const char *value;
+    hashTableObj *meta = &(map->web.metadata); 
+    char tilebufferstr[64];
 
+    /* Write the tile buffer to a string */
+    snprintf(tilebufferstr, 64, "-%d", params.map_edge_buffer);
+
+    /* Hm, the labelcache buffer is set... */
+    if((value = msLookupHashTable(meta, "labelcache_map_edge_buffer")) != NULL) {
+      /* If it's too small, replace with a bigger one */
+      if( params.map_edge_buffer > abs(atoi(value)) ) {
+        msRemoveHashTable(meta, "labelcache_map_edge_buffer");
+        msInsertHashTable(meta, "labelcache_map_edge_buffer", tilebufferstr);
+      }
+    }
+    /* No labelcache buffer value? Then we use the tile buffer. */
+    else {
+      msInsertHashTable(meta, "labelcache_map_edge_buffer", tilebufferstr);
+    }
+  }
+  
   if(map->debug) {
     msDebug( "msTileSetExtent (%f, %f) (%f, %f)\n", map->extent.minx, map->extent.miny, map->extent.maxx, map->extent.maxy);
   }
@@ -280,4 +533,31 @@
   return(MS_SUCCESS);
 }
 
+/************************************************************************
+ *                            msDrawTile                                *
+ *                                                                      * 
+ *   Draw the tile once with gutters, metatiling and buffers, then      *
+ *   clip out the final tile.                                           *
+ *   WARNING: Call msTileSetExtent() first or this will be a pointless  *
+ *   fucnction call.                                                    *
+ ************************************************************************/
 
+imageObj* msTileDraw(mapservObj *msObj)
+{
+  imageObj *img;
+  tileParams params;
+  msTileGetParams(msObj->map, &params);
+  img = msDrawMap(msObj->map, MS_FALSE);
+  if( img == NULL )
+    return NULL;
+  if( params.metatile_level > 0 || params.map_edge_buffer > 0 )
+  {
+    imageObj *tmp = msTileExtractSubTile(msObj, img);
+    msFreeImage(img);
+    if( tmp == NULL )
+      return NULL;
+    img = tmp;
+  }
+  return img;
+}
+

Modified: trunk/mapserver/maptile.h
===================================================================
--- trunk/mapserver/maptile.h	2010-02-20 18:36:27 UTC (rev 9815)
+++ trunk/mapserver/maptile.h	2010-02-20 18:38:16 UTC (rev 9816)
@@ -36,12 +36,19 @@
 
 #define SPHEREMERC_PROJ4 "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +units=m +k=1.0 +nadgrids=@null"
 #define SPHEREMERC_GROUND_SIZE (20037508.34*2)
-#define SPHEREMERC_IMAGE_SIZE 256
+#define SPHEREMERC_IMAGE_SIZE 0x0100
 
 enum tileModes { TILE_GMAP, TILE_VE };
  
 MS_DLL_EXPORT int msTileSetup(mapservObj *msObj); 
 MS_DLL_EXPORT int msTileSetExtent(mapservObj *msObj); 
 MS_DLL_EXPORT int msTileSetProjections(mapObj *map);
+MS_DLL_EXPORT imageObj* msTileDraw(mapservObj *msObj);
 
+typedef struct {
+  int metatile_level; /* In zoom levels above tile request: best bet is 0, 1 or 2 */
+  int tile_size; /* In pixels */
+  int map_edge_buffer; /* In pixels */
+} tileParams;
 
+



More information about the mapserver-commits mailing list