New vector rendering for better performance and less renderer specific limitations. r=elemoine (closes #1675, closes #1656, closes #1631, closes #1431, closes #1709)

git-svn-id: http://svn.openlayers.org/trunk/openlayers@7930 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
ahocevar
2008-09-02 17:17:52 +00:00
parent ede7bef13c
commit c12cb25aee
12 changed files with 676 additions and 155 deletions

View File

@@ -106,6 +106,13 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
* {Array(<OpenLayers.Feature.Vector>)} * {Array(<OpenLayers.Feature.Vector>)}
*/ */
selectedFeatures: null, selectedFeatures: null,
/**
* Property: unrenderedFeatures
* {Object} hash of features, keyed by feature.id, that the renderer
* failed to draw
*/
unrenderedFeatures: null,
/** /**
* APIProperty: reportError * APIProperty: reportError
@@ -213,6 +220,7 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
this.features = []; this.features = [];
this.selectedFeatures = []; this.selectedFeatures = [];
this.unrenderedFeatures = {};
// Allow for custom layer behavior // Allow for custom layer behavior
if(this.strategies){ if(this.strategies){
@@ -247,6 +255,7 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
this.destroyFeatures(); this.destroyFeatures();
this.features = null; this.features = null;
this.selectedFeatures = null; this.selectedFeatures = null;
this.unrenderedFeatures = null;
if (this.renderer) { if (this.renderer) {
this.renderer.destroy(); this.renderer.destroy();
} }
@@ -359,6 +368,8 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
*/ */
moveTo: function(bounds, zoomChanged, dragging) { moveTo: function(bounds, zoomChanged, dragging) {
OpenLayers.Layer.prototype.moveTo.apply(this, arguments); OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
var coordSysUnchanged = true;
if (!dragging) { if (!dragging) {
this.renderer.root.style.visibility = "hidden"; this.renderer.root.style.visibility = "hidden";
@@ -366,7 +377,7 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px"; this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px";
this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px"; this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px";
var extent = this.map.getExtent(); var extent = this.map.getExtent();
this.renderer.setExtent(extent); coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
this.renderer.root.style.visibility = "visible"; this.renderer.root.style.visibility = "visible";
@@ -376,17 +387,33 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) { if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) {
this.div.scrollLeft = this.div.scrollLeft; this.div.scrollLeft = this.div.scrollLeft;
} }
if(!zoomChanged && coordSysUnchanged) {
var unrenderedFeatures = {};
for(var i in this.unrenderedFeatures) {
var feature = this.unrenderedFeatures[i];
if(!this.drawFeature(feature)) {
unrenderedFeatures[i] = feature;
}
}
this.unrenderedFeatures = unrenderedFeatures;
}
} }
if (!this.drawn || zoomChanged) { if (!this.drawn || zoomChanged || !coordSysUnchanged) {
this.unrenderedFeatures = {};
this.drawn = true; this.drawn = true;
var feature;
for(var i=0, len=this.features.length; i<len; i++) { for(var i=0, len=this.features.length; i<len; i++) {
if (i != (this.features.length - 1)) { if (i != (this.features.length - 1)) {
this.renderer.locked = true; this.renderer.locked = true;
} else { } else {
this.renderer.locked = false; this.renderer.locked = false;
} }
this.drawFeature(this.features[i]); feature = this.features[i];
if (!this.drawFeature(feature)) {
this.unrenderedFeatures[feature.id] = feature;
};
} }
} }
}, },
@@ -438,7 +465,9 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
} }
if (this.drawn) { if (this.drawn) {
this.drawFeature(feature); if(!this.drawFeature(feature)) {
this.unrenderedFeatures[feature.id] = feature;
}
} }
if (notify) { if (notify) {
@@ -488,6 +517,7 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
} }
var feature = features[i]; var feature = features[i];
delete this.unrenderedFeatures[feature.id];
if (notify) { if (notify) {
this.events.triggerEvent("beforefeatureremoved", { this.events.triggerEvent("beforefeatureremoved", {
@@ -553,6 +583,10 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
* Parameters: * Parameters:
* feature - {<OpenLayers.Feature.Vector>} * feature - {<OpenLayers.Feature.Vector>}
* style - {Object} Symbolizer hash or {String} renderIntent * style - {Object} Symbolizer hash or {String} renderIntent
*
* Returns:
* {Boolean} true if the renderer was able to draw the feature, false
* otherwise
*/ */
drawFeature: function(feature, style) { drawFeature: function(feature, style) {
if (typeof style != "object") { if (typeof style != "object") {
@@ -564,7 +598,7 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
} }
} }
this.renderer.drawFeature(feature, style); return this.renderer.drawFeature(feature, style);
}, },
/** /**

View File

@@ -103,13 +103,19 @@ OpenLayers.Renderer = OpenLayers.Class({
* Resolution has probably changed, so we nullify the resolution * Resolution has probably changed, so we nullify the resolution
* cache (this.resolution) -- this way it will be re-computed when * cache (this.resolution) -- this way it will be re-computed when
* next it is needed. * next it is needed.
* We nullify the resolution cache (this.resolution) if resolutionChanged
* is set to true - this way it will be re-computed on the next
* getResolution() request.
* *
* Parameters: * Parameters:
* extent - {<OpenLayers.Bounds>} * extent - {<OpenLayers.Bounds>}
* resolutionChanged - {Boolean}
*/ */
setExtent: function(extent) { setExtent: function(extent, resolutionChanged) {
this.extent = extent.clone(); this.extent = extent.clone();
this.resolution = null; if (resolutionChanged) {
this.resolution = null;
}
}, },
/** /**
@@ -148,14 +154,21 @@ OpenLayers.Renderer = OpenLayers.Class({
* *
* Parameters: * Parameters:
* feature - {<OpenLayers.Feature.Vector>} * feature - {<OpenLayers.Feature.Vector>}
* style - {<Object>} * style - {<Object>}
*
* Returns:
* {Boolean} true if the feature has been drawn completely, false if not,
* undefined if the feature had no geometry
*/ */
drawFeature: function(feature, style) { drawFeature: function(feature, style) {
if(style == null) { if(style == null) {
style = feature.style; style = feature.style;
} }
if (feature.geometry) { if (feature.geometry) {
this.drawGeometry(feature.geometry, style, feature.id); if (!feature.geometry.getBounds().intersectsBounds(this.extent)) {
style = {display: "none"};
}
return this.drawGeometry(feature.geometry, style, feature.id);
} }
}, },
@@ -223,4 +236,4 @@ OpenLayers.Renderer = OpenLayers.Class({
eraseGeometry: function(geometry) {}, eraseGeometry: function(geometry) {},
CLASS_NAME: "OpenLayers.Renderer" CLASS_NAME: "OpenLayers.Renderer"
}); });

View File

@@ -450,26 +450,36 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
* style - {Object} * style - {Object}
* featureId - {String} * featureId - {String}
*
* Returns:
* {Boolean} true if the geometry has been drawn completely; null if
* incomplete; false otherwise
*/ */
drawGeometry: function(geometry, style, featureId) { drawGeometry: function(geometry, style, featureId) {
var className = geometry.CLASS_NAME; var className = geometry.CLASS_NAME;
var rendered = true;
if ((className == "OpenLayers.Geometry.Collection") || if ((className == "OpenLayers.Geometry.Collection") ||
(className == "OpenLayers.Geometry.MultiPoint") || (className == "OpenLayers.Geometry.MultiPoint") ||
(className == "OpenLayers.Geometry.MultiLineString") || (className == "OpenLayers.Geometry.MultiLineString") ||
(className == "OpenLayers.Geometry.MultiPolygon")) { (className == "OpenLayers.Geometry.MultiPolygon")) {
for (var i = 0, len=geometry.components.length; i<len; i++) { for (var i = 0, len=geometry.components.length; i<len; i++) {
this.drawGeometry(geometry.components[i], style, featureId); rendered = rendered && this.drawGeometry(
geometry.components[i], style, featureId);
} }
return; return rendered;
}; };
rendered = false;
if (style.display != "none") { if (style.display != "none") {
if (style.backgroundGraphic) { if (style.backgroundGraphic) {
this.redrawBackgroundNode(geometry.id, geometry, style, featureId); this.redrawBackgroundNode(geometry.id, geometry, style,
featureId);
} }
this.redrawNode(geometry.id, geometry, style, featureId); rendered = this.redrawNode(geometry.id, geometry, style,
} else { featureId);
var node = OpenLayers.Util.getElement(geometry.id); }
if (rendered == false) {
var node = document.getElementById(geometry.id);
if (node) { if (node) {
if (node._style.backgroundGraphic) { if (node._style.backgroundGraphic) {
node.parentNode.removeChild(document.getElementById( node.parentNode.removeChild(document.getElementById(
@@ -478,6 +488,7 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
node.parentNode.removeChild(node); node.parentNode.removeChild(node);
} }
} }
return rendered;
}, },
/** /**
@@ -488,6 +499,10 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
* style - {Object} * style - {Object}
* featureId - {String} * featureId - {String}
*
* Returns:
* {Boolean} true if the complete geometry could be drawn, null if parts of
* the geometry could not be drawn, false otherwise
*/ */
redrawNode: function(id, geometry, style, featureId) { redrawNode: function(id, geometry, style, featureId) {
// Get the node if it's already on the map. // Get the node if it's already on the map.
@@ -508,7 +523,13 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
newNode._geometry = geometry; newNode._geometry = geometry;
newNode._geometryClass = geometry.CLASS_NAME; newNode._geometryClass = geometry.CLASS_NAME;
newNode._style = style; newNode._style = style;
newNode = this.drawGeometryNode(newNode, geometry, style);
var drawResult = this.drawGeometryNode(newNode, geometry, style);
if(drawResult === false) {
return false;
}
newNode = drawResult.node;
// Insert the node into the indexer so it can show us where to // Insert the node into the indexer so it can show us where to
// place it. Note that this operation is O(log(n)). If there's a // place it. Note that this operation is O(log(n)). If there's a
@@ -522,7 +543,9 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
this.root.appendChild(newNode); this.root.appendChild(newNode);
} }
this.postDraw(newNode); this.postDraw(newNode);
return drawResult.complete;
}, },
/** /**
@@ -540,6 +563,10 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
* style - {Object} * style - {Object}
* featureId - {String} * featureId - {String}
*
* Returns:
* {Boolean} true if the complete geometry could be drawn, null if parts of
* the geometry could not be drawn, false otherwise
*/ */
redrawBackgroundNode: function(id, geometry, style, featureId) { redrawBackgroundNode: function(id, geometry, style, featureId) {
var backgroundStyle = OpenLayers.Util.extend({}, style); var backgroundStyle = OpenLayers.Util.extend({}, style);
@@ -556,7 +583,7 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
backgroundStyle.backgroundYOffset = null; backgroundStyle.backgroundYOffset = null;
backgroundStyle.backgroundGraphicZIndex = null; backgroundStyle.backgroundGraphicZIndex = null;
this.redrawNode( return this.redrawNode(
id + this.BACKGROUND_ID_SUFFIX, id + this.BACKGROUND_ID_SUFFIX,
geometry, geometry,
backgroundStyle, backgroundStyle,
@@ -574,6 +601,11 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
* style - {Object} * style - {Object}
*
* Returns:
* {Object} a hash with properties "node" (the drawn node) and "complete"
* (null if parts of the geometry could not be drawn, false if nothing
* could be drawn)
*/ */
drawGeometryNode: function(node, geometry, style) { drawGeometryNode: function(node, geometry, style) {
style = style || node._style; style = style || node._style;
@@ -583,25 +615,26 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
'isFilled': true, 'isFilled': true,
'isStroked': !!style.strokeWidth 'isStroked': !!style.strokeWidth
}; };
var drawn;
switch (geometry.CLASS_NAME) { switch (geometry.CLASS_NAME) {
case "OpenLayers.Geometry.Point": case "OpenLayers.Geometry.Point":
this.drawPoint(node, geometry); drawn = this.drawPoint(node, geometry);
break; break;
case "OpenLayers.Geometry.LineString": case "OpenLayers.Geometry.LineString":
options.isFilled = false; options.isFilled = false;
this.drawLineString(node, geometry); drawn = this.drawLineString(node, geometry);
break; break;
case "OpenLayers.Geometry.LinearRing": case "OpenLayers.Geometry.LinearRing":
this.drawLinearRing(node, geometry); drawn = this.drawLinearRing(node, geometry);
break; break;
case "OpenLayers.Geometry.Polygon": case "OpenLayers.Geometry.Polygon":
this.drawPolygon(node, geometry); drawn = this.drawPolygon(node, geometry);
break; break;
case "OpenLayers.Geometry.Surface": case "OpenLayers.Geometry.Surface":
this.drawSurface(node, geometry); drawn = this.drawSurface(node, geometry);
break; break;
case "OpenLayers.Geometry.Rectangle": case "OpenLayers.Geometry.Rectangle":
this.drawRectangle(node, geometry); drawn = this.drawRectangle(node, geometry);
break; break;
default: default:
break; break;
@@ -612,7 +645,14 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
//set style //set style
//TBD simplify this //TBD simplify this
return this.setStyle(node, style, options, geometry); if (drawn != false) {
return {
node: this.setStyle(node, style, options, geometry),
complete: drawn
};
} else {
return false;
}
}, },
/** /**
@@ -634,6 +674,9 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the point
*/ */
drawPoint: function(node, geometry) {}, drawPoint: function(node, geometry) {},
@@ -646,6 +689,10 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or null if the renderer could not draw all components of
* the linestring, or false if nothing could be drawn
*/ */
drawLineString: function(node, geometry) {}, drawLineString: function(node, geometry) {},
@@ -658,6 +705,10 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or null if the renderer could not draw all components
* of the linear ring, or false if nothing could be drawn
*/ */
drawLinearRing: function(node, geometry) {}, drawLinearRing: function(node, geometry) {},
@@ -670,6 +721,10 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or null if the renderer could not draw all components
* of the polygon, or false if nothing could be drawn
*/ */
drawPolygon: function(node, geometry) {}, drawPolygon: function(node, geometry) {},
@@ -682,6 +737,9 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the rectangle
*/ */
drawRectangle: function(node, geometry) {}, drawRectangle: function(node, geometry) {},
@@ -694,6 +752,9 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the circle
*/ */
drawCircle: function(node, geometry) {}, drawCircle: function(node, geometry) {},
@@ -706,6 +767,9 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the surface
*/ */
drawSurface: function(node, geometry) {}, drawSurface: function(node, geometry) {},

View File

@@ -31,14 +31,14 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* {Integer} Firefox has a limitation where values larger or smaller than * {Integer} Firefox has a limitation where values larger or smaller than
* about 15000 in an SVG document lock the browser up. This * about 15000 in an SVG document lock the browser up. This
* works around it. * works around it.
*/ */
MAX_PIXEL: 15000, MAX_PIXEL: 15000,
/** /**
* Property: localResolution * Property: translationParameters
* {Float} * {Object} Hash with "x" and "y" properties
*/ */
localResolution: null, translationParameters: null,
/** /**
* Property: symbolSize * Property: symbolSize
@@ -58,6 +58,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
} }
OpenLayers.Renderer.Elements.prototype.initialize.apply(this, OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
arguments); arguments);
this.translationParameters = {x: 0, y: 0};
}, },
/** /**
@@ -86,16 +87,20 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* See #669 for more information * See #669 for more information
* *
* Parameters: * Parameters:
* x - {Integer} * x - {Integer}
* y - {Integer} * y - {Integer}
* xyOnly - {Boolean} whether or not to just check for x and y, which means
* to not take the current translation parameters into account if true.
* *
* Returns: * Returns:
* {Boolean} Whether or not the 'x' and 'y' coordinates are in the * {Boolean} Whether or not the 'x' and 'y' coordinates are in the
* valid range. * valid range.
*/ */
inValidRange: function(x, y) { inValidRange: function(x, y, xyOnly) {
return (x >= -this.MAX_PIXEL && x <= this.MAX_PIXEL && var left = x + (xyOnly ? 0 : this.translationParameters.x);
y >= -this.MAX_PIXEL && y <= this.MAX_PIXEL); var top = y + (xyOnly ? 0 : this.translationParameters.y);
return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
}, },
/** /**
@@ -103,39 +108,66 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* *
* Parameters: * Parameters:
* extent - {<OpenLayers.Bounds>} * extent - {<OpenLayers.Bounds>}
* resolutionChanged - {Boolean}
*
* Returns:
* {Boolean} true to notify the layer that the new extent does not exceed
* the coordinate range, and the features will not need to be redrawn.
* False otherwise.
*/ */
setExtent: function(extent) { setExtent: function(extent, resolutionChanged) {
OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, OpenLayers.Renderer.Elements.prototype.setExtent.apply(this,
arguments); arguments);
var resolution = this.getResolution(); var resolution = this.getResolution();
var left = -extent.left / resolution;
var top = extent.top / resolution;
// If the resolution has changed, start over changing the corner, because // If the resolution has changed, start over changing the corner, because
// the features will redraw. // the features will redraw.
if (!this.localResolution || resolution != this.localResolution) { if (resolutionChanged) {
this.left = -extent.left / resolution; this.left = left;
this.top = extent.top / resolution; this.top = top;
} // Set the viewbox
var extentString = "0 0 " + this.size.w + " " + this.size.h;
var left = 0;
var top = 0;
// If the resolution has not changed, we already have features, and we need this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
// to adjust the viewbox to fit them. this.translate(0, 0);
if (this.localResolution && resolution == this.localResolution) { return true;
left = (this.left) - (-extent.left / resolution); } else {
top = (this.top) - (extent.top / resolution); var inRange = this.translate(left - this.left, top - this.top);
} if (!inRange) {
// recenter the coordinate system
// Store resolution for use later. this.setExtent(extent, true);
this.localResolution = resolution; }
return inRange;
// Set the viewbox -- the left/top will be pixels-dragged-since-res change, }
// the width/height will be pixels. },
var extentString = left + " " + top + " " +
this.size.w + " " + this.size.h; /**
//var extentString = extent.left / resolution + " " + -extent.top / resolution + " " + * Method: translate
this.rendererRoot.setAttributeNS(null, "viewBox", extentString); * Transforms the SVG coordinate system
*
* Parameters:
* x - {Float}
* y - {Float}
*
* Returns:
* {Boolean} true if the translation parameters ar in the valid coordinates
* range, false otherwise.
*/
translate: function(x, y) {
if (!this.inValidRange(x, y, true)) {
return false;
} else {
var transformString = "";
if (x || y) {
transformString = "translate(" + x + "," + y + ")";
}
this.root.setAttributeNS(null, "transform", transformString);
this.translationParameters = {x: x, y: y};
return true;
}
}, },
/** /**
@@ -413,9 +445,12 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the point
*/ */
drawPoint: function(node, geometry) { drawPoint: function(node, geometry) {
this.drawCircle(node, geometry, 1); return this.drawCircle(node, geometry, 1);
}, },
/** /**
@@ -426,6 +461,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
* radius - {Float} * radius - {Float}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the circle
*/ */
drawCircle: function(node, geometry, radius) { drawCircle: function(node, geometry, radius) {
var resolution = this.getResolution(); var resolution = this.getResolution();
@@ -436,10 +474,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
node.setAttributeNS(null, "cx", x); node.setAttributeNS(null, "cx", x);
node.setAttributeNS(null, "cy", y); node.setAttributeNS(null, "cy", y);
node.setAttributeNS(null, "r", radius); node.setAttributeNS(null, "r", radius);
return node;
} else { } else {
node.setAttributeNS(null, "cx", ""); return false;
node.setAttributeNS(null, "cy", "");
node.setAttributeNS(null, "r", 0);
} }
}, },
@@ -451,23 +488,41 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or null if the renderer could not draw all components of
* the linestring, or false if nothing could be drawn
*/ */
drawLineString: function(node, geometry) { drawLineString: function(node, geometry) {
node.setAttributeNS(null, "points", var componentsResult = this.getComponentsString(geometry.components);
this.getComponentsString(geometry.components)); if (componentsResult.path) {
node.setAttributeNS(null, "points", componentsResult.path);
return (componentsResult.complete ? node : null);
} else {
return false;
}
}, },
/**v /**
* Method: drawLinearRing * Method: drawLinearRing
* This method is only called by the renderer itself. * This method is only called by the renderer itself.
* *
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or null if the renderer could not draw all components
* of the linear ring, or false if nothing could be drawn
*/ */
drawLinearRing: function(node, geometry) { drawLinearRing: function(node, geometry) {
node.setAttributeNS(null, "points", var componentsResult = this.getComponentsString(geometry.components);
this.getComponentsString(geometry.components)); if (componentsResult.path) {
node.setAttributeNS(null, "points", componentsResult.path);
return (componentsResult.complete ? node : null);
} else {
return false;
}
}, },
/** /**
@@ -477,28 +532,35 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or null if the renderer could not draw all components
* of the polygon, or false if nothing could be drawn
*/ */
drawPolygon: function(node, geometry) { drawPolygon: function(node, geometry) {
var d = ""; var d = "";
var draw = true; var draw = true;
var complete = true;
var linearRingResult, path;
for (var j=0, len=geometry.components.length; j<len; j++) { for (var j=0, len=geometry.components.length; j<len; j++) {
var linearRing = geometry.components[j];
d += " M"; d += " M";
for (var i=0, ilen=linearRing.components.length; i<ilen; i++) { linearRingResult = this.getComponentsString(
var component = this.getShortString(linearRing.components[i]); geometry.components[j].components, " ");
if (component) { path = linearRingResult.path;
d += " " + component; if (path) {
} else { d += " " + path;
draw = false; complete = linearRingResult.complete && complete;
} } else {
draw = false;
} }
} }
d += " z"; d += " z";
if (draw) { if (draw) {
node.setAttributeNS(null, "d", d); node.setAttributeNS(null, "d", d);
node.setAttributeNS(null, "fill-rule", "evenodd"); node.setAttributeNS(null, "fill-rule", "evenodd");
return complete ? node : null;
} else { } else {
node.setAttributeNS(null, "d", ""); return false;
} }
}, },
@@ -509,6 +571,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the rectangle
*/ */
drawRectangle: function(node, geometry) { drawRectangle: function(node, geometry) {
var resolution = this.getResolution(); var resolution = this.getResolution();
@@ -520,11 +585,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
node.setAttributeNS(null, "y", y); node.setAttributeNS(null, "y", y);
node.setAttributeNS(null, "width", geometry.width / resolution); node.setAttributeNS(null, "width", geometry.width / resolution);
node.setAttributeNS(null, "height", geometry.height / resolution); node.setAttributeNS(null, "height", geometry.height / resolution);
return node;
} else { } else {
node.setAttributeNS(null, "x", ""); return false;
node.setAttributeNS(null, "y", "");
node.setAttributeNS(null, "width", 0);
node.setAttributeNS(null, "height", 0);
} }
}, },
@@ -535,6 +598,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the surface
*/ */
drawSurface: function(node, geometry) { drawSurface: function(node, geometry) {
@@ -559,8 +625,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
d += " Z"; d += " Z";
if (draw) { if (draw) {
node.setAttributeNS(null, "d", d); node.setAttributeNS(null, "d", d);
return node;
} else { } else {
node.setAttributeNS(null, "d", ""); return false;
} }
}, },
@@ -569,19 +636,93 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* *
* Parameters: * Parameters:
* components - {Array(<OpenLayers.Geometry.Point>)} Array of points * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
* separator - {String} character between coordinate pairs. Defaults to ","
*
* Returns:
* {Object} hash with properties "path" (the string created from the
* components and "complete" (false if the renderer was unable to
* draw all components)
*/ */
getComponentsString: function(components) { getComponentsString: function(components, separator) {
var renderCmp = [];
var complete = true;
var len = components.length;
var strings = []; var strings = [];
for(var i=0, len=components.length; i<len; i++) { var str, component, j;
var component = this.getShortString(components[i]); for(var i=0; i<len; i++) {
if (component) { component = components[i];
strings.push(component); renderCmp.push(component);
str = this.getShortString(component);
if (str) {
strings.push(str);
} else {
// The current component is outside the valid range. Let's
// see if the previous or next component is inside the range.
// If so, add the coordinate of the intersection with the
// valid range bounds.
if (i > 0) {
if (this.getShortString(components[i + 1])) {
strings.push(this.clipLine(components[i],
components[i-1]));
}
}
if (i < len - 1) {
if (this.getShortString(components[i + 1])) {
strings.push(this.clipLine(components[i],
components[i+1]));
}
}
complete = false;
} }
} }
return strings.join(",");
return {
path: strings.join(separator || ","),
complete: complete
};
},
/**
* Method: clipLine
* Given two points (one inside the valid range, and one outside),
* clips the line betweeen the two points so that the new points are both
* inside the valid range.
*
* Parameters:
* badComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
* invalid point
* goodComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
* valid point
* Returns
* {String} the SVG coordinate pair of the clipped point (like
* getShortString), or an empty string if both passed componets are at
* the same point.
*/
clipLine: function(badComponent, goodComponent) {
if (goodComponent.equals(badComponent)) {
return "";
}
var resolution = this.getResolution();
var maxX = this.MAX_PIXEL - this.translationParameters.x;
var maxY = this.MAX_PIXEL - this.translationParameters.y;
var x1 = goodComponent.x / resolution + this.left;
var y1 = this.top - goodComponent.y / resolution;
var x2 = badComponent.x / resolution + this.left;
var y2 = this.top - badComponent.y / resolution;
var k;
if (x2 < -maxX || x2 > maxX) {
k = (y2 - y1) / (x2 - x1);
x2 = x2 < 0 ? -maxX : maxX;
y2 = y1 + (x2 - x1) * k;
}
if (y2 < -maxY || y2 > maxY) {
k = (x2 - x1) / (y2 - y1);
y2 = y2 < 0 ? -maxY : maxY;
x2 = x1 + (y2 - y1) * k;
}
return x2 + "," + y2;
}, },
/** /**
* Method: getShortString * Method: getShortString
* *
@@ -589,7 +730,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* point - {<OpenLayers.Geometry.Point>} * point - {<OpenLayers.Geometry.Point>}
* *
* Returns: * Returns:
* {String} * {String} or false if point is outside the valid range
*/ */
getShortString: function(point) { getShortString: function(point) {
var resolution = this.getResolution(); var resolution = this.getResolution();

View File

@@ -79,8 +79,13 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* *
* Parameters: * Parameters:
* extent - {<OpenLayers.Bounds>} * extent - {<OpenLayers.Bounds>}
* resolutionChanged - {Boolean}
*
* Returns:
* {Boolean} true to notify the layer that the new extent does not exceed
* the coordinate range, and the features will not need to be redrawn.
*/ */
setExtent: function(extent) { setExtent: function(extent, resolutionChanged) {
OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, OpenLayers.Renderer.Elements.prototype.setExtent.apply(this,
arguments); arguments);
var resolution = this.getResolution(); var resolution = this.getResolution();
@@ -95,6 +100,8 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
// flip the VML display Y axis upside down so it // flip the VML display Y axis upside down so it
// matches the display Y axis of the map // matches the display Y axis of the map
this.root.style.flip = "y"; this.root.style.flip = "y";
return true;
}, },
@@ -569,23 +576,6 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
return this.nodeFactory(this.container.id + "_root", "olv:group"); return this.nodeFactory(this.container.id + "_root", "olv:group");
}, },
/**
* Method: drawFeature
* Overrides the superclass's drawFeature method to take care of features
* that are outside the viewport.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
* style - {<Object>}
*/
drawFeature: function(feature, style) {
if (!feature.geometry.getBounds().intersectsBounds(this.extent)) {
style = {display: "none"};
}
OpenLayers.Renderer.Elements.prototype.drawFeature.apply(this,
[feature, style]);
},
/************************************** /**************************************
* * * *
* GEOMETRY DRAWING FUNCTIONS * * GEOMETRY DRAWING FUNCTIONS *
@@ -599,9 +589,12 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the point could not be drawn
*/ */
drawPoint: function(node, geometry) { drawPoint: function(node, geometry) {
this.drawCircle(node, geometry, 1); return this.drawCircle(node, geometry, 1);
}, },
/** /**
@@ -613,6 +606,9 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
* radius - {float} * radius - {float}
*
* Returns:
* {DOMElement} or false if the circle could not ne drawn
*/ */
drawCircle: function(node, geometry, radius) { drawCircle: function(node, geometry, radius) {
if(!isNaN(geometry.x)&& !isNaN(geometry.y)) { if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
@@ -625,7 +621,9 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
node.style.width = diameter + "px"; node.style.width = diameter + "px";
node.style.height = diameter + "px"; node.style.height = diameter + "px";
return node;
} }
return false;
}, },
@@ -636,9 +634,12 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement}
*/ */
drawLineString: function(node, geometry) { drawLineString: function(node, geometry) {
this.drawLine(node, geometry, false); return this.drawLine(node, geometry, false);
}, },
/** /**
@@ -648,9 +649,12 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement}
*/ */
drawLinearRing: function(node, geometry) { drawLinearRing: function(node, geometry) {
this.drawLine(node, geometry, true); return this.drawLine(node, geometry, true);
}, },
/** /**
@@ -661,6 +665,9 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
* closeLine - {Boolean} Close the line? (make it a ring?) * closeLine - {Boolean} Close the line? (make it a ring?)
*
* Returns:
* {DOMElement}
*/ */
drawLine: function(node, geometry, closeLine) { drawLine: function(node, geometry, closeLine) {
@@ -679,6 +686,7 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
} }
var end = (closeLine) ? " x e" : " e"; var end = (closeLine) ? " x e" : " e";
node.path = "m" + parts.join("") + end; node.path = "m" + parts.join("") + end;
return node;
}, },
/** /**
@@ -688,6 +696,9 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement}
*/ */
drawPolygon: function(node, geometry) { drawPolygon: function(node, geometry) {
this.setNodeDimension(node, geometry); this.setNodeDimension(node, geometry);
@@ -713,6 +724,7 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
} }
path.push("e"); path.push("e");
node.path = path.join(""); node.path = path.join("");
return node;
}, },
/** /**
@@ -722,6 +734,9 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement}
*/ */
drawRectangle: function(node, geometry) { drawRectangle: function(node, geometry) {
var resolution = this.getResolution(); var resolution = this.getResolution();
@@ -730,6 +745,8 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
node.style.top = geometry.y/resolution + "px"; node.style.top = geometry.y/resolution + "px";
node.style.width = geometry.width/resolution + "px"; node.style.width = geometry.width/resolution + "px";
node.style.height = geometry.height/resolution + "px"; node.style.height = geometry.height/resolution + "px";
return node;
}, },
/** /**
@@ -738,6 +755,9 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* Parameters: * Parameters:
* node - {DOMElement} * node - {DOMElement}
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement}
*/ */
drawSurface: function(node, geometry) { drawSurface: function(node, geometry) {
@@ -761,6 +781,7 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
path.push(" x e"); path.push(" x e");
node.path = path.join(""); node.path = path.join("");
return node;
}, },
/** /**

View File

@@ -126,23 +126,28 @@
} }
function test_Layer_Vector_drawFeature(t) { function test_Layer_Vector_drawFeature(t) {
t.plan(4); t.plan(6);
var layer = new OpenLayers.Layer.Vector("Test Layer"); var layer = new OpenLayers.Layer.Vector("Test Layer", {isBaseLayer: true});
var map = new OpenLayers.Map('map'); var map = new OpenLayers.Map('map', {
maxExtent: new OpenLayers.Bounds(-100, -100, 100, 100)
});
map.addLayer(layer); map.addLayer(layer);
var geometry = new OpenLayers.Geometry.Point(10, 10); var geometry = new OpenLayers.Geometry.Point(10, 10);
var feature = new OpenLayers.Feature.Vector(geometry); var feature = new OpenLayers.Feature.Vector(geometry);
var f, s; var f, s;
// Layer renderer needs a destroy, and draw, for functional tests. // Bogus layer renderer needs some methods
// for functional tests.
layer.renderer = { layer.renderer = {
drawFeature: function(feature, style) { drawFeature: function(feature, style) {
f = feature; f = feature;
s = style; s = style;
}, },
root: document.createElement("div"),
destroy: function() { }, destroy: function() { },
eraseFeatures: function() {} eraseFeatures: function() {},
setExtent: function() {}
}; };
@@ -168,6 +173,17 @@
t.eq(customStyle.foo, s.foo, t.eq(customStyle.foo, s.foo,
"given a custom style, renders with that"); "given a custom style, renders with that");
// the real renderer's drawFeature method is tested in Renderer.html
layer.renderer.drawFeature = function(feature) {
return(feature.geometry.getBounds().intersectsBounds(map.getExtent()));
}
layer.addFeatures([feature]);
map.setCenter(new OpenLayers.Bounds(0, 0, 0, 0), 6);
t.ok(layer.unrenderedFeatures[feature.id], "Did not render feature outside the viewport.");
map.panTo(new OpenLayers.LonLat(10, 10));
t.ok(!layer.unrenderedFeatures[feature.id], "Rendered feature inside the viewport.");
layer.features = [];
} }
function test_Layer_Vector_eraseFeatures(t) { function test_Layer_Vector_eraseFeatures(t) {

View File

@@ -26,7 +26,7 @@
var r = new OpenLayers.Renderer(); var r = new OpenLayers.Renderer();
var extent = new OpenLayers.Bounds(1,2,3,4); var extent = new OpenLayers.Bounds(1,2,3,4);
r.resolution = 1; r.resolution = 1;
r.setExtent(extent); r.setExtent(extent, true);
t.ok(r.extent.equals(extent), "extent is correctly set"); t.ok(r.extent.equals(extent), "extent is correctly set");
t.eq(r.resolution, null, "resolution nullified"); t.eq(r.resolution, null, "resolution nullified");
} }

View File

@@ -134,7 +134,6 @@
var r = create_renderer(); var r = create_renderer();
var element = document.createElement("div"); var element = document.createElement("div");
document.body.appendChild(element);
r.root = element; r.root = element;
r.nodeFactory = function(id, type) { r.nodeFactory = function(id, type) {
@@ -142,14 +141,15 @@
return element; return element;
}; };
var g_Node = null; var g_Node = null;
var b_Node = null;
r.drawGeometryNode = function(node, geometry, style) { r.drawGeometryNode = function(node, geometry, style) {
g_Node = node; g_Node = node;
return node; return {node: node, complete: true};
}; };
r.redrawBackgroundNode = function(id, geometry, style, featureId) { r.redrawBackgroundNode = function(id, geometry, style, featureId) {
var el = r.nodeFactory(); b_Node = r.nodeFactory();
el.id = "foo_background"; b_Node.id = "foo_background";
r.root.appendChild(el); r.root.appendChild(b_Node);
}; };
r.getNodeType = function(geometry, style) { r.getNodeType = function(geometry, style) {
@@ -163,20 +163,25 @@
var featureId = 'dude'; var featureId = 'dude';
r.drawGeometry(geometry, style, featureId); r.drawGeometry(geometry, style, featureId);
t.ok(g_Node.parentNode == r.root, "node is correctly appended to root"); t.ok(g_Node.parentNode == r.root, "node is correctly appended to root");
t.eq(r.root.childNodes.length, 2, "redrawBackgroundNode appended background node"); t.ok(b_Node.parentNode == r.root, "redrawBackgroundNode appended background node");
t.eq(g_Node._featureId, 'dude', "_featureId is correct"); t.eq(g_Node._featureId, 'dude', "_featureId is correct");
t.eq(g_Node._style.backgroundGraphic, "foo", "_style is correct"); t.eq(g_Node._style.backgroundGraphic, "foo", "_style is correct");
t.eq(g_Node._geometryClass, 'bar', "_geometryClass is correct"); t.eq(g_Node._geometryClass, 'bar', "_geometryClass is correct");
var _getElement = OpenLayers.Util.getElement; var returnNode = function(id) {
OpenLayers.Util.getElement = function(id) { return id == "foo_background" ? b_Node : g_Node;
return g_Node;
} }
var _getElement = document.getElementById;
document.getElementById = returnNode;
OpenLayers.Util.getElement = returnNode;
style = {'display':'none'}; style = {'display':'none'};
r.drawGeometry(geometry, style, featureId); r.drawGeometry(geometry, style, featureId);
t.ok(g_Node.parentNode != r.root, "node is correctly removed"); t.ok(g_Node.parentNode != r.root, "node is correctly removed");
t.ok(!document.getElementById("foo_background"), "background node correctly removed") t.ok(b_Node.parentNode != r.root, "background node correctly removed")
document.getElementById = _getElement;
tearDown(); tearDown();
} }
@@ -216,7 +221,7 @@
properDraw = true; properDraw = true;
return {}; return {};
}; };
geometry = {CLASS_NAME: 'OpenLayers.Geometry.LineString'}; geometry = {id: "foo", CLASS_NAME: 'OpenLayers.Geometry.LineString'};
style = true; style = true;
r.drawGeometry(geometry, style); r.drawGeometry(geometry, style);
t.ok(properDraw, "drawGeometry called drawLineString when passed a line string"); t.ok(properDraw, "drawGeometry called drawLineString when passed a line string");

View File

@@ -79,7 +79,7 @@
// test extent changes // test extent changes
var extent = new OpenLayers.Bounds(4,3,2,1); var extent = new OpenLayers.Bounds(4,3,2,1);
r.setExtent(extent); r.setExtent(extent);
t.eq(r.rendererRoot.getAttributeNS(null, "viewBox"), "6 6 4 4", "rendererRoot viewBox is correct after a new setExtent"); t.eq(r.root.getAttributeNS(null, "transform").replace(/ /g, ""), "translate(-6,-6)", "rendererRoot viewBox is correct after a new setExtent");
OpenLayers.Renderer.Elements.prototype.setExtent = OpenLayers.Renderer.Elements.prototype.setExtent =
OpenLayers.Renderer.Elements.prototype._setExtent; OpenLayers.Renderer.Elements.prototype._setExtent;
@@ -128,7 +128,7 @@
return; return;
} }
t.plan(6); t.plan(5);
var r = new OpenLayers.Renderer.SVG(document.body); var r = new OpenLayers.Renderer.SVG(document.body);
r.resolution = 0.5; r.resolution = 0.5;
@@ -151,14 +151,22 @@
// #1274: out of bound node fails when first added // #1274: out of bound node fails when first added
var geometry = { var geometry = {
x: 10000000, x: 10000000,
y: 200000000 y: 200000000,
CLASS_NAME: "OpenLayers.Geometry.Point",
id: "foo"
}
node.id = geometry.id;
r.root.appendChild(node);
var drawCircleCalled = false;
r.drawCircle = function() {
drawCircleCalled = true;
return OpenLayers.Renderer.SVG.prototype.drawCircle.apply(r, arguments);
} }
r.drawCircle(node, geometry, "blah_4000"); r.drawGeometry(geometry, {pointRadius: 3}, "blah_4000");
t.eq(drawCircleCalled, true, "drawCircle called on drawGeometry for a point geometry.")
t.eq(node.getAttributeNS(null, 'cx'), '', "cx is correct"); t.ok(node.parentNode != r.root, "circle will not be drawn when coordinates are outside the valid range");
t.eq(node.getAttributeNS(null, 'cy'), '', "cy is correct");
t.eq(node.getAttributeNS(null, 'r'), '0', "r is correct");
} }
function test_SVG_drawlinestring(t) { function test_SVG_drawlinestring(t) {
@@ -181,7 +189,7 @@
r.getComponentsString = function(c) { r.getComponentsString = function(c) {
g_GetString = true; g_GetString = true;
g_Components = c; g_Components = c;
return "bar"; return {path: "bar", complete: true};
} }
r.drawLineString(node, geometry); r.drawLineString(node, geometry);
@@ -210,7 +218,7 @@
r.getComponentsString = function(c) { r.getComponentsString = function(c) {
g_GetString = true; g_GetString = true;
g_Components = c; g_Components = c;
return "bar"; return {path: "bar", complete: true};
} }
r.drawLinearRing(node, geometry); r.drawLinearRing(node, geometry);
@@ -255,8 +263,7 @@
r.getShortString = function(c) { r.getShortString = function(c) {
return false; return false;
} }
r.drawPolygon(node, geometry); t.eq(r.drawPolygon(node, geometry), false, "drawPolygon returns false if one linearRing cannot be drawn");
t.eq(node.getAttributeNS(null, "d"), "", "d attribute is empty if one linearRing cannot be drawn");
} }
function test_SVG_drawrectangle(t) { function test_SVG_drawrectangle(t) {
@@ -319,8 +326,8 @@
r.getShortString = function(c) { r.getShortString = function(c) {
return false; return false;
} }
r.drawSurface(node, geometry);
t.eq(node.getAttributeNS(null, "d"), "", "d attribute is empty if one linearRing cannot be drawn"); t.eq(r.drawSurface(node, geometry), false, "drawSurface returns false if one linearRing cannot be drawn");
} }
function test_SVG_getcomponentsstring(t) { function test_SVG_getcomponentsstring(t) {
@@ -340,7 +347,7 @@
return p; return p;
}; };
var string = OpenLayers.Renderer.SVG.prototype.getComponentsString(components); var string = OpenLayers.Renderer.SVG.prototype.getComponentsString(components).path;
t.eq(string, "foo,bar", "returned string is correct"); t.eq(string, "foo,bar", "returned string is correct");
OpenLayers.Renderer.SVG.prototype.getShortString = OpenLayers.Renderer.SVG.prototype.getShortString =
@@ -423,6 +430,35 @@
t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dashdot"}, 1), "4,4,1,4", "dashdot dasharray created correctly"); t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dashdot"}, 1), "4,4,1,4", "dashdot dasharray created correctly");
t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "longdashdot"}, 1), "8,4,1,4", "dashdot dasharray created correctly"); t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "longdashdot"}, 1), "8,4,1,4", "dashdot dasharray created correctly");
} }
function test_svg_clipline(t) {
if (!OpenLayers.Renderer.SVG.prototype.supported()) {
t.plan(0);
return;
}
t.plan(1);
var r = new OpenLayers.Renderer.SVG(document.body);
r.setSize(new OpenLayers.Size(0, 0));
r.map = {
getResolution: function() {
return 0.5;
}
}
r.setExtent(new OpenLayers.Bounds(0, 0, 0, 0));
var geometry = new OpenLayers.Geometry.LineString([
new OpenLayers.Geometry.Point(0, -5000),
new OpenLayers.Geometry.Point(10000, 0),
new OpenLayers.Geometry.Point(0, 5000)
]);
var node = document.createElement('div');
r.drawLineString(node, geometry);
t.eq(node.getAttribute("points"), "0,10000,15000,2500,15000,-2500,0,-10000", "Geometry correctly clipped at inValidRange bounds");
}
</script> </script>
</head> </head>

View File

@@ -0,0 +1,123 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SVG inValidRange Test Case</title><link
href="../../theme/default/style.css"
rel="stylesheet" type="text/css">
<style>
#map {
width: 512px;
height: 512px;
border: 1px solid #4B3624;
background: White;
}
.olControlAttribution { bottom: 0px!important }
</style>
<script src="../../lib/OpenLayers.js"
type="text/javascript"></script>
<script type="text/javascript">var map;
// avoid pink tiles
OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3;
OpenLayers.Util.onImageLoadErrorColor = "transparent";
var vectorLayer;
var markerLayer, boxes, newPoint;
function init(){
var options = {
projection: new OpenLayers.Projection("EPSG:900913"),
units: "m",
numZoomLevels: 19,
maxResolution: 156543.0339,
maxExtent: new OpenLayers.Bounds(-20037508, -20037508,
20037508, 20037508.34)
};
map = new OpenLayers.Map('map', options);
map.addControl(new OpenLayers.Control.MousePosition());
vectorLayer = new OpenLayers.Layer.Vector("Trails", {isBaseLayer: true});
markerLayer = new OpenLayers.Layer.Markers("WayPoints");
map.addLayers([vectorLayer,markerLayer]);
var style_trail = OpenLayers.Util.extend({},
OpenLayers.Feature.Vector.style['default']);
style_trail.strokeColor = "green";
style_trail.strokeWidth = 5;
var pointList = [];
newPoint = new OpenLayers.Geometry.Point(-13653735.8487833,5726045.3578081);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-13653731.3960036,5726056.5070679);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-13653730.8394062,5726044.7207079);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-13653743.1958697,5726043.9243328);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-13653754.1051798,5726046.9505586);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-13653760.4503907,5726056.5070679);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-13653767.4635187,5726065.5857612);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-13653830.136392,5726052.2066375);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-13653846.5003571,5726042.3315828);
pointList.push(newPoint);
var lineFeature = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.LineString(pointList));
lineFeature.fid = 52730;
vectorLayer.addFeatures(lineFeature);
pointList = [];
newPoint = new OpenLayers.Geometry.Point(-12250153.3626406,4852001.6114048);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-12194315.5060664,4800503.5113048);
pointList.push(newPoint);
newPoint = new OpenLayers.Geometry.Point(-12180445.0975155,4873109.008858);
pointList.push(newPoint);
lineFeature = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.LineString(pointList),null,style_trail);
lineFeature.fid = 52751;
vectorLayer.addFeatures([lineFeature]);
var size = new OpenLayers.Size(15, 15);
var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
var icon = new OpenLayers.Icon('../../img/marker.png', size, offset);
markerLayer.addMarker(new OpenLayers.Marker(
new OpenLayers.LonLat((newPoint.x + 400), (newPoint.y - 400)), icon));
map.setCenter(new OpenLayers.LonLat(newPoint.x, newPoint.y), 13)
}
function zoomToScale(zoom) {
if (zoom == 8) map.zoomToScale(3385.5001275);
else if(zoom == 7) map.zoomToScale(6771.000255);
else if (zoom == 6) map.zoomToScale(13542);
else if (zoom == 5) map.zoomToScale(27084.001020);
else if (zoom == 4) map.zoomToScale(54168.001020);
else if (zoom == 3) map.zoomToScale(108337);
else if (zoom == 2) map.zoomToScale(3466752.1306573446);
else if (zoom == 1) map.zoomToScale(13867008.522629378);
else if (zoom == 0) map.zoomToScale(55468034.09051751);
}
</script>
</head>
<body onLoad="init()">
<h1 id="title">SVG inValidRange Clipping Test Case</h1>
<p>Behavior before fixing #1631: Push Zoom 5. You see lines. Push
Zoom 6. No lines.</p>
<div id="map">
</div>
<button onClick="zoomToScale(5);">Zoom 5</button>
<button onClick="zoomToScale(6);">Zoom 6</button>
<button onClick="zoomToScale(7);">Zoom 7</button>
<button onClick="zoomToScale(8);">Zoom 8</button>
</body>
</html>

View File

@@ -0,0 +1,58 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
<style type="text/css">
#map {
width: 512px;
height: 512px;
border: 1px solid gray;
}
</style>
<script src="../../lib/OpenLayers.js"></script>
<script type="text/javascript">
var map, point;
function init(){
var options = {
projection: new OpenLayers.Projection("EPSG:900913"),
displayProjection: new OpenLayers.Projection("EPSG:4326"),
units: "m",
maxResolution: 20, //0.07464553542137146,
maxExtent: new OpenLayers.Bounds(-20037508, -20037508,
20037508, 20037508.34)
};
map = new OpenLayers.Map('map', options);
var vector = new OpenLayers.Layer.Vector(
"Vectors",
{isBaseLayer: true}
);
map.addLayer(vector);
var x = -20000;//4.33791754;
var y = 20000;
point = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(x, y)
);
map.addLayer(vector);
vector.addFeatures([point]);
map.setCenter(new OpenLayers.LonLat(0, 0), 5);
}
function pan(){
map.panTo(point.geometry.getBounds().getCenterLonLat());
}
</script>
</head>
<body onload="init()">
<h3 id="title">SVG inValidRange Redraw Test Case</h3>
<p>Before fixing #1631, after klicking Go! no point would have appeared. The Go! button
pans the map over a long distance. Before dragging, the point would have been
outside the valid range, and the pan operation would not have triggered the SVG
coordinate system to be recreated. The new vector rendering takes care of all this. </p>
<div id="map"></div>
<input type="button" value="Go!" onclick="pan();"></input>
</body>
</html>

View File

@@ -73,7 +73,7 @@
} }
function vectorTestNew() { function vectorTestNew() {
vectorLayer.renderer.drawFeature = drawFeature; vectorLayer.renderer.drawFeature = OpenLayers.Renderer[vectorLayer.renderer.CLASS_NAME.split(".")[2]].prototype.drawFeature;
console.time("addFeatures"); console.time("addFeatures");
vectorLayer.addFeatures(features); vectorLayer.addFeatures(features);
@@ -89,17 +89,25 @@
map.addControl(new OpenLayers.Control.MousePosition()); map.addControl(new OpenLayers.Control.MousePosition());
map.setCenter(new OpenLayers.LonLat(-22.5, -22.5), 3); map.setCenter(new OpenLayers.LonLat(-22.5, -22.5), 3);
drawFeature = vectorLayer.renderer.drawFeature;
vectorLayer.events.register("featuresadded", this, nextRun); vectorLayer.events.register("featuresadded", this, nextRun);
features = new Array(100); features = new Array(200);
var x, y var x, y
for (var i = 0; i < 100; i++) { for (var i = 0; i < 200; i++) {
x = -Math.random()*45; x = -Math.random()*45;
y = -Math.random()*45; y = -Math.random()*45;
features[i] = new OpenLayers.Feature.Vector( features[i] = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.LinearRing([ new OpenLayers.Geometry.LinearRing([
new OpenLayers.Geometry.Point(
-Math.random()*5+x, -Math.random()*5+y),
new OpenLayers.Geometry.Point(
-Math.random()*5+x, -Math.random()*5+y),
new OpenLayers.Geometry.Point(
-Math.random()*5+x, -Math.random()*5+y),
new OpenLayers.Geometry.Point(
-Math.random()*5+x, -Math.random()*5+y),
new OpenLayers.Geometry.Point(
-Math.random()*5+x, -Math.random()*5+y),
new OpenLayers.Geometry.Point( new OpenLayers.Geometry.Point(
-Math.random()*5+x, -Math.random()*5+y), -Math.random()*5+x, -Math.random()*5+y),
new OpenLayers.Geometry.Point( new OpenLayers.Geometry.Point(
@@ -119,14 +127,16 @@
</script> </script>
</head> </head>
<body onload="init()"> <body onload="init()">
<h1 id="title">Vector Features Performance Test</h1> <h1 id="title">New Rendering - Vector Features Performance Test</h1>
<div id="map"></div> <div id="map"></div>
<p> <p>
This test examines if checking for a feature being inside the visible This test examines if checking for a feature being inside the visible
extent before rendering it has an impact on performance. Make sure that extent before rendering it has an impact on performance. Open the Firebug
the Firebug console is visible when running this test to see the results. console after running this test (hit F12) to see the results.
<br/>
After the performance test, you can drag around the map to see how the new
vector rendering feels where features get rendered only when they are visible
inside the map extent.
</p> </p>
</body> </body>
</html> </html>