diff --git a/examples/measure.html b/examples/measure.html
new file mode 100644
index 0000000000..94bc4000e1
--- /dev/null
+++ b/examples/measure.html
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+ OpenLayers Measure Example
+
+ Demonstrates the measure control to measure distances and areas.
+
+
+
+
+
diff --git a/lib/OpenLayers/Control/Measure.js b/lib/OpenLayers/Control/Measure.js
new file mode 100644
index 0000000000..0a5be55e5a
--- /dev/null
+++ b/lib/OpenLayers/Control/Measure.js
@@ -0,0 +1,246 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Control.Measure
+ * Allows for drawing of features for measurements.
+ *
+ * Inherits from:
+ * -
+ */
+OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Constant: EVENT_TYPES
+ * {Array(String)} Supported application event types. Register a listener
+ * for a particular event with the following syntax:
+ * (code)
+ * control.events.register(type, obj, listener);
+ * (end)
+ *
+ * Listeners will be called with a reference to an event object. The
+ * properties of this event depends on exactly what happened.
+ *
+ * Supported control event types (in addition to those from ):
+ * - *measure* Triggered when a measurement sketch is complete. Listeners
+ * will receive an event with measure, units, order, and geometry
+ * properties.
+ * - *measurepartial* Triggered when a new point is added to the
+ * measurement sketch. Listeners receive an event with measure,
+ * units, order, and geometry.
+ */
+ EVENT_TYPES: ['measure', 'measurepartial'],
+
+ /**
+ * APIProperty: handlerOptions
+ * {Object} Used to set non-default properties on the control's handler
+ */
+ handlerOptions: null,
+
+ /**
+ * Property: callbacks
+ * {Object} The functions that are sent to the handler for callback
+ */
+ callbacks: null,
+
+ /**
+ * Property: displaySystem
+ * {String} Display system for output measurements. Supported values
+ * are 'english', 'metric', and 'geographic'. Default is 'metric'.
+ */
+ displaySystem: 'metric',
+
+ /**
+ * Property: displaySystemUnits
+ * {Object} Units for various measurement systems. Values are arrays
+ * of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing
+ * order of length.
+ */
+ displaySystemUnits: {
+ geographic: ['dd'],
+ english: ['mi', 'ft', 'in'],
+ metric: ['km', 'm']
+ },
+
+ /**
+ * Constructor: OpenLayers.Control.Measure
+ *
+ * Parameters:
+ * handler - {}
+ * options - {Object}
+ */
+ initialize: function(handler, options) {
+ // concatenate events specific to measure with those from the base
+ this.EVENT_TYPES =
+ OpenLayers.Control.Measure.prototype.EVENT_TYPES.concat(
+ OpenLayers.Control.prototype.EVENT_TYPES
+ );
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.callbacks = OpenLayers.Util.extend(
+ {done: this.measureComplete, point: this.measurePartial},
+ this.callbacks
+ );
+ this.handler = new handler(this, this.callbacks, this.handlerOptions);
+ },
+
+ /**
+ * Method: updateHandler
+ *
+ * Parameters:
+ * handler - {Function} One of the sketch handler constructors.
+ * options - {Object} Options for the handler.
+ */
+ updateHandler: function(handler, options) {
+ var active = this.active;
+ if(active) {
+ this.deactivate();
+ }
+ this.handler = new handler(this, this.callbacks, options);
+ if(active) {
+ this.activate();
+ }
+ },
+
+ /**
+ * Method: measureComplete
+ * Called when the measurement sketch is done.
+ *
+ * Parameters:
+ * geometry - {}
+ */
+ measureComplete: function(geometry) {
+ this.measure(geometry, "measure");
+ },
+
+ /**
+ * Method: measurePartial
+ * Called each time a new point is added to the measurement sketch.
+ *
+ * Parameters:
+ * point - {} The last point added.
+ * geometry - {} The sketch geometry.
+ */
+ measurePartial: function(point, geometry) {
+ this.measure(geometry, "measurepartial");
+ },
+
+ /**
+ * Method: measure
+ *
+ * Parameters:
+ * geometry - {}
+ * eventType - {String}
+ */
+ measure: function(geometry, eventType) {
+ var stat, order;
+ if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
+ stat = this.getBestLength(geometry);
+ order = 1;
+ } else {
+ stat = this.getBestArea(geometry);
+ order = 2;
+ }
+ this.events.triggerEvent(eventType, {
+ measure: stat[0],
+ units: stat[1],
+ order: order,
+ geometry: geometry
+ });
+ },
+
+ /**
+ * Method: getBestArea
+ * Based on the returns the area of a geometry.
+ *
+ * Parameters:
+ * geometry - {}
+ *
+ * Returns:
+ * {Array([Float, String])} Returns a two item array containing the
+ * area and the units abbreviation.
+ */
+ getBestArea: function(geometry) {
+ var units = this.displaySystemUnits[this.displaySystem];
+ var unit, area;
+ for(var i=0, len=units.length; i 1) {
+ break;
+ }
+ }
+ return [area, unit];
+ },
+
+ /**
+ * Method: getArea
+ *
+ * Parameters:
+ * geometry - {}
+ * units - {String} Unit abbreviation
+ *
+ * Returns:
+ * {Float} The geometry area in the given units.
+ */
+ getArea: function(geometry, units) {
+ var area = geometry.getArea();
+ var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
+ if(inPerDisplayUnit) {
+ var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[this.map.getUnits()];
+ area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
+ }
+ return area;
+ },
+
+ /**
+ * Method: getBestLength
+ * Based on the returns the length of a geometry.
+ *
+ * Parameters:
+ * geometry - {}
+ *
+ * Returns:
+ * {Array([Float, String])} Returns a two item array containing the
+ * length and the units abbreviation.
+ */
+ getBestLength: function(geometry) {
+ var units = this.displaySystemUnits[this.displaySystem];
+ var unit, length;
+ for(var i=0, len=units.length; i 1) {
+ break;
+ }
+ }
+ return [length, unit];
+ },
+
+ /**
+ * Method: getLength
+ *
+ * Parameters:
+ * geometry - {}
+ * units - {String} Unit abbreviation
+ *
+ * Returns:
+ * {Float} The geometry length in the given units.
+ */
+ getLength: function(geometry, units) {
+ var length = geometry.getLength();
+ var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
+ if(inPerDisplayUnit) {
+ var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[this.map.getUnits()];
+ length *= (inPerMapUnit / inPerDisplayUnit);
+ }
+ return length;
+ },
+
+ CLASS_NAME: "OpenLayers.Control.Measure"
+});