Add ol.geom2.LineStringCollection

This commit is contained in:
Tom Payne
2013-05-01 16:17:18 +02:00
parent 4cc5441579
commit 1690cb9cae
3 changed files with 488 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
@exportSymbol ol.geom2.LineStringCollection
@exportSymbol ol.geom2.LineStringCollection.pack

View File

@@ -0,0 +1,183 @@
goog.provide('ol.geom2.LineString');
goog.provide('ol.geom2.LineStringCollection');
goog.require('goog.asserts');
goog.require('goog.object');
goog.require('ol.geom2');
goog.require('ol.structs.Buffer');
/**
* @typedef {Array.<Array.<number>>}
*/
ol.geom2.LineString;
/**
* @constructor
* @param {ol.structs.Buffer} buf Buffer.
* @param {Object.<number, Array.<number>>=} opt_ranges Ranges.
* @param {number=} opt_dim Dimension.
*/
ol.geom2.LineStringCollection = function(buf, opt_ranges, opt_dim) {
/**
* @type {ol.structs.Buffer}
*/
this.buf = buf;
/**
* @type {Object.<number, Array.<number>>}
*/
this.ranges = goog.isDef(opt_ranges) ? opt_ranges : {};
/**
* @type {number}
*/
this.dim = goog.isDef(opt_dim) ? opt_dim : 2;
};
/**
* @param {number} capacity Capacity.
* @param {number=} opt_dim Dimension.
* @return {ol.geom2.LineStringCollection} Line string collection.
*/
ol.geom2.LineStringCollection.createEmpty = function(capacity, opt_dim) {
var dim = goog.isDef(opt_dim) ? opt_dim : 2;
var buf = new ol.structs.Buffer(new Array(capacity * dim), 0);
return new ol.geom2.LineStringCollection(buf, undefined, dim);
};
/**
* @param {Array.<ol.geom2.LineString>} unpackedLineStrings Unpacked line
* strings.
* @param {number=} opt_capacity Capacity.
* @param {number=} opt_dim Dimension.
* @return {ol.geom2.LineStringCollection} Line string collection.
*/
ol.geom2.LineStringCollection.pack =
function(unpackedLineStrings, opt_capacity, opt_dim) {
var i;
var n = unpackedLineStrings.length;
var dim = goog.isDef(opt_dim) ? opt_dim :
n > 0 ? unpackedLineStrings[0][0].length : 2;
var capacity;
if (goog.isDef(opt_capacity)) {
capacity = opt_capacity;
} else {
capacity = 0;
for (i = 0; i < n; ++i) {
capacity += unpackedLineStrings[i].length;
}
}
capacity *= dim;
var arr = new Array(capacity);
/** @type {Object.<number, Array.<number>>} */
var ranges = {};
var offset = 0;
var start;
for (i = 0; i < n; ++i) {
goog.asserts.assert(unpackedLineStrings[i].length > 1);
start = offset;
offset = ol.geom2.packPoints(arr, offset, unpackedLineStrings[i], dim);
ranges[start] = [start, offset];
}
goog.asserts.assert(offset <= capacity);
var buf = new ol.structs.Buffer(arr, offset);
return new ol.geom2.LineStringCollection(buf, ranges, dim);
};
/**
* @param {ol.geom2.LineString} lineString Line string.
* @return {number} Offset.
*/
ol.geom2.LineStringCollection.prototype.add = function(lineString) {
var n = lineString.length * this.dim;
var offset = this.buf.allocate(n);
goog.asserts.assert(offset != -1);
this.ranges[offset] = [offset, offset + n];
ol.geom2.packPoints(this.buf.getArray(), offset, lineString, this.dim);
return offset;
};
/**
* @param {number} offset Offset.
* @return {ol.geom2.LineString} Line string.
*/
ol.geom2.LineStringCollection.prototype.get = function(offset) {
goog.asserts.assert(offset in this.ranges);
var range = this.ranges[offset];
return ol.geom2.unpackPoints(
this.buf.getArray(), range[0], range[1], this.dim);
};
/**
* @return {number} Count.
*/
ol.geom2.LineStringCollection.prototype.getCount = function() {
return goog.object.getCount(this.ranges);
};
/**
* @return {ol.Extent} Extent.
*/
ol.geom2.LineStringCollection.prototype.getExtent = function() {
return ol.geom2.getExtent(this.buf, this.dim);
};
/**
* @param {number} offset Offset.
*/
ol.geom2.LineStringCollection.prototype.remove = function(offset) {
goog.asserts.assert(offset in this.ranges);
var range = this.ranges[offset];
this.buf.remove(range[1] - range[0], range[0]);
delete this.ranges[offset];
};
/**
* @param {number} offset Offset.
* @param {ol.geom2.LineString} lineString Line string.
* @return {number} Offset.
*/
ol.geom2.LineStringCollection.prototype.set = function(offset, lineString) {
var dim = this.dim;
goog.asserts.assert(offset in this.ranges);
var range = this.ranges[offset];
if (lineString.length * dim == range[1] - range[0]) {
ol.geom2.packPoints(this.buf.getArray(), range[0], lineString, dim);
this.buf.markDirty(range[1] - range[0], range[0]);
return offset;
} else {
this.remove(offset);
return this.add(lineString);
}
};
/**
* @return {Array.<ol.geom2.LineString>} Line strings.
*/
ol.geom2.LineStringCollection.prototype.unpack = function() {
var dim = this.dim;
var n = this.getCount();
var lineStrings = new Array(n);
var i = 0;
var offset, range;
for (offset in this.ranges) {
range = this.ranges[Number(offset)];
lineStrings[i++] = ol.geom2.unpackPoints(
this.buf.getArray(), range[0], range[1], dim);
}
return lineStrings;
};

