Better label placement for polygons

Using the y-coordinate of the polygon's bounding box, this
simple algorithm intersects the polygon with the horizontal
center line of its bounding box. The x-coordinate of the label
point is the center of the longest segment of this intersection
that is inside the polygon.
This commit is contained in:
ahocevar
2013-07-20 10:36:13 +02:00
parent c3bed305c7
commit 11ef2cb7d1
2 changed files with 53 additions and 3 deletions

View File

@@ -1,6 +1,7 @@
goog.provide('ol.geom.Polygon');
goog.require('goog.asserts');
goog.require('ol.extent');
goog.require('ol.geom.Geometry');
goog.require('ol.geom.GeometryType');
goog.require('ol.geom.LinearRing');
@@ -35,6 +36,12 @@ ol.geom.Polygon = function(coordinates, opt_shared) {
vertices = new ol.geom.SharedVertices({dimension: dimension});
}
/**
* @private
* @type {ol.Coordinate}
*/
this.labelPoint_ = null;
/**
* @type {ol.geom.SharedVertices}
*/
@@ -126,3 +133,48 @@ ol.geom.Polygon.prototype.containsCoordinate = function(coordinate) {
}
return containsCoordinate;
};
/**
* Calculates a label point that is guaranteed to be inside the polygon.
* @return {ol.Coordinate} The label point.
*/
ol.geom.Polygon.prototype.getLabelPoint = function() {
if (goog.isNull(this.labelPoint_)) {
var center = ol.extent.getCenter(this.getBounds()),
resultY = center[1],
vertices = this.rings[0].getCoordinates(),
intersections = [],
maxLength = 0,
i, vertex1, vertex2, x, segmentLength, resultX;
// Calculate intersections with the horizontal bounding box center line
for (i = vertices.length - 1; i >= 1; --i) {
vertex1 = vertices[i];
vertex2 = vertices[i - 1];
if ((vertex1[1] >= resultY && vertex2[1] <= resultY) ||
(vertex1[1] <= resultY && vertex2[1] >= resultY)) {
x = (resultY - vertex1[1]) / (vertex2[1] - vertex1[1]) *
(vertex2[0] - vertex1[0]) + vertex1[0];
intersections.push(x);
}
}
// Find the longest segment of the horizontal bounding box center line that
// has its center point inside the polygon
intersections.sort();
for (i = intersections.length - 1; i >= 1; --i) {
segmentLength = Math.abs(intersections[i] - intersections[i - 1]);
if (segmentLength > maxLength) {
x = (intersections[i] + intersections[i - 1]) / 2;
if (this.containsCoordinate([x, resultY])) {
maxLength = segmentLength;
resultX = x;
}
}
}
this.labelPoint_ = [resultX, resultY];
}
return this.labelPoint_;
};

View File

@@ -8,7 +8,6 @@ goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.vec.Mat4');
goog.require('ol.Feature');
goog.require('ol.extent');
goog.require('ol.geom.AbstractCollection');
goog.require('ol.geom.Geometry');
goog.require('ol.geom.GeometryType');
@@ -446,8 +445,7 @@ ol.renderer.canvas.VectorRenderer.getLabelVectors = function(geometry) {
return [[geometry.get(0), geometry.get(1), 0]];
}
if (type == ol.geom.GeometryType.POLYGON) {
// TODO: better label placement
var coordinates = ol.extent.getCenter(geometry.getBounds());
var coordinates = geometry.getLabelPoint();
return [[coordinates[0], coordinates[1], 0]];
}
throw new Error('Label rendering not implemented for geometry type: ' +