From 6781a6cef33dac51b68638aedc9ffe9eb0302db6 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 29 Jan 2014 01:22:11 +0100 Subject: [PATCH 1/5] Add ol.geom.flat.lineStringCoordinateAtM --- src/ol/geom/flatgeom.js | 59 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/ol/geom/flatgeom.js b/src/ol/geom/flatgeom.js index 9e35919857..c8e8ed03a4 100644 --- a/src/ol/geom/flatgeom.js +++ b/src/ol/geom/flatgeom.js @@ -305,6 +305,65 @@ ol.geom.flat.lineStringInterpolate = }; +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {number} m M. + * @param {boolean} extrapolate Extrapolate. + * @return {ol.Coordinate} Coordinate. + */ +ol.geom.flat.lineStringCoordinateAtM = + function(flatCoordinates, offset, end, stride, layout, m, extrapolate) { + if ((layout != ol.geom.GeometryLayout.XYM && + layout != ol.geom.GeometryLayout.XYZM) || + 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 + for (offset += stride; offset < end; offset += stride) { + var m1 = flatCoordinates[offset + stride - 1]; + if (m < m1) { + var m0 = flatCoordinates[offset - 1]; + var t = (m - m0) / (m1 - m0); + coordinate = []; + var i; + for (i = 0; i < stride - 1; ++i) { + coordinate.push((1 - t) * flatCoordinates[offset - stride + i] + + t * flatCoordinates[offset + i]); + } + coordinate.push(m); + goog.asserts.assert(coordinate.length == stride); + return coordinate; + } else if (m == m1) { + return flatCoordinates.slice(offset, offset + stride); + } + } + goog.asserts.fail(); + return null; +}; + + /** * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. From c218d5d4e885d498836c8a123e239ffc68786c60 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 29 Jan 2014 01:22:33 +0100 Subject: [PATCH 2/5] Add ol.geom.LineString#getCoordinateAtM --- src/ol/geom/linestring.exports | 1 + src/ol/geom/linestring.js | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/ol/geom/linestring.exports b/src/ol/geom/linestring.exports index a258e088be..f5c50bdf5c 100644 --- a/src/ol/geom/linestring.exports +++ b/src/ol/geom/linestring.exports @@ -1,5 +1,6 @@ @exportSymbol ol.geom.LineString @exportProperty ol.geom.LineString.prototype.clone +@exportProperty ol.geom.LineString.prototype.getCoordinateAtM @exportProperty ol.geom.LineString.prototype.getCoordinates @exportProperty ol.geom.LineString.prototype.getLength @exportProperty ol.geom.LineString.prototype.getType diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 8b8cfedc29..d97a316f6a 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -80,6 +80,18 @@ ol.geom.LineString.prototype.closestPointXY = }; +/** + * @param {number} m M. + * @param {boolean=} opt_extrapolate Extrapolate. + * @return {ol.Coordinate} Coordinate. + */ +ol.geom.LineString.prototype.getCoordinateAtM = function(m, opt_extrapolate) { + var extrapolate = goog.isDef(opt_extrapolate) ? opt_extrapolate : false; + return ol.geom.flat.lineStringCoordinateAtM(this.flatCoordinates, 0, + this.flatCoordinates.length, this.stride, this.layout, m, extrapolate); +}; + + /** * @return {ol.geom.RawLineString} Coordinates. * @todo stability experimental From 1c21316d2063a00b0159e4ed50108045969d31d6 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 25 Feb 2014 15:52:02 +0100 Subject: [PATCH 3/5] Add tests for ol.geom.LineString#getCoordinateAtM --- test/spec/ol/geom/linestring.test.js | 63 ++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/test/spec/ol/geom/linestring.test.js b/test/spec/ol/geom/linestring.test.js index 0cf269c399..a09ee352be 100644 --- a/test/spec/ol/geom/linestring.test.js +++ b/test/spec/ol/geom/linestring.test.js @@ -228,6 +228,69 @@ describe('ol.geom.LineString', function() { }); + describe('with a simple XYM coordinates', function() { + + var lineString; + beforeEach(function() { + lineString = new ol.geom.LineString( + [[1, 2, 3], [4, 5, 6]], ol.geom.GeometryLayout.XYM); + }); + + describe('#getCoordinateAtM', function() { + + it('returns the expected value', function() { + expect(lineString.getCoordinateAtM(2, false)).to.be(null); + expect(lineString.getCoordinateAtM(2, true)).to.eql([1, 2, 2]); + expect(lineString.getCoordinateAtM(3, false)).to.eql([1, 2, 3]); + expect(lineString.getCoordinateAtM(3, true)).to.eql([1, 2, 3]); + expect(lineString.getCoordinateAtM(4, false)).to.eql([2, 3, 4]); + expect(lineString.getCoordinateAtM(4, true)).to.eql([2, 3, 4]); + expect(lineString.getCoordinateAtM(5, false)).to.eql([3, 4, 5]); + expect(lineString.getCoordinateAtM(5, true)).to.eql([3, 4, 5]); + expect(lineString.getCoordinateAtM(6, false)).to.eql([4, 5, 6]); + expect(lineString.getCoordinateAtM(6, true)).to.eql([4, 5, 6]); + expect(lineString.getCoordinateAtM(7, false)).to.eql(null); + expect(lineString.getCoordinateAtM(7, true)).to.eql([4, 5, 7]); + }); + + }); + + }); + + describe('with several XYZM coordinates', function() { + + var lineString; + beforeEach(function() { + lineString = new ol.geom.LineString([ + [0, 0, 0, 0], + [1, -1, 2, 1], + [2, -2, 4, 2], + [4, -4, 8, 4], + [8, -8, 16, 8], + [12, -12, 24, 12], + [14, -14, 28, 14], + [15, -15, 30, 15], + [16, -16, 32, 16], + [18, -18, 36, 18], + [22, -22, 44, 22] + ]); + }); + + describe('#getCoordinateAtM', function() { + + it('returns the expected value', function() { + expect(lineString.getLayout()).to.be(ol.geom.GeometryLayout.XYZM); + var m; + for (m = 0; m <= 22; m += 0.5) { + expect(lineString.getCoordinateAtM(m, true)).to.eql( + [m, -m, 2 * m, m]); + } + }); + + }); + + }); + }); From c0f9b1de19fbaa1ffae45faf46ec90904bdfc52f Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 25 Feb 2014 19:13:38 +0100 Subject: [PATCH 4/5] Use binary search in ol.geom.flat.lineStringCoordinateAtM --- src/ol/geom/flatgeom.js | 46 +++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/ol/geom/flatgeom.js b/src/ol/geom/flatgeom.js index c8e8ed03a4..5915684be4 100644 --- a/src/ol/geom/flatgeom.js +++ b/src/ol/geom/flatgeom.js @@ -341,26 +341,36 @@ ol.geom.flat.lineStringCoordinateAtM = } } // FIXME use O(1) search - for (offset += stride; offset < end; offset += stride) { - var m1 = flatCoordinates[offset + stride - 1]; - if (m < m1) { - var m0 = flatCoordinates[offset - 1]; - var t = (m - m0) / (m1 - m0); - coordinate = []; - var i; - for (i = 0; i < stride - 1; ++i) { - coordinate.push((1 - t) * flatCoordinates[offset - stride + i] + - t * flatCoordinates[offset + i]); - } - coordinate.push(m); - goog.asserts.assert(coordinate.length == stride); - return coordinate; - } else if (m == m1) { - return flatCoordinates.slice(offset, offset + stride); + 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; } } - goog.asserts.fail(); - return null; + 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((1 - t) * flatCoordinates[(lo - 1) * stride + i] + + t * flatCoordinates[lo * stride + i]); + } + coordinate.push(m); + goog.asserts.assert(coordinate.length == stride); + return coordinate; }; From bca9512478342ceec5bdbf94337c7b886f3d2418 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Fri, 28 Feb 2014 10:25:19 +0100 Subject: [PATCH 5/5] Use ol.geom.LineString#getCoordinateAtM in igc example --- examples/igc.html | 1 + examples/igc.js | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/examples/igc.html b/examples/igc.html index 10509271ba..5fd0925a10 100644 --- a/examples/igc.html +++ b/examples/igc.html @@ -36,6 +36,7 @@

See the igc.js source to see how this is done.

+
complex-geometry, closest-feature, igc, opencyclemap
diff --git a/examples/igc.js b/examples/igc.js index 7787c7ad73..ac86418471 100644 --- a/examples/igc.js +++ b/examples/igc.js @@ -1,4 +1,6 @@ goog.require('ol.Attribution'); +goog.require('ol.Feature'); +goog.require('ol.FeatureOverlay'); goog.require('ol.Map'); goog.require('ol.View2D'); goog.require('ol.geom.LineString'); @@ -8,6 +10,7 @@ goog.require('ol.layer.Vector'); goog.require('ol.source.IGC'); goog.require('ol.source.OSM'); goog.require('ol.style.Circle'); +goog.require('ol.style.Fill'); goog.require('ol.style.Stroke'); goog.require('ol.style.Style'); @@ -47,6 +50,19 @@ var vectorSource = new ol.source.IGC({ ] }); +var time = { + start: Infinity, + stop: -Infinity, + duration: 0 +}; +vectorSource.on('addfeature', function(event) { + var geometry = event.feature.getGeometry(); + time.start = Math.min(time.start, geometry.getFirstCoordinate()[2]); + time.stop = Math.max(time.stop, geometry.getLastCoordinate()[2]); + time.duration = time.stop - time.start; +}); + + var map = new ol.Map({ layers: [ new ol.layer.Tile({ @@ -137,3 +153,34 @@ map.on('postcompose', function(evt) { vectorContext.drawLineStringGeometry(line); } }); + +var featureOverlay = new ol.FeatureOverlay({ + map: map, + style: new ol.style.Style({ + image: new ol.style.Circle({ + radius: 5, + fill: new ol.style.Fill({ + color: 'rgba(255,0,0,0.9)' + }), + stroke: null + }) + }) +}); + +$('#time').on('input', function(event) { + var value = parseInt($(this).val(), 10) / 100; + var m = time.start + (time.duration * value); + vectorSource.forEachFeature(function(feature) { + var geometry = /** @type {ol.geom.LineString} */ (feature.getGeometry()); + var coordinate = geometry.getCoordinateAtM(m, true); + var highlight = feature.get('highlight'); + if (highlight == undefined) { + highlight = new ol.Feature(new ol.geom.Point(coordinate)); + feature.set('highlight', highlight); + featureOverlay.addFeature(highlight); + } else { + highlight.getGeometry().setCoordinates(coordinate); + } + }); + map.render(); +});