diff --git a/src/ol/geom/flat/interpolateflatgeom.js b/src/ol/geom/flat/interpolateflatgeom.js new file mode 100644 index 0000000000..6a7a54031d --- /dev/null +++ b/src/ol/geom/flat/interpolateflatgeom.js @@ -0,0 +1,190 @@ +goog.provide('ol.geom.flat.interpolate'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.math'); +goog.require('ol.geom.flat'); + + +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} fraction Fraction. + * @param {Array.=} opt_dest Destination. + * @return {Array.} Destination. + */ +ol.geom.flat.interpolate.lineString = + function(flatCoordinates, offset, end, stride, fraction, opt_dest) { + // FIXME interpolate extra dimensions + goog.asserts.assert(0 <= fraction && fraction <= 1); + var pointX = NaN; + var pointY = NaN; + var n = (end - offset) / stride; + if (n === 0) { + goog.asserts.fail(); + } else if (n == 1) { + pointX = flatCoordinates[offset]; + pointY = flatCoordinates[offset + 1]; + } else if (n == 2) { + pointX = (1 - fraction) * flatCoordinates[offset] + + fraction * flatCoordinates[offset + stride]; + pointY = (1 - fraction) * flatCoordinates[offset + 1] + + fraction * flatCoordinates[offset + stride + 1]; + } else { + var x1 = flatCoordinates[offset]; + var y1 = flatCoordinates[offset + 1]; + var length = 0; + var cumulativeLengths = [0]; + var i; + for (i = offset + stride; i < end; i += stride) { + var x2 = flatCoordinates[i]; + var y2 = flatCoordinates[i + 1]; + length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + cumulativeLengths.push(length); + x1 = x2; + y1 = y2; + } + var target = fraction * length; + var index = goog.array.binarySearch(cumulativeLengths, target); + if (index < 0) { + var t = (target - cumulativeLengths[-index - 2]) / + (cumulativeLengths[-index - 1] - cumulativeLengths[-index - 2]); + var o = offset + (-index - 2) * stride; + pointX = goog.math.lerp( + flatCoordinates[o], flatCoordinates[o + stride], t); + pointY = goog.math.lerp( + flatCoordinates[o + 1], flatCoordinates[o + stride + 1], t); + } else { + pointX = flatCoordinates[offset + index * stride]; + pointY = flatCoordinates[offset + index * stride + 1]; + } + } + if (goog.isDefAndNotNull(opt_dest)) { + opt_dest.push(pointX, pointY); + return opt_dest; + } else { + return [pointX, pointY]; + } +}; + + +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} m M. + * @param {boolean} extrapolate Extrapolate. + * @return {ol.Coordinate} Coordinate. + */ +ol.geom.flat.lineStringCoordinateAtM = + function(flatCoordinates, offset, end, stride, m, extrapolate) { + if (end == offset) { + return null; + } + var coordinate; + if (m < flatCoordinates[offset + stride - 1]) { + if (extrapolate) { + coordinate = flatCoordinates.slice(offset, offset + stride); + coordinate[stride - 1] = m; + return coordinate; + } else { + return null; + } + } else if (flatCoordinates[end - 1] < m) { + if (extrapolate) { + coordinate = flatCoordinates.slice(end - stride, end); + coordinate[stride - 1] = m; + return coordinate; + } else { + return null; + } + } + // FIXME use O(1) search + if (m == flatCoordinates[offset + stride - 1]) { + return flatCoordinates.slice(offset, offset + stride); + } + var lo = offset / stride; + var hi = end / stride; + while (lo < hi) { + var mid = (lo + hi) >> 1; + if (m < flatCoordinates[(mid + 1) * stride - 1]) { + hi = mid; + } else { + lo = mid + 1; + } + } + var m0 = flatCoordinates[lo * stride - 1]; + if (m == m0) { + return flatCoordinates.slice((lo - 1) * stride, (lo - 1) * stride + stride); + } + var m1 = flatCoordinates[(lo + 1) * stride - 1]; + goog.asserts.assert(m0 < m); + goog.asserts.assert(m <= m1); + var t = (m - m0) / (m1 - m0); + coordinate = []; + var i; + for (i = 0; i < stride - 1; ++i) { + coordinate.push(goog.math.lerp(flatCoordinates[(lo - 1) * stride + i], + flatCoordinates[lo * stride + i], t)); + } + coordinate.push(m); + goog.asserts.assert(coordinate.length == stride); + return coordinate; +}; + + +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.} ends Ends. + * @param {number} stride Stride. + * @param {number} m M. + * @param {boolean} extrapolate Extrapolate. + * @param {boolean} interpolate Interpolate. + * @return {ol.Coordinate} Coordinate. + */ +ol.geom.flat.lineStringsCoordinateAtM = function( + flatCoordinates, offset, ends, stride, m, extrapolate, interpolate) { + if (interpolate) { + return ol.geom.flat.lineStringCoordinateAtM( + flatCoordinates, offset, ends[ends.length - 1], stride, m, extrapolate); + } + var coordinate; + if (m < flatCoordinates[stride - 1]) { + if (extrapolate) { + coordinate = flatCoordinates.slice(0, stride); + coordinate[stride - 1] = m; + return coordinate; + } else { + return null; + } + } + if (flatCoordinates[flatCoordinates.length - 1] < m) { + if (extrapolate) { + coordinate = flatCoordinates.slice(flatCoordinates.length - stride); + coordinate[stride - 1] = m; + return coordinate; + } else { + return null; + } + } + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + if (offset == end) { + continue; + } + if (m < flatCoordinates[offset + stride - 1]) { + return null; + } else if (m <= flatCoordinates[end - 1]) { + return ol.geom.flat.lineStringCoordinateAtM( + flatCoordinates, offset, end, stride, m, false); + } + offset = end; + } + goog.asserts.fail(); + return null; +}; diff --git a/src/ol/geom/flatgeom.js b/src/ol/geom/flatgeom.js index 9ba82972fd..3acca91c64 100644 --- a/src/ol/geom/flatgeom.js +++ b/src/ol/geom/flatgeom.js @@ -2,7 +2,6 @@ goog.provide('ol.geom.flat'); goog.require('goog.array'); goog.require('goog.asserts'); -goog.require('goog.math'); goog.require('goog.vec.Mat4'); goog.require('ol.extent'); @@ -199,190 +198,6 @@ ol.geom.flat.inflateCoordinatesss = }; -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} fraction Fraction. - * @param {Array.=} opt_dest Destination. - * @return {Array.} Destination. - */ -ol.geom.flat.lineStringInterpolate = - function(flatCoordinates, offset, end, stride, fraction, opt_dest) { - // FIXME interpolate extra dimensions - goog.asserts.assert(0 <= fraction && fraction <= 1); - var pointX = NaN; - var pointY = NaN; - var n = (end - offset) / stride; - if (n === 0) { - goog.asserts.fail(); - } else if (n == 1) { - pointX = flatCoordinates[offset]; - pointY = flatCoordinates[offset + 1]; - } else if (n == 2) { - pointX = (1 - fraction) * flatCoordinates[offset] + - fraction * flatCoordinates[offset + stride]; - pointY = (1 - fraction) * flatCoordinates[offset + 1] + - fraction * flatCoordinates[offset + stride + 1]; - } else { - var x1 = flatCoordinates[offset]; - var y1 = flatCoordinates[offset + 1]; - var length = 0; - var cumulativeLengths = [0]; - var i; - for (i = offset + stride; i < end; i += stride) { - var x2 = flatCoordinates[i]; - var y2 = flatCoordinates[i + 1]; - length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - cumulativeLengths.push(length); - x1 = x2; - y1 = y2; - } - var target = fraction * length; - var index = goog.array.binarySearch(cumulativeLengths, target); - if (index < 0) { - var t = (target - cumulativeLengths[-index - 2]) / - (cumulativeLengths[-index - 1] - cumulativeLengths[-index - 2]); - var o = offset + (-index - 2) * stride; - pointX = goog.math.lerp( - flatCoordinates[o], flatCoordinates[o + stride], t); - pointY = goog.math.lerp( - flatCoordinates[o + 1], flatCoordinates[o + stride + 1], t); - } else { - pointX = flatCoordinates[offset + index * stride]; - pointY = flatCoordinates[offset + index * stride + 1]; - } - } - if (goog.isDefAndNotNull(opt_dest)) { - opt_dest.push(pointX, pointY); - return opt_dest; - } else { - return [pointX, pointY]; - } -}; - - -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} m M. - * @param {boolean} extrapolate Extrapolate. - * @return {ol.Coordinate} Coordinate. - */ -ol.geom.flat.lineStringCoordinateAtM = - function(flatCoordinates, offset, end, stride, m, extrapolate) { - if (end == offset) { - return null; - } - var coordinate; - if (m < flatCoordinates[offset + stride - 1]) { - if (extrapolate) { - coordinate = flatCoordinates.slice(offset, offset + stride); - coordinate[stride - 1] = m; - return coordinate; - } else { - return null; - } - } else if (flatCoordinates[end - 1] < m) { - if (extrapolate) { - coordinate = flatCoordinates.slice(end - stride, end); - coordinate[stride - 1] = m; - return coordinate; - } else { - return null; - } - } - // FIXME use O(1) search - if (m == flatCoordinates[offset + stride - 1]) { - return flatCoordinates.slice(offset, offset + stride); - } - var lo = offset / stride; - var hi = end / stride; - while (lo < hi) { - var mid = (lo + hi) >> 1; - if (m < flatCoordinates[(mid + 1) * stride - 1]) { - hi = mid; - } else { - lo = mid + 1; - } - } - var m0 = flatCoordinates[lo * stride - 1]; - if (m == m0) { - return flatCoordinates.slice((lo - 1) * stride, (lo - 1) * stride + stride); - } - var m1 = flatCoordinates[(lo + 1) * stride - 1]; - goog.asserts.assert(m0 < m); - goog.asserts.assert(m <= m1); - var t = (m - m0) / (m1 - m0); - coordinate = []; - var i; - for (i = 0; i < stride - 1; ++i) { - coordinate.push(goog.math.lerp(flatCoordinates[(lo - 1) * stride + i], - flatCoordinates[lo * stride + i], t)); - } - coordinate.push(m); - goog.asserts.assert(coordinate.length == stride); - return coordinate; -}; - - -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.} ends Ends. - * @param {number} stride Stride. - * @param {number} m M. - * @param {boolean} extrapolate Extrapolate. - * @param {boolean} interpolate Interpolate. - * @return {ol.Coordinate} Coordinate. - */ -ol.geom.flat.lineStringsCoordinateAtM = function( - flatCoordinates, offset, ends, stride, m, extrapolate, interpolate) { - if (interpolate) { - return ol.geom.flat.lineStringCoordinateAtM( - flatCoordinates, offset, ends[ends.length - 1], stride, m, extrapolate); - } - var coordinate; - if (m < flatCoordinates[stride - 1]) { - if (extrapolate) { - coordinate = flatCoordinates.slice(0, stride); - coordinate[stride - 1] = m; - return coordinate; - } else { - return null; - } - } - if (flatCoordinates[flatCoordinates.length - 1] < m) { - if (extrapolate) { - coordinate = flatCoordinates.slice(flatCoordinates.length - stride); - coordinate[stride - 1] = m; - return coordinate; - } else { - return null; - } - } - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - if (offset == end) { - continue; - } - if (m < flatCoordinates[offset + stride - 1]) { - return null; - } else if (m <= flatCoordinates[end - 1]) { - return ol.geom.flat.lineStringCoordinateAtM( - flatCoordinates, offset, end, stride, m, false); - } - offset = end; - } - goog.asserts.fail(); - return null; -}; - - /** * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 15ebb69cc8..71197f755c 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -7,6 +7,7 @@ goog.require('ol.geom.GeometryType'); goog.require('ol.geom.SimpleGeometry'); goog.require('ol.geom.flat'); goog.require('ol.geom.flat.closest'); +goog.require('ol.geom.flat.interpolate'); goog.require('ol.geom.flat.simplify'); @@ -145,7 +146,7 @@ ol.geom.LineString.prototype.getLength = function() { */ ol.geom.LineString.prototype.getFlatMidpoint = function() { if (this.flatMidpointRevision_ != this.getRevision()) { - this.flatMidpoint_ = ol.geom.flat.lineStringInterpolate( + this.flatMidpoint_ = ol.geom.flat.interpolate.lineString( this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0.5, this.flatMidpoint_); this.flatMidpointRevision_ = this.getRevision(); diff --git a/src/ol/geom/multilinestring.js b/src/ol/geom/multilinestring.js index 8581fde85b..781ccfb3fe 100644 --- a/src/ol/geom/multilinestring.js +++ b/src/ol/geom/multilinestring.js @@ -8,6 +8,7 @@ goog.require('ol.geom.LineString'); goog.require('ol.geom.SimpleGeometry'); goog.require('ol.geom.flat'); goog.require('ol.geom.flat.closest'); +goog.require('ol.geom.flat.interpolate'); goog.require('ol.geom.flat.simplify'); @@ -198,7 +199,7 @@ ol.geom.MultiLineString.prototype.getFlatMidpoints = function() { var i, ii; for (i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; - var midpoint = ol.geom.flat.lineStringInterpolate( + var midpoint = ol.geom.flat.interpolate.lineString( flatCoordinates, offset, end, stride, 0.5); goog.array.extend(midpoints, midpoint); offset = end; diff --git a/test/spec/ol/geom/flat/interpolateflatgeom.test.js b/test/spec/ol/geom/flat/interpolateflatgeom.test.js new file mode 100644 index 0000000000..3d05b52214 --- /dev/null +++ b/test/spec/ol/geom/flat/interpolateflatgeom.test.js @@ -0,0 +1,50 @@ +goog.provide('ol.test.geom.flat.interpolate'); + +describe('ol.geom.flat.interpolate', function() { + + describe('ol.geom.flat.interpolate.lineString', function() { + + it('returns the expected value for single points', function() { + var flatCoordinates = [0, 1]; + var point = + ol.geom.flat.interpolate.lineString(flatCoordinates, 0, 2, 2, 0.5); + expect(point).to.eql([0, 1]); + }); + + it('returns the expected value for simple line segments', function() { + var flatCoordinates = [0, 1, 2, 3]; + var point = + ol.geom.flat.interpolate.lineString(flatCoordinates, 0, 4, 2, 0.5); + expect(point).to.eql([1, 2]); + }); + + it('returns the expected value when the mid point is an existing ' + + 'coordinate', function() { + var flatCoordinates = [0, 1, 2, 3, 4, 5]; + var point = ol.geom.flat.interpolate.lineString( + flatCoordinates, 0, 6, 2, 0.5); + expect(point).to.eql([2, 3]); + }); + + it('returns the expected value when the midpoint falls halfway between ' + + 'two existing coordinates', function() { + var flatCoordinates = [0, 1, 2, 3, 4, 5, 6, 7]; + var point = ol.geom.flat.interpolate.lineString( + flatCoordinates, 0, 8, 2, 0.5); + expect(point).to.eql([3, 4]); + }); + + it('returns the expected value when the coordinates are not evenly spaced', + function() { + var flatCoordinates = [0, 1, 2, 3, 6, 7]; + var point = ol.geom.flat.interpolate.lineString( + flatCoordinates, 0, 6, 2, 0.5); + expect(point).to.eql([3, 4]); + }); + + }); + +}); + +goog.require('ol.geom.flat'); +goog.require('ol.geom.flat.interpolate'); diff --git a/test/spec/ol/geom/flatgeom.test.js b/test/spec/ol/geom/flatgeom.test.js index dbbf55bf90..75328a9660 100644 --- a/test/spec/ol/geom/flatgeom.test.js +++ b/test/spec/ol/geom/flatgeom.test.js @@ -84,49 +84,6 @@ describe('ol.geom.flat', function() { }); }); - - describe('ol.geom.flat.lineStringInterpolate', function() { - - it('returns the expected value for single points', function() { - var flatCoordinates = [0, 1]; - var point = - ol.geom.flat.lineStringInterpolate(flatCoordinates, 0, 2, 2, 0.5); - expect(point).to.eql([0, 1]); - }); - - it('returns the expected value for simple line segments', function() { - var flatCoordinates = [0, 1, 2, 3]; - var point = - ol.geom.flat.lineStringInterpolate(flatCoordinates, 0, 4, 2, 0.5); - expect(point).to.eql([1, 2]); - }); - - it('returns the expected value when the mid point is an existing ' + - 'coordinate', function() { - var flatCoordinates = [0, 1, 2, 3, 4, 5]; - var point = - ol.geom.flat.lineStringInterpolate(flatCoordinates, 0, 6, 2, 0.5); - expect(point).to.eql([2, 3]); - }); - - it('returns the expected value when the midpoint falls halfway between ' + - 'two existing coordinates', function() { - var flatCoordinates = [0, 1, 2, 3, 4, 5, 6, 7]; - var point = - ol.geom.flat.lineStringInterpolate(flatCoordinates, 0, 8, 2, 0.5); - expect(point).to.eql([3, 4]); - }); - - it('returns the expected value when the coordinates are not evenly spaced', - function() { - var flatCoordinates = [0, 1, 2, 3, 6, 7]; - var point = - ol.geom.flat.lineStringInterpolate(flatCoordinates, 0, 6, 2, 0.5); - expect(point).to.eql([3, 4]); - }); - - }); - describe('ol.geom.flat.linearRingArea', function() { it('calcaultes the area of a triangle', function() {