View File

@@ -0,0 +1,303 @@
goog.provide('ol.test.geom2.LineStringCollection');
describe('ol.geom2.LineStringCollection', function() {
describe('createEmpty', function() {
it('creates an empty instance with the specified capacity', function() {
var lsc = ol.geom2.LineStringCollection.createEmpty(16);
expect(lsc.getCount()).to.be(0);
expect(lsc.buf.getArray()).to.have.length(32);
});
it('can create empty collections for higher dimensions', function() {
var lsc = ol.geom2.LineStringCollection.createEmpty(16, 3);
expect(lsc.getCount()).to.be(0);
expect(lsc.buf.getArray()).to.have.length(48);
});
});
describe('pack', function() {
it('packs an empty array', function() {
var lsc = ol.geom2.LineStringCollection.pack([]);
expect(lsc.buf.getArray()).to.be.empty();
expect(lsc.ranges).to.be.empty();
expect(lsc.dim).to.be(2);
});
it('packs an empty array with a capacity', function() {
var lsc = ol.geom2.LineStringCollection.pack([], 4);
expect(lsc.buf.getArray()).to.eql(
[NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]);
expect(lsc.ranges).to.be.empty();
expect(lsc.dim).to.be(2);
});
it('packs an array of line strings', function() {
var lsc = ol.geom2.LineStringCollection.pack(
[[[0, 1], [2, 3], [4, 5]], [[6, 7], [8, 9]]]);
expect(lsc.buf.getArray()).to.eql([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
expect(lsc.getCount()).to.be(2);
expect(lsc.ranges[0]).to.eql([0, 6]);
expect(lsc.ranges[6]).to.eql([6, 10]);
expect(lsc.dim).to.be(2);
});
it('packs an array of line strings with a different dimension', function() {
var lsc = ol.geom2.LineStringCollection.pack(
[[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]]);
expect(lsc.buf.getArray()).to.eql([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
expect(lsc.getCount()).to.be(2);
expect(lsc.ranges[0]).to.eql([0, 6]);
expect(lsc.ranges[6]).to.eql([6, 12]);
expect(lsc.dim).to.be(3);
});
it('packs an array of line strings with extra capacity', function() {
var lsc = ol.geom2.LineStringCollection.pack(
[[[0, 1], [2, 3], [4, 5]], [[6, 7], [8, 9]]], 16);
expect(lsc.buf.getArray().slice(0, 10)).to.eql(
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
expect(lsc.buf.getArray()).to.have.length(32);
expect(lsc.getCount()).to.be(2);
expect(lsc.ranges[0]).to.eql([0, 6]);
expect(lsc.ranges[6]).to.eql([6, 10]);
expect(lsc.dim).to.be(2);
});
it('throws an error when dimensions are inconsistent', function() {
expect(function() {
var lsc = ol.geom2.LineStringCollection.pack([[0, 1], [2, 3, 4]]);
lsc = lsc; // suppress gjslint warning about unused variable
}).to.throwException();
});
it('throws an error when a line string is too short', function() {
expect(function() {
var lsc = ol.geom2.LineStringCollection.pack([[0, 1]]);
lsc = lsc; // suppress gjslint warning about unused variable
}).to.throwException();
});
it('throws an error when the capacity is too small', function() {
expect(function() {
var lsc = ol.geom2.LineStringCollection.pack(
[[[0, 1], [2, 3], [4, 5]], [[6, 7], [8, 9]]], 4);
lsc = lsc; // suppress gjslint warning about unused variable
}).to.throwException();
});
});
describe('with an empty instance with spare capacity', function() {
var lsc;
beforeEach(function() {
var buf = new ol.structs.Buffer(new Array(8), 0);
lsc = new ol.geom2.LineStringCollection(buf);
});
describe('add', function() {
it('adds a line string', function() {
var offset = lsc.add([[0, 1], [2, 3]]);
expect(offset).to.be(0);
expect(lsc.getCount()).to.be(1);
expect(lsc.ranges[0]).to.eql([0, 4]);
expect(lsc.dim).to.be(2);
});
});
describe('getCount', function() {
it('returns zero', function() {
expect(lsc.getCount()).to.be(0);
});
});
describe('getExtent', function() {
it('returns an empty extent', function() {
expect(ol.extent.isEmpty(lsc.getExtent())).to.be(true);
});
});
describe('remove', function() {
it('throws an exception', function() {
expect(function() {
lsc.remove(0);
}).to.throwException();
});
});
});
describe('with an initial line string', function() {
var lsc, offset;
beforeEach(function() {
var buf = new ol.structs.Buffer(new Array(8), 0);
lsc = new ol.geom2.LineStringCollection(buf);
offset = lsc.add([[0, 1], [2, 3]]);
});
describe('add', function() {
it('can add a second line string', function() {
var offset2 = lsc.add([[4, 5], [6, 7]]);
expect(offset2).to.be(4);
expect(lsc.getCount()).to.be(2);
expect(lsc.ranges[0]).to.eql([0, 4]);
expect(lsc.ranges[4]).to.eql([4, 8]);
expect(lsc.dim).to.be(2);
});
});
describe('get', function() {
it('returns the expected line string', function() {
expect(lsc.get(0)).to.eql([[0, 1], [2, 3]]);
});
});
describe('getCount', function() {
it('returns the expected value', function() {
expect(lsc.getCount()).to.be(1);
});
});
describe('getExtent', function() {
it('returns the expected extent', function() {
expect(lsc.getExtent()).to.eql([0, 2, 1, 3]);
});
});
describe('remove', function() {
it('removes the line string', function() {
lsc.remove(0);
expect(lsc.getCount()).to.be(0);
});
});
describe('set', function() {
it('can update the line string in place', function() {
expect(lsc.set(0, [[4, 5], [6, 7]])).to.be(0);
expect(lsc.buf.getArray()).to.eql([4, 5, 6, 7, NaN, NaN, NaN, NaN]);
});
it('can replace the line string with a shorter one', function() {
expect(lsc.set(0, [[4, 5]])).to.be(0);
expect(lsc.buf.getArray()).to.eql([4, 5, NaN, NaN, NaN, NaN, NaN, NaN]);
});
it('can replace the line string with a longer one', function() {
expect(lsc.set(0, [[4, 5], [6, 7], [8, 9], [10, 11]])).to.be(0);
expect(lsc.buf.getArray()).to.eql([4, 5, 6, 7, 8, 9, 10, 11]);
});
});
describe('unpack', function() {
it('returns the expected value', function() {
expect(lsc.unpack()).to.eql([[[0, 1], [2, 3]]]);
});
});
});
describe('with multiple initial line strings', function() {
var lsc;
beforeEach(function() {
lsc = ol.geom2.LineStringCollection.pack(
[[[0, 1], [2, 3]], [[4, 5], [6, 7], [8, 9]]], 16);
});
describe('get', function() {
it('returns the expected values', function() {
expect(lsc.get(0)).to.eql([[0, 1], [2, 3]]);
expect(lsc.get(4)).to.eql([[4, 5], [6, 7], [8, 9]]);
});
});
describe('getCount', function() {
it('returns the expected value', function() {
expect(lsc.getCount()).to.be(2);
});
});
describe('getExtent', function() {
it('returns the expected value', function() {
expect(lsc.getExtent()).to.eql([0, 8, 1, 9]);
});
});
describe('remove', function() {
it('can remove the first line string', function() {
lsc.remove(0);
expect(lsc.getCount()).to.be(1);
expect(lsc.get(4)).to.eql([[4, 5], [6, 7], [8, 9]]);
});
it('can remove the second line string', function() {
lsc.remove(4);
expect(lsc.getCount()).to.be(1);
expect(lsc.get(0)).to.eql([[0, 1], [2, 3]]);
});
});
describe('usage examples', function() {
it('allows the first line string to be replaced', function() {
lsc.remove(0);
expect(lsc.getCount()).to.be(1);
expect(lsc.add([[10, 11], [12, 13]])).to.be(0);
expect(lsc.getCount()).to.be(2);
expect(lsc.get(0)).to.eql([[10, 11], [12, 13]]);
});
it('will allocate at the end of the array', function() {
lsc.remove(0);
expect(lsc.getCount()).to.be(1);
expect(lsc.add([[10, 11], [12, 13], [14, 15]])).to.be(10);
expect(lsc.getCount()).to.be(2);
expect(lsc.get(10)).to.eql([[10, 11], [12, 13], [14, 15]]);
});
});
});
});
goog.require('ol.geom2.LineStringCollection');
goog.require('ol.extent');
goog.require('ol.structs.Buffer');