[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, ¶ms);
+
+ /*
+ ** 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, ¶ms);
/*
** 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, ¶ms);
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, ¶ms);
+ 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