diff --git a/examples/data/arrow.png b/examples/data/arrow.png
new file mode 100644
index 0000000000..a0d3834586
Binary files /dev/null and b/examples/data/arrow.png differ
diff --git a/examples/line-arrows.html b/examples/line-arrows.html
new file mode 100644
index 0000000000..6338653387
--- /dev/null
+++ b/examples/line-arrows.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+ LineString arrows example
+
+
+
+
+
+
+
+
+
+
+
+
+
LineString arrows example
+
Example of drawing arrows for each line string segment.
+
+
draw, vector, arrow
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/line-arrows.js b/examples/line-arrows.js
new file mode 100644
index 0000000000..90f810acfa
--- /dev/null
+++ b/examples/line-arrows.js
@@ -0,0 +1,67 @@
+goog.require('ol.Map');
+goog.require('ol.View');
+goog.require('ol.geom.Point');
+goog.require('ol.interaction.Draw');
+goog.require('ol.layer.Tile');
+goog.require('ol.layer.Vector');
+goog.require('ol.source.MapQuest');
+goog.require('ol.source.Vector');
+goog.require('ol.style.Icon');
+goog.require('ol.style.Stroke');
+goog.require('ol.style.Style');
+
+var raster = new ol.layer.Tile({
+ source: new ol.source.MapQuest({layer: 'sat'})
+});
+
+var source = new ol.source.Vector();
+
+var styleFunction = function(feature, resolution) {
+ var geometry = feature.getGeometry();
+ var styles = [
+ // linestring
+ new ol.style.Style({
+ stroke: new ol.style.Stroke({
+ color: '#ffcc33',
+ width: 2
+ })
+ })
+ ];
+
+ geometry.forEachSegment(function(start, end) {
+ var dx = end[0] - start[0];
+ var dy = end[1] - start[1];
+ var rotation = Math.atan2(dy, dx);
+ // arrows
+ styles.push(new ol.style.Style({
+ geometry: new ol.geom.Point(end),
+ image: new ol.style.Icon({
+ src: 'data/arrow.png',
+ anchor: [0.75, 0.5],
+ rotateWithView: false,
+ rotation: -rotation
+ })
+ }));
+ });
+
+ return styles;
+};
+var vector = new ol.layer.Vector({
+ source: source,
+ style: styleFunction
+});
+
+var map = new ol.Map({
+ layers: [raster, vector],
+ renderer: exampleNS.getRendererFromQueryString(),
+ target: 'map',
+ view: new ol.View({
+ center: [-11000000, 4600000],
+ zoom: 4
+ })
+});
+
+map.addInteraction(new ol.interaction.Draw({
+ source: source,
+ type: /** @type {ol.geom.GeometryType} */ ('LineString')
+}));
diff --git a/src/ol/geom/flat/segmentsflatgeom.js b/src/ol/geom/flat/segmentsflatgeom.js
index 8c6aac24fd..b834eecbdc 100644
--- a/src/ol/geom/flat/segmentsflatgeom.js
+++ b/src/ol/geom/flat/segmentsflatgeom.js
@@ -9,20 +9,22 @@ goog.provide('ol.geom.flat.segments');
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
- * @param {function(ol.Coordinate, ol.Coordinate): T} callback Function
+ * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function
* called for each segment.
+ * @param {S=} opt_this The object to be used as the value of 'this'
+ * within callback.
* @return {T|boolean} Value.
- * @template T
+ * @template T,S
*/
ol.geom.flat.segments.forEach =
- function(flatCoordinates, offset, end, stride, callback) {
+ function(flatCoordinates, offset, end, stride, callback, opt_this) {
var point1 = [flatCoordinates[offset], flatCoordinates[offset + 1]];
var point2 = [];
var ret;
for (; (offset + stride) < end; offset += stride) {
point2[0] = flatCoordinates[offset + stride];
point2[1] = flatCoordinates[offset + stride + 1];
- ret = callback(point1, point2);
+ ret = callback.call(opt_this, point1, point2);
if (ret) {
return ret;
}
diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js
index 3477285f3c..08c99fc771 100644
--- a/src/ol/geom/linestring.js
+++ b/src/ol/geom/linestring.js
@@ -11,6 +11,7 @@ goog.require('ol.geom.flat.inflate');
goog.require('ol.geom.flat.interpolate');
goog.require('ol.geom.flat.intersectsextent');
goog.require('ol.geom.flat.length');
+goog.require('ol.geom.flat.segments');
goog.require('ol.geom.flat.simplify');
@@ -107,6 +108,25 @@ ol.geom.LineString.prototype.closestPointXY =
};
+/**
+ * Iterate over each segment, calling the provided callback.
+ * If the callback returns a truthy value the function returns that
+ * value immediately. Otherwise the function returns `false`.
+ *
+ * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function
+ * called for each segment.
+ * @param {S=} opt_this The object to be used as the value of 'this'
+ * within callback.
+ * @return {T|boolean} Value.
+ * @template T,S
+ * @api
+ */
+ol.geom.LineString.prototype.forEachSegment = function(callback, opt_this) {
+ return ol.geom.flat.segments.forEach(this.flatCoordinates, 0,
+ this.flatCoordinates.length, this.stride, callback, opt_this);
+};
+
+
/**
* Returns the coordinate at `m` using linear interpolation, or `null` if no
* such coordinate exists.