#529 give tiles gutters - all layers that use Tile.Image must now look after layer.imageSize and layer.imageOffset - this is handled by layer.setTileSize - for untiled layers, setTileSize must be defined by the subclass - gutters are currently supported in Layer.Mapserver and Layer.WMS

git-svn-id: http://svn.openlayers.org/trunk/openlayers@2979 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
Tim Schaub
2007-04-02 17:18:38 +00:00
parent 7f0ccb69f0
commit b6a5e6619a
14 changed files with 351 additions and 56 deletions

49
examples/gutter.html Normal file
View File

@@ -0,0 +1,49 @@
<html>
<head>
<style type="text/css">
#map {
width: 500px;
height: 300px;
border: 1px solid gray;
}
p.caption {
width: 500px;
}
</style>
<script src="../lib/OpenLayers.js"></script>
<script type="text/javascript">
<!--
OpenLayers.IMAGE_RELOAD_ATTEMPTS = 2;
var map;
window.onload = function() {
options = {maxExtent: new OpenLayers.Bounds(-73.5295, 41.2318,
-69.9097, 42.8883),
maxResolution: 0.0003}
map = new OpenLayers.Map('map', options);
var roads15 = new OpenLayers.Layer.WMS( "Roads (15px gutter)",
"http://boston.freemap.in/cgi-bin/mapserv?map=/www/freemap.in/boston/map/gmaps.map&",
{layers: 'roads_200_40'},
{gutter: 15});
var roads = new OpenLayers.Layer.WMS( "Roads (no gutter)",
"http://boston.freemap.in/cgi-bin/mapserv?map=/www/freemap.in/boston/map/gmaps.map&",
{layers: 'roads_200_40'});
map.addLayers([roads, roads15]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.setCenter(new OpenLayers.LonLat(-71.848, 42.2), 0);
}
// -->
</script>
</head>
<body>
<h1>OpenLayers Gutter Example</h1>
<div id="map"></div>
<p class="caption">
When you render tiles with certain types of symbols, there are artifacts
at tile edges that make symbology not look continuous. In the center of
the above map (when it first loads), the large orange road is split
vertically by a tile. Open the layer switcher and change to the layer
with a 15 pixel gutter to see how the symbology looks nicer.
</p>
</body>
</html>

View File

@@ -18,7 +18,8 @@
function init(){
map = new OpenLayers.Map( 'map' );
layer = new OpenLayers.Layer.MapServer( "OpenLayers WMS",
"http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
"http://labs.metacarta.com/wms/vmap0", {layers: 'basic'},
{gutter: 15});
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);

View File

@@ -2,8 +2,8 @@
<head>
<style type="text/css">
#map {
width: 800px;
height: 475px;
width: 100%;
height: 100%;
border: 1px solid black;
}
</style>

34
examples/wms-untiled.html Normal file
View File

@@ -0,0 +1,34 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css">
#map {
width: 100%;
height: 100%;
border: 1px solid black;
}
</style>
<script src="../lib/OpenLayers.js"></script>
<script type="text/javascript">
<!--
var lon = 5;
var lat = 40;
var zoom = 5;
var map, layer;
function init(){
map = new OpenLayers.Map( 'map' );
layer = new OpenLayers.Layer.WMS.Untiled( "OpenLayers WMS",
"http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
map.addControl( new OpenLayers.Control.LayerSwitcher() );
}
// -->
</script>
</head>
<body onload="init()">
<div id="map"></div>
</body>
</html>

View File

@@ -65,12 +65,45 @@ OpenLayers.Layer.prototype = {
* @type Boolean
*/
inRange: false,
/**
* For layers that use Tile.Image, the image size is cached here. For
* layers without a gutter, the image size is equal to the tile size.
* For layers with a gutter, the image is larger than the tile by twice
* the gutter in each dimension.
*
* @type OpenLayers.Size
* @private
*/
imageSize: null,
/**
* For layers that use Tile.Image, the image offset is cached here.
* Layers without a gutter have zero offset. For layers with a gutter,
* the image offset represents displacement due to the gutter.
*
* @type OpenLayers.Pixel
* @private
*/
imageOffset: null,
// OPTIONS
/** @type Array */
options: null,
/** Determines the width (in pixels) of the gutter around image tiles
* to ignore. By setting this property to a non-zero value, images
* will be requested that are wider and taller than the tile size by
* a value of 2 x gutter. This allows artifacts of rendering at tile
* edges to be ignored. Set a gutter value that is equal to half the size
* of the widest symbol that needs to be displayed. Defaults to zero.
* Non-tiled layers always have zero gutter.
*
* @type Int
*/
gutter: 0,
/** @type String */
projection: null,
@@ -243,9 +276,41 @@ OpenLayers.Layer.prototype = {
if (!this.isBaseLayer) {
this.inRange = this.calculateInRange();
}
}
// deal with gutters
this.setTileSize();
},
/**
* Set the tile size based on the map size. This also sets layer.imageSize
* and layer.imageOffset for use by Tile.Image.
*
* @param OpenLayers.Size
*/
setTileSize: function(size) {
var tileSize = (size) ? size :
((this.tileSize) ? this.tileSize :
this.map.getTileSize());
this.tileSize = tileSize;
if(this.gutter) {
// layers with gutters need non-null tile sizes
//if(tileSize == null) {
// OpenLayers.console.error("Error in layer.setMap() for " +
// this.name + ": layers with gutters " +
// "need non-null tile sizes");
//}
this.imageOffset = new OpenLayers.Pixel(-this.gutter, -this.gutter);
this.imageSize = new OpenLayers.Size(tileSize.w + (2 * this.gutter),
tileSize.h + (2 * this.gutter));
} else {
// layers without gutters may have null tile size - as long
// as they don't rely on Tile.Image
this.imageSize = tileSize;
this.imageOffset = new OpenLayers.Pixel(0, 0);
}
},
/**
* @returns Whether or not the layer should be displayed (if in range)
* @type Boolean
@@ -559,6 +624,23 @@ OpenLayers.Layer.prototype = {
return px;
},
/**
* Adjusts the extent of a bounds in map units by the layer's gutter
* in pixels.
*
* @param {OpenLayers.Bounds} bounds
* @type OpenLayers.Bounds
* @return A bounds adjusted in height and width by the gutter
*/
adjustBoundsByGutter: function(bounds) {
var mapGutter = this.gutter * this.map.getResolution();
bounds = new OpenLayers.Bounds(bounds.left - mapGutter,
bounds.bottom - mapGutter,
bounds.right + mapGutter,
bounds.top + mapGutter);
return bounds;
},
/**
* Sets the opacity for the entire layer (all images)
* @param {Float} opacity
@@ -566,7 +648,7 @@ OpenLayers.Layer.prototype = {
setOpacity: function(opacity) {
this.opacity = opacity;
for(var i=0; i<this.div.childNodes.length; ++i) {
var element = this.div.childNodes[i];
var element = this.div.childNodes[i].firstChild;
OpenLayers.Util.modifyDOMElement(element, null, null, null,
null, null, null, opacity);
}

View File

@@ -87,7 +87,9 @@ OpenLayers.Layer.MapServer.prototype =
* @type String
*/
getURL: function (bounds) {
if(this.gutter) {
bounds = this.adjustBoundsByGutter(bounds);
}
// Make a list, so that getFullRequestString uses literal ","
var extent = [bounds.left, bounds. bottom, bounds.right, bounds.top];
@@ -95,10 +97,10 @@ OpenLayers.Layer.MapServer.prototype =
var url = this.getFullRequestString(
{mapext: extent,
imgext: extent,
map_size: [this.tileSize.w,this.tileSize.h],
imgx: this.tileSize.w/2,
imgy: this.tileSize.h/2,
imgxy: [this.tileSize.w,this.tileSize.h]
map_size: [this.imageSize.w, this.imageSize.h],
imgx: this.imageSize.w / 2,
imgy: this.imageSize.h / 2,
imgxy: [this.imageSize.w, this.imageSize.h]
});
return url;

View File

@@ -99,12 +99,21 @@ OpenLayers.Layer.MapServer.Untiled.prototype =
* @param {OpenLayers.Map} map
*/
setMap: function(map) {
//determine new tile size
this.tileSize = map.getSize();
this.tileSize.w = this.tileSize.w * this.ratio;
this.tileSize.h = this.tileSize.h * this.ratio;
OpenLayers.Layer.HTTPRequest.prototype.setMap.apply(this, arguments);
},
/**
* Set the tile size based on the map size. This also sets layer.imageSize
* and layer.imageOffset for use by Tile.Image.
*/
setTileSize: function() {
var tileSize = this.map.getSize();
tileSize.w = tileSize.w * this.ratio;
tileSize.h = tileSize.h * this.ratio;
this.tileSize = tileSize;
this.imageSize = tileSize;
this.imageOffset = new OpenLayers.Pixel(0, 0);
},
/** When it is not a dragging move (ie when done dragging)
* reload and recenter the div.
@@ -149,9 +158,7 @@ OpenLayers.Layer.MapServer.Untiled.prototype =
center.lat + (tileHeight / 2));
//determine new tile size
this.tileSize = this.map.getSize();
this.tileSize.w = this.tileSize.w * this.ratio;
this.tileSize.h = this.tileSize.h * this.ratio;
this.setTileSize();
//formulate request url string
var url = this.getURL(tileBounds);

View File

@@ -24,7 +24,7 @@ OpenLayers.Layer.WMS.prototype =
},
reproject: true,
/**
* @constructor
*
@@ -93,10 +93,13 @@ OpenLayers.Layer.WMS.prototype =
* @type String
*/
getURL: function (bounds) {
if(this.gutter) {
bounds = this.adjustBoundsByGutter(bounds);
}
return this.getFullRequestString(
{BBOX:bounds.toBBOX(),
WIDTH:this.tileSize.w,
HEIGHT:this.tileSize.h});
WIDTH:this.imageSize.w,
HEIGHT:this.imageSize.h});
},
/**
@@ -109,7 +112,7 @@ OpenLayers.Layer.WMS.prototype =
* @type OpenLayers.Tile.Image
*/
addTile:function(bounds,position) {
url = this.getURL(bounds);
var url = this.getURL(bounds);
return new OpenLayers.Tile.Image(this, position, bounds,
url, this.tileSize);
},

View File

@@ -102,13 +102,22 @@ OpenLayers.Layer.WMS.Untiled.prototype =
* @param {OpenLayers.Map} map
*/
setMap: function(map) {
//determine new tile size
this.tileSize = map.getSize();
this.tileSize.w = this.tileSize.w * this.ratio;
this.tileSize.h = this.tileSize.h * this.ratio;
OpenLayers.Layer.HTTPRequest.prototype.setMap.apply(this, arguments);
},
/**
* Set the tile size based on the map size. This also sets layer.imageSize
* and layer.imageOffset for use by Tile.Image.
*/
setTileSize: function() {
var tileSize = this.map.getSize();
tileSize.w = tileSize.w * this.ratio;
tileSize.h = tileSize.h * this.ratio;
this.tileSize = tileSize;
this.imageSize = tileSize;
this.imageOffset = new OpenLayers.Pixel(0, 0);
},
/** When it is not a dragging move (ie when done dragging)
* reload and recenter the div.
*
@@ -152,9 +161,7 @@ OpenLayers.Layer.WMS.Untiled.prototype =
center.lat + (tileHeight / 2));
//determine new tile size
this.tileSize = this.map.getSize();
this.tileSize.w = this.tileSize.w * this.ratio;
this.tileSize.h = this.tileSize.h * this.ratio;
this.setTileSize();
//formulate request url string
var url = this.getURL(tileBounds);

View File

@@ -15,6 +15,13 @@ OpenLayers.Tile.Image.prototype =
/** @type DOMElement img */
imgDiv: null,
/**
* The image element is appended to the frame. Any gutter on the image
* will be hidden behind the frame.
*
* @type DOMElement div */
frame: null,
/**
* @constructor
*
@@ -26,6 +33,9 @@ OpenLayers.Tile.Image.prototype =
*/
initialize: function(layer, position, bounds, url, size) {
OpenLayers.Tile.prototype.initialize.apply(this, arguments);
this.frame = document.createElement('div');
this.frame.style.overflow = 'hidden';
this.frame.style.position = 'absolute';
},
/**
@@ -34,12 +44,16 @@ OpenLayers.Tile.Image.prototype =
destroy: function() {
if (this.imgDiv != null) {
OpenLayers.Event.stopObservingElement(this.imgDiv.id);
if (this.imgDiv.parentNode == this.layer.div) {
this.layer.div.removeChild(this.imgDiv);
if (this.imgDiv.parentNode == this.frame) {
this.frame.removeChild(this.imgDiv);
this.imgDiv.map = null;
}
}
this.imgDiv = null;
if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) {
this.layer.div.removeChild(this.frame);
}
this.frame = null;
OpenLayers.Tile.prototype.destroy.apply(this, arguments);
},
@@ -60,14 +74,17 @@ OpenLayers.Tile.Image.prototype =
this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
this.url = this.layer.getURL(this.bounds);
// position the frame
OpenLayers.Util.modifyDOMElement(this.frame,
null, this.position, this.size);
if (this.layer.alpha) {
OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,
null, this.position, this.size, this.url);
null, null, this.layer.imageSize, this.url);
} else {
this.imgDiv.src = this.url;
OpenLayers.Util.modifyDOMElement(this.imgDiv,
null, this.position, this.size) ;
null, null, this.layer.imageSize) ;
}
this.drawn = true;
return true;
@@ -102,20 +119,20 @@ OpenLayers.Tile.Image.prototype =
initImgDiv: function() {
if (this.layer.alpha) {
this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null,
this.position,
this.size,
this.layer.imageOffset,
this.layer.imageSize,
null,
"absolute",
"relative",
null,
null,
null,
true);
} else {
this.imgDiv = OpenLayers.Util.createImage(null,
this.position,
this.size,
this.layer.imageOffset,
this.layer.imageSize,
null,
"absolute",
"relative",
null,
null,
true);
@@ -131,7 +148,9 @@ OpenLayers.Tile.Image.prototype =
OpenLayers.Event.observe( this.imgDiv, "load",
this.checkImgURL.bindAsEventListener(this) );
*/
this.layer.div.appendChild(this.imgDiv);
this.frame.appendChild(this.imgDiv);
this.layer.div.appendChild(this.frame);
if(this.layer.opacity != null) {
OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, null,

View File

@@ -54,10 +54,10 @@
t.eq( img.src,
url + "?" + OpenLayers.Util.getParameterString(tParams).replace(/,/g, "+"),
"image src is created correctly via addtile" );
t.eq( tile.imgDiv.style.top, "6px", "image top is set correctly via addtile" );
t.eq( tile.imgDiv.style.left, "5px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" );
var firstChild = layer.div.firstChild;
var firstChild = layer.div.firstChild.firstChild;
if (!isMozilla)
t.ok( true, "skipping element test outside of Mozilla");
else
@@ -186,10 +186,10 @@
map.addLayer(tLayer);
map.zoomToMaxExtent();
t.eq(tLayer.opacity, "0.5", "Opacity is set correctly");
t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
t.eq(parseFloat(tLayer.div.firstChild.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
tLayer.setOpacity("0.6");
t.eq(tLayer.opacity, "0.6", "setOpacity works properly");
t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
t.eq(parseFloat(tLayer.div.firstChild.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
var pixel = new OpenLayers.Pixel(5,6);
var tile = tLayer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
tile.draw();

View File

@@ -127,10 +127,10 @@
map.addLayer(tLayer);
map.zoomToMaxExtent();
t.eq(tLayer.opacity, "0.5", "Opacity is set correctly");
t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
t.eq(parseFloat(tLayer.div.firstChild.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
tLayer.setOpacity("0.6");
t.eq(tLayer.opacity, "0.6", "setOpacity works properly");
t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
t.eq(parseFloat(tLayer.div.firstChild.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
}

View File

@@ -50,10 +50,10 @@
t.eq( img.src,
url + "?" + OpenLayers.Util.getParameterString(tParams),
"image src is created correctly via addtile" );
t.eq( tile.imgDiv.style.top, "6px", "image top is set correctly via addtile" );
t.eq( tile.imgDiv.style.left, "5px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" );
var firstChild = layer.div.firstChild;
var firstChild = layer.div.firstChild.firstChild;
if (!isMozilla)
t.ok( true, "skipping element test outside of Mozilla");
else
@@ -191,10 +191,10 @@
map.addLayer(tLayer);
map.zoomToMaxExtent();
t.eq(tLayer.opacity, "0.5", "Opacity is set correctly");
t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
t.eq(parseFloat(tLayer.div.firstChild.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
tLayer.setOpacity("0.6");
t.eq(tLayer.opacity, "0.6", "setOpacity works properly");
t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
t.eq(parseFloat(tLayer.div.firstChild.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
var pixel = new OpenLayers.Pixel(5,6);
var tile = tLayer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
tile.draw();
@@ -231,6 +231,48 @@
map.destroy();
}
function test_21_Layer_WMS_noGutters (t) {
t.plan(2);
var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.WMS("no gutter layer", url, params, {gutter: 0});
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(0,0), 5);
var tile = layer.grid[0][0];
var request = layer.getURL(tile.bounds);
var args = OpenLayers.Util.getArgs(request);
t.eq(parseInt(args['WIDTH']),
tile.size.w,
"layer without gutter requests images that are as wide as the tile");
t.eq(parseInt(args['HEIGHT']),
tile.size.h,
"layer without gutter requests images that are as tall as the tile");
layer.destroy();
map.destroy();
}
function test_22_Layer_WMS_gutters (t) {
t.plan(2);
var gutter = 15;
var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.WMS("gutter layer", url, params, {gutter: gutter});
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(0,0), 5);
var tile = layer.grid[0][0];
var request = layer.getURL(tile.bounds);
var args = OpenLayers.Util.getArgs(request);
t.eq(parseInt(args['WIDTH']),
tile.size.w + (2 * gutter),
"layer with gutter requests images that are wider by twice the gutter");
t.eq(parseInt(args['HEIGHT']),
tile.size.h + (2 * gutter),
"layer with gutter requests images that are taller by twice the gutter");
layer.destroy();
map.destroy();
}
function test_99_Layer_WMS_destroy (t) {
t.plan( 1 );

View File

@@ -29,12 +29,15 @@
var map = new OpenLayers.Map('map');
layer = new OpenLayers.Layer.WMS("Name", "http://labs.metacarta.com/TESTURL");
var size = new OpenLayers.Size(5,6);
layer = new OpenLayers.Layer.WMS("Name",
"http://labs.metacarta.com/TESTURL",
null,
{tileSize: size});
map.addLayer(layer);
var position = new OpenLayers.Pixel(20,30);
var bounds = new OpenLayers.Bounds(1,2,3,4);
var url = "http://www.openlayers.org/dev/tests/tileimage";
var size = new OpenLayers.Size(5,6);
tile = new OpenLayers.Tile.Image(layer, position, bounds, url, size);
tile.draw();
@@ -53,7 +56,7 @@
REQUEST: "GetMap", STYLES: "",
EXCEPTIONS: "application/vnd.ogc.se_inimage", FORMAT: "image/jpeg",
SRS: "EPSG:4326", BBOX: "1,2,3,4",
WIDTH: "256", HEIGHT: "256"
WIDTH: String(size.w), HEIGHT: String(size.h)
};
t.eq( img.src,
"http://labs.metacarta.com/TESTURL?" + OpenLayers.Util.getParameterString(tParams),
@@ -167,11 +170,57 @@
t.delay_call( 1, function() { t.eq(tile.imgDiv, null, "Alpha tile imgDiv is null.") } );
}
function test_05_Tile_Image_gutters(t) {
t.plan(5);
var gutter = 0;
var name = 'Test Layer';
var url = "http://octo.metacarta.com/cgi-bin/mapserv";
var params = { map: '/mapdata/vmap_wms.map',
layers: 'basic',
format: 'image/png'};
var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.WMS(name, url, params, {gutter: gutter});
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(0,0), 5);
var tile = layer.grid[0][0];
t.ok(tile.layer.imageSize.equals(tile.size),
"zero size gutter doesn't change image size");
t.ok(tile.layer.imageOffset.equals(new OpenLayers.Pixel(0, 0)),
"zero size gutter doesn't affect image offset");
map.destroy();
var gutter = 15;
var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.WMS(name, url, params, {gutter: gutter});
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(0,0), 5);
var tile = layer.grid[0][0];
t.ok(tile.layer.imageSize.equals(new OpenLayers.Size(tile.size.w + (2 * gutter),
tile.size.h + (2 * gutter))),
"gutter properly changes image size");
t.ok(tile.layer.imageOffset.equals(new OpenLayers.Pixel(-gutter, -gutter)),
"gutter properly sets image offset");
t.ok(tile.bounds.equals(new OpenLayers.Bounds(-33.75, 33.75, -22.5, 45)),
"gutter doesn't affect tile bounds");
map.destroy();
}
// -->
</script>
</head>
<body>
<div id="map" style="height:500px;width:500px"></div>
<div id="map" style="height:550px;width:500px"></div>
</body>
</html>