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:
@@ -31,14 +31,14 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
* {Integer} Firefox has a limitation where values larger or smaller than
|
||||
* about 15000 in an SVG document lock the browser up. This
|
||||
* works around it.
|
||||
*/
|
||||
*/
|
||||
MAX_PIXEL: 15000,
|
||||
|
||||
/**
|
||||
* Property: localResolution
|
||||
* {Float}
|
||||
/**
|
||||
* Property: translationParameters
|
||||
* {Object} Hash with "x" and "y" properties
|
||||
*/
|
||||
localResolution: null,
|
||||
translationParameters: null,
|
||||
|
||||
/**
|
||||
* Property: symbolSize
|
||||
@@ -58,6 +58,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
}
|
||||
OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
|
||||
arguments);
|
||||
this.translationParameters = {x: 0, y: 0};
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -86,16 +87,20 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
* See #669 for more information
|
||||
*
|
||||
* Parameters:
|
||||
* x - {Integer}
|
||||
* y - {Integer}
|
||||
* x - {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:
|
||||
* {Boolean} Whether or not the 'x' and 'y' coordinates are in the
|
||||
* valid range.
|
||||
*/
|
||||
inValidRange: function(x, y) {
|
||||
return (x >= -this.MAX_PIXEL && x <= this.MAX_PIXEL &&
|
||||
y >= -this.MAX_PIXEL && y <= this.MAX_PIXEL);
|
||||
inValidRange: function(x, y, xyOnly) {
|
||||
var left = x + (xyOnly ? 0 : this.translationParameters.x);
|
||||
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:
|
||||
* 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,
|
||||
arguments);
|
||||
|
||||
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
|
||||
// the features will redraw.
|
||||
if (!this.localResolution || resolution != this.localResolution) {
|
||||
this.left = -extent.left / resolution;
|
||||
this.top = extent.top / resolution;
|
||||
}
|
||||
|
||||
var left = 0;
|
||||
var top = 0;
|
||||
if (resolutionChanged) {
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
// Set the viewbox
|
||||
var extentString = "0 0 " + this.size.w + " " + this.size.h;
|
||||
|
||||
// If the resolution has not changed, we already have features, and we need
|
||||
// to adjust the viewbox to fit them.
|
||||
if (this.localResolution && resolution == this.localResolution) {
|
||||
left = (this.left) - (-extent.left / resolution);
|
||||
top = (this.top) - (extent.top / resolution);
|
||||
}
|
||||
|
||||
// Store resolution for use later.
|
||||
this.localResolution = resolution;
|
||||
|
||||
// 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 + " " +
|
||||
this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
|
||||
this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
|
||||
this.translate(0, 0);
|
||||
return true;
|
||||
} else {
|
||||
var inRange = this.translate(left - this.left, top - this.top);
|
||||
if (!inRange) {
|
||||
// recenter the coordinate system
|
||||
this.setExtent(extent, true);
|
||||
}
|
||||
return inRange;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: translate
|
||||
* 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:
|
||||
* node - {DOMElement}
|
||||
* geometry - {<OpenLayers.Geometry>}
|
||||
*
|
||||
* Returns:
|
||||
* {DOMElement} or false if the renderer could not draw the point
|
||||
*/
|
||||
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}
|
||||
* geometry - {<OpenLayers.Geometry>}
|
||||
* radius - {Float}
|
||||
*
|
||||
* Returns:
|
||||
* {DOMElement} or false if the renderer could not draw the circle
|
||||
*/
|
||||
drawCircle: function(node, geometry, radius) {
|
||||
var resolution = this.getResolution();
|
||||
@@ -436,10 +474,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
node.setAttributeNS(null, "cx", x);
|
||||
node.setAttributeNS(null, "cy", y);
|
||||
node.setAttributeNS(null, "r", radius);
|
||||
return node;
|
||||
} else {
|
||||
node.setAttributeNS(null, "cx", "");
|
||||
node.setAttributeNS(null, "cy", "");
|
||||
node.setAttributeNS(null, "r", 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
},
|
||||
@@ -451,23 +488,41 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
* Parameters:
|
||||
* node - {DOMElement}
|
||||
* 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) {
|
||||
node.setAttributeNS(null, "points",
|
||||
this.getComponentsString(geometry.components));
|
||||
var componentsResult = this.getComponentsString(geometry.components);
|
||||
if (componentsResult.path) {
|
||||
node.setAttributeNS(null, "points", componentsResult.path);
|
||||
return (componentsResult.complete ? node : null);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**v
|
||||
/**
|
||||
* Method: drawLinearRing
|
||||
* This method is only called by the renderer itself.
|
||||
*
|
||||
* Parameters:
|
||||
* node - {DOMElement}
|
||||
* 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) {
|
||||
node.setAttributeNS(null, "points",
|
||||
this.getComponentsString(geometry.components));
|
||||
var componentsResult = 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:
|
||||
* node - {DOMElement}
|
||||
* 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) {
|
||||
var d = "";
|
||||
var draw = true;
|
||||
var complete = true;
|
||||
var linearRingResult, path;
|
||||
for (var j=0, len=geometry.components.length; j<len; j++) {
|
||||
var linearRing = geometry.components[j];
|
||||
d += " M";
|
||||
for (var i=0, ilen=linearRing.components.length; i<ilen; i++) {
|
||||
var component = this.getShortString(linearRing.components[i]);
|
||||
if (component) {
|
||||
d += " " + component;
|
||||
} else {
|
||||
draw = false;
|
||||
}
|
||||
linearRingResult = this.getComponentsString(
|
||||
geometry.components[j].components, " ");
|
||||
path = linearRingResult.path;
|
||||
if (path) {
|
||||
d += " " + path;
|
||||
complete = linearRingResult.complete && complete;
|
||||
} else {
|
||||
draw = false;
|
||||
}
|
||||
}
|
||||
d += " z";
|
||||
if (draw) {
|
||||
node.setAttributeNS(null, "d", d);
|
||||
node.setAttributeNS(null, "fill-rule", "evenodd");
|
||||
return complete ? node : null;
|
||||
} else {
|
||||
node.setAttributeNS(null, "d", "");
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -509,6 +571,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
* Parameters:
|
||||
* node - {DOMElement}
|
||||
* geometry - {<OpenLayers.Geometry>}
|
||||
*
|
||||
* Returns:
|
||||
* {DOMElement} or false if the renderer could not draw the rectangle
|
||||
*/
|
||||
drawRectangle: function(node, geometry) {
|
||||
var resolution = this.getResolution();
|
||||
@@ -520,11 +585,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
node.setAttributeNS(null, "y", y);
|
||||
node.setAttributeNS(null, "width", geometry.width / resolution);
|
||||
node.setAttributeNS(null, "height", geometry.height / resolution);
|
||||
return node;
|
||||
} else {
|
||||
node.setAttributeNS(null, "x", "");
|
||||
node.setAttributeNS(null, "y", "");
|
||||
node.setAttributeNS(null, "width", 0);
|
||||
node.setAttributeNS(null, "height", 0);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -535,6 +598,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
* Parameters:
|
||||
* node - {DOMElement}
|
||||
* geometry - {<OpenLayers.Geometry>}
|
||||
*
|
||||
* Returns:
|
||||
* {DOMElement} or false if the renderer could not draw the surface
|
||||
*/
|
||||
drawSurface: function(node, geometry) {
|
||||
|
||||
@@ -559,8 +625,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
d += " Z";
|
||||
if (draw) {
|
||||
node.setAttributeNS(null, "d", d);
|
||||
return node;
|
||||
} else {
|
||||
node.setAttributeNS(null, "d", "");
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -569,19 +636,93 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
*
|
||||
* Parameters:
|
||||
* 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 = [];
|
||||
for(var i=0, len=components.length; i<len; i++) {
|
||||
var component = this.getShortString(components[i]);
|
||||
if (component) {
|
||||
strings.push(component);
|
||||
var str, component, j;
|
||||
for(var i=0; i<len; i++) {
|
||||
component = components[i];
|
||||
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
|
||||
*
|
||||
@@ -589,7 +730,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
||||
* point - {<OpenLayers.Geometry.Point>}
|
||||
*
|
||||
* Returns:
|
||||
* {String}
|
||||
* {String} or false if point is outside the valid range
|
||||
*/
|
||||
getShortString: function(point) {
|
||||
var resolution = this.getResolution();
|
||||
|
||||
Reference in New Issue
Block a user