Skip to content

Instantly share code, notes, and snippets.

@pgiraud
Last active April 4, 2024 19:09
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pgiraud/6131715 to your computer and use it in GitHub Desktop.
Save pgiraud/6131715 to your computer and use it in GitHub Desktop.
OpenLayers 2 - Direction arrows along linestrings
var map, drawControls;
function init() {
map = new OpenLayers.Map('map');
var wmsLayer = new OpenLayers.Layer.WMS("OpenLayers WMS", "http://vmap0.tiles.osgeo.org/wms/vmap0?", {
layers: 'basic'
});
var vector = new OpenLayers.Layer.Vector("Vector Layer", {
renderers: ['SVGExtended', 'VMLExtended', 'CanvasExtended'],
styleMap: new OpenLayers.StyleMap({
'default': OpenLayers.Util.extend({
orientation: true
}, OpenLayers.Feature.Vector.style['default']),
'temporary': OpenLayers.Util.extend({
orientation: true
}, OpenLayers.Feature.Vector.style['temporary'])
})
});
map.addLayers([wmsLayer, vector]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.addControl(new OpenLayers.Control.MousePosition());
drawControls = {
line: new OpenLayers.Control.DrawFeature(
vector,
OpenLayers.Handler.Path
),
modify: new OpenLayers.Control.ModifyFeature(vector)
};
for (var key in drawControls) {
map.addControl(drawControls[key]);
}
map.setCenter(new OpenLayers.LonLat(0, 0), 3);
document.getElementById('noneToggle').checked = true;
}
function toggleControl(element) {
for (key in drawControls) {
var control = drawControls[key];
if (element.value == key && element.checked) {
control.activate();
} else {
control.deactivate();
}
}
}
/* Copyright Pierre GIRAUD, https://gist.github.com/pgiraud/6131715
* Published under WTFPL license. */
/**
* @requires OpenLayers/Renderer/SVG.js
*/
OpenLayers.Renderer.SVGExtended = OpenLayers.Class(OpenLayers.Renderer.SVG, {
eraseGeometry: function(geometry, featureId) {
this.removeArrows(geometry);
return OpenLayers.Renderer.SVG.prototype.eraseGeometry.apply(this, arguments);
},
drawFeature: function(feature, style) {
if (feature.geometry) {
this.removeArrows(feature.geometry);
}
return OpenLayers.Renderer.SVG.prototype.drawFeature.apply(this, arguments);
},
/**
* Method: drawLineString
* Method which extends parent class by also drawing an arrow in the middle
* of the line to represent it's orientation.
*/
drawLineString: function(node, geometry) {
this.drawArrows(geometry, node._style);
return OpenLayers.Renderer.SVG.prototype.drawLineString.apply(this, arguments);
}
});
/**
* @requires OpenLayers/Renderer/Canvas.js
*/
OpenLayers.Renderer.CanvasExtended = OpenLayers.Class(OpenLayers.Renderer.Canvas, {
eraseGeometry: function(geometry, featureId) {
this.removeArrows(geometry);
return OpenLayers.Renderer.Canvas.prototype.eraseGeometry.apply(this, arguments);
},
drawFeature: function(feature, style) {
if (feature.geometry) {
this.removeArrows(feature.geometry);
}
return OpenLayers.Renderer.Canvas.prototype.drawFeature.apply(this, arguments);
},
/**
* Method: drawLineString
* Method which extends parent class by also drawing an arrow in the middle
* of the line to represent it's orientation.
*/
drawLineString: function(geometry, style) {
this.drawArrows(geometry, style);
return OpenLayers.Renderer.Canvas.prototype.drawLineString.apply(this, arguments);
}
});
/**
* @requires OpenLayers/Renderer/VML.js
*/
OpenLayers.Renderer.VMLExtended = OpenLayers.Class(OpenLayers.Renderer.VML, {
eraseGeometry: function(geometry, featureId) {
this.removeArrows(geometry);
return OpenLayers.Renderer.VML.prototype.eraseGeometry.apply(this, arguments);
},
drawFeature: function(feature, style) {
if (feature.geometry) {
this.removeArrows(feature.geometry);
}
return OpenLayers.Renderer.VML.prototype.drawFeature.apply(this, arguments);
},
/**
* Method: drawLineString
* Method which extends parent class by also drawing an arrow in the middle
* of the line to represent it's orientation.
*/
drawLineString: function(node, geometry) {
this.drawArrows(geometry, node._style);
return OpenLayers.Renderer.VML.prototype.drawLineString.apply(this, arguments);
}
});
OpenLayers.Renderer.prototype.removeArrows = function(geometry) {
var i;
// remove any arrow already drawn
// FIXME may be a performance issue
var children = this.vectorRoot.childNodes,
arrowsToRemove = [];
for (i = 0; i < children.length; i++) {
var child = children[i];
if (child.id.indexOf(geometry.id + "_arrow") != -1) {
arrowsToRemove.push(child);
}
}
for (i = 0; i < arrowsToRemove.length; i++) {
this.vectorRoot.removeChild(arrowsToRemove[i]);
}
};
OpenLayers.Renderer.prototype.drawArrows = function(geometry, style) {
var i;
if (style.orientation) {
var pts = geometry.components;
var prevArrow,
distance;
for (i = 0, len = pts.length; i < len - 1; ++i) {
var prevVertex = pts[i];
var nextVertex = pts[i + 1];
var x = (prevVertex.x + nextVertex.x) / 2;
var y = (prevVertex.y + nextVertex.y) / 2;
var arrow = new OpenLayers.Geometry.Point(x, y);
arrow.id = geometry.id + '_arrow_' + i;
style = OpenLayers.Util.extend({}, style);
style.graphicName = "arrow";
style.pointRadius = 4;
style.rotation = this.getOrientation(prevVertex, nextVertex);
if (prevArrow) {
var pt1 = this.map.getPixelFromLonLat(new OpenLayers.LonLat(arrow.x, arrow.y)),
pt2 = this.map.getPixelFromLonLat(new OpenLayers.LonLat(prevArrow.x, prevArrow.y)),
w = pt2.x - pt1.x,
h = pt2.y - pt1.y;
distance = Math.sqrt(w*w + h*h);
}
// don't draw every arrow, ie. ensure that there is enough space
// between two
if (!prevArrow || distance > 40) {
this.drawGeometry(arrow, style, arrow.id);
prevArrow = arrow;
}
}
}
};
OpenLayers.Renderer.prototype.getOrientation = function(pt1, pt2) {
var x = pt2.x - pt1.x;
var y = pt2.y - pt1.y;
var rad = Math.acos(y / Math.sqrt(x * x + y * y));
// negative or positive
var factor = x > 0 ? 1 : -1;
return Math.round(factor * rad * 180 / Math.PI);
};
OpenLayers.Renderer.symbol.arrow = [0, 2, 1, 0, 2, 2, 1, 0, 0, 2];
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>Direction Arrows Example</title>
<link rel="stylesheet" href="http://openlayers.org/dev/theme/default/style.css" type="text/css">
<link rel="stylesheet" href="http://openlayers.org/dev/examples/style.css" type="text/css">
<style type="text/css">
#controlToggle li {
list-style: none;
}
p {
width: 512px;
}
/* avoid pink tiles */
.olImageLoadError {
background-color: transparent !important;
}
</style>
<script src="http://openlayers.org/dev/OpenLayers.js"></script>
<script type="text/javascript" src="ExtendedRenderers.js"></script>
<script type="text/javascript" src="direction-arrows.js"></script>
</head>
<body onload="init()">
<h1 id="title">OpenLayers Draw Feature Example</h1>
<div id="tags">
point, line, linestring, polygon, box, digitizing, geometry, draw, drag
</div>
<p id="shortdesc">
Demonstrate the rendering of arrows to show line direction. Can be useful for routing or trails.
</p>
<div id="map" class="smallmap"></div>
<ul id="controlToggle">
<li>
<input type="radio" name="type" value="none" id="noneToggle"
onclick="toggleControl(this);" checked="checked" />
<label for="noneToggle">navigate</label>
</li>
<li>
<input type="radio" name="type" value="line" id="lineToggle" onclick="toggleControl(this);" />
<label for="lineToggle">draw line</label>
</li>
<li>
<input type="radio" name="type" value="modify" id="modifyToggle" onclick="toggleControl(this);" />
<label for="modifyToggle">modify</label>
</li>
</ul>
</body>
</html>
Copyright (C) 2013 Pierre GIRAUD <pierre.giraud@camptocamp.com>
This work is free. You can redistribute it and/or modify it under the
terms of the Do What The Fuck You Want To Public License, Version 2,
as published by Sam Hocevar:
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
@r4jin
Copy link

r4jin commented Oct 21, 2013

Beautiful. Thanks!

@theoneno1
Copy link

Thanks man, very nice code.

@malvandi
Copy link

malvandi commented May 7, 2015

Beautiful. Thanks!

But how can I set My Style rule to it, for example strokeColor: '#010101'?

@Lebim01
Copy link

Lebim01 commented Jan 11, 2018

@malvandi

If you are going to overwrite the styles, you need to place the orientation property with any value

var vectorLayer = new OpenLayers.Layer.Vector("Overlay", {
renderers: ['SVGExtended', 'VMLExtended', 'CanvasExtended'],
styleMap: new OpenLayers.StyleMap({
'default': OpenLayers.Util.extend({
orientation: true
}, OpenLayers.Feature.Vector.style['default']),
'temporary': OpenLayers.Util.extend({
orientation: true
}, OpenLayers.Feature.Vector.style['temporary'])
}),
style : {
strokeColor : '#ff0000',
orientation : 'any value'
}
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment