summaryrefslogtreecommitdiff
path: root/misc/openlayers/lib/OpenLayers/Handler/Path.js
diff options
context:
space:
mode:
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Handler/Path.js')
-rw-r--r--misc/openlayers/lib/OpenLayers/Handler/Path.js543
1 files changed, 543 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Handler/Path.js b/misc/openlayers/lib/OpenLayers/Handler/Path.js
new file mode 100644
index 0000000..28512a1
--- /dev/null
+++ b/misc/openlayers/lib/OpenLayers/Handler/Path.js
@@ -0,0 +1,543 @@
+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
+ * full list of contributors). Published under the 2-clause BSD license.
+ * See license.txt in the OpenLayers distribution or repository for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Handler/Point.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Path
+ * Handler to draw a path on the map. Path is displayed on mouse down,
+ * moves on mouse move, and is finished on mouse up.
+ *
+ * Inherits from:
+ * - <OpenLayers.Handler.Point>
+ */
+OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {
+
+ /**
+ * Property: line
+ * {<OpenLayers.Feature.Vector>}
+ */
+ line: null,
+
+ /**
+ * APIProperty: maxVertices
+ * {Number} The maximum number of vertices which can be drawn by this
+ * handler. When the number of vertices reaches maxVertices, the
+ * geometry is automatically finalized. Default is null.
+ */
+ maxVertices: null,
+
+ /**
+ * Property: doubleTouchTolerance
+ * {Number} Maximum number of pixels between two touches for
+ * the gesture to be considered a "finalize feature" action.
+ * Default is 20.
+ */
+ doubleTouchTolerance: 20,
+
+ /**
+ * Property: freehand
+ * {Boolean} In freehand mode, the handler starts the path on mouse down,
+ * adds a point for every mouse move, and finishes the path on mouse up.
+ * Outside of freehand mode, a point is added to the path on every mouse
+ * click and double-click finishes the path.
+ */
+ freehand: false,
+
+ /**
+ * Property: freehandToggle
+ * {String} If set, freehandToggle is checked on mouse events and will set
+ * the freehand mode to the opposite of this.freehand. To disallow
+ * toggling between freehand and non-freehand mode, set freehandToggle to
+ * null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and 'altKey'.
+ */
+ freehandToggle: 'shiftKey',
+
+ /**
+ * Property: timerId
+ * {Integer} The timer used to test the double touch.
+ */
+ timerId: null,
+
+ /**
+ * Property: redoStack
+ * {Array} Stack containing points removed with <undo>.
+ */
+ redoStack: null,
+
+ /**
+ * Constructor: OpenLayers.Handler.Path
+ * Create a new path hander
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>} The control that owns this handler
+ * callbacks - {Object} An object with a properties whose values are
+ * functions. Various callbacks described below.
+ * options - {Object} An optional object with properties to be set on the
+ * handler
+ *
+ * Named callbacks:
+ * create - Called when a sketch is first created. Callback called with
+ * the creation point geometry and sketch feature.
+ * modify - Called with each move of a vertex with the vertex (point)
+ * geometry and the sketch feature.
+ * point - Called as each point is added. Receives the new point geometry.
+ * done - Called when the point drawing is finished. The callback will
+ * recieve a single argument, the linestring geometry.
+ * cancel - Called when the handler is deactivated while drawing. The
+ * cancel callback will receive a geometry.
+ */
+
+ /**
+ * Method: createFeature
+ * Add temporary geometries
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
+ * feature.
+ */
+ createFeature: function(pixel) {
+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
+ var geometry = new OpenLayers.Geometry.Point(
+ lonlat.lon, lonlat.lat
+ );
+ this.point = new OpenLayers.Feature.Vector(geometry);
+ this.line = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString([this.point.geometry])
+ );
+ this.callback("create", [this.point.geometry, this.getSketch()]);
+ this.point.geometry.clearBounds();
+ this.layer.addFeatures([this.line, this.point], {silent: true});
+ },
+
+ /**
+ * Method: destroyFeature
+ * Destroy temporary geometries
+ *
+ * Parameters:
+ * force - {Boolean} Destroy even if persist is true.
+ */
+ destroyFeature: function(force) {
+ OpenLayers.Handler.Point.prototype.destroyFeature.call(
+ this, force);
+ this.line = null;
+ },
+
+ /**
+ * Method: destroyPersistedFeature
+ * Destroy the persisted feature.
+ */
+ destroyPersistedFeature: function() {
+ var layer = this.layer;
+ if(layer && layer.features.length > 2) {
+ this.layer.features[0].destroy();
+ }
+ },
+
+ /**
+ * Method: removePoint
+ * Destroy the temporary point.
+ */
+ removePoint: function() {
+ if(this.point) {
+ this.layer.removeFeatures([this.point]);
+ }
+ },
+
+ /**
+ * Method: addPoint
+ * Add point to geometry. Send the point index to override
+ * the behavior of LinearRing that disregards adding duplicate points.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} The pixel location for the new point.
+ */
+ addPoint: function(pixel) {
+ this.layer.removeFeatures([this.point]);
+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
+ this.point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
+ );
+ this.line.geometry.addComponent(
+ this.point.geometry, this.line.geometry.components.length
+ );
+ this.layer.addFeatures([this.point]);
+ this.callback("point", [this.point.geometry, this.getGeometry()]);
+ this.callback("modify", [this.point.geometry, this.getSketch()]);
+ this.drawFeature();
+ delete this.redoStack;
+ },
+
+ /**
+ * Method: insertXY
+ * Insert a point in the current sketch given x & y coordinates. The new
+ * point is inserted immediately before the most recently drawn point.
+ *
+ * Parameters:
+ * x - {Number} The x-coordinate of the point.
+ * y - {Number} The y-coordinate of the point.
+ */
+ insertXY: function(x, y) {
+ this.line.geometry.addComponent(
+ new OpenLayers.Geometry.Point(x, y),
+ this.getCurrentPointIndex()
+ );
+ this.drawFeature();
+ delete this.redoStack;
+ },
+
+ /**
+ * Method: insertDeltaXY
+ * Insert a point given offsets from the previously inserted point.
+ *
+ * Parameters:
+ * dx - {Number} The x-coordinate offset of the point.
+ * dy - {Number} The y-coordinate offset of the point.
+ */
+ insertDeltaXY: function(dx, dy) {
+ var previousIndex = this.getCurrentPointIndex() - 1;
+ var p0 = this.line.geometry.components[previousIndex];
+ if (p0 && !isNaN(p0.x) && !isNaN(p0.y)) {
+ this.insertXY(p0.x + dx, p0.y + dy);
+ }
+ },
+
+ /**
+ * Method: insertDirectionLength
+ * Insert a point in the current sketch given a direction and a length.
+ *
+ * Parameters:
+ * direction - {Number} Degrees clockwise from the positive x-axis.
+ * length - {Number} Distance from the previously drawn point.
+ */
+ insertDirectionLength: function(direction, length) {
+ direction *= Math.PI / 180;
+ var dx = length * Math.cos(direction);
+ var dy = length * Math.sin(direction);
+ this.insertDeltaXY(dx, dy);
+ },
+
+ /**
+ * Method: insertDeflectionLength
+ * Insert a point in the current sketch given a deflection and a length.
+ * The deflection should be degrees clockwise from the previously
+ * digitized segment.
+ *
+ * Parameters:
+ * deflection - {Number} Degrees clockwise from the previous segment.
+ * length - {Number} Distance from the previously drawn point.
+ */
+ insertDeflectionLength: function(deflection, length) {
+ var previousIndex = this.getCurrentPointIndex() - 1;
+ if (previousIndex > 0) {
+ var p1 = this.line.geometry.components[previousIndex];
+ var p0 = this.line.geometry.components[previousIndex-1];
+ var theta = Math.atan2(p1.y - p0.y, p1.x - p0.x);
+ this.insertDirectionLength(
+ (theta * 180 / Math.PI) + deflection, length
+ );
+ }
+ },
+
+ /**
+ * Method: getCurrentPointIndex
+ *
+ * Returns:
+ * {Number} The index of the most recently drawn point.
+ */
+ getCurrentPointIndex: function() {
+ return this.line.geometry.components.length - 1;
+ },
+
+
+ /**
+ * Method: undo
+ * Remove the most recently added point in the sketch geometry.
+ *
+ * Returns:
+ * {Boolean} A point was removed.
+ */
+ undo: function() {
+ var geometry = this.line.geometry;
+ var components = geometry.components;
+ var index = this.getCurrentPointIndex() - 1;
+ var target = components[index];
+ var undone = geometry.removeComponent(target);
+ if (undone) {
+ // On touch devices, set the current ("mouse location") point to
+ // match the last digitized point.
+ if (this.touch && index > 0) {
+ components = geometry.components; // safety
+ var lastpt = components[index - 1];
+ var curptidx = this.getCurrentPointIndex();
+ var curpt = components[curptidx];
+ curpt.x = lastpt.x;
+ curpt.y = lastpt.y;
+ }
+ if (!this.redoStack) {
+ this.redoStack = [];
+ }
+ this.redoStack.push(target);
+ this.drawFeature();
+ }
+ return undone;
+ },
+
+ /**
+ * Method: redo
+ * Reinsert the most recently removed point resulting from an <undo> call.
+ * The undo stack is deleted whenever a point is added by other means.
+ *
+ * Returns:
+ * {Boolean} A point was added.
+ */
+ redo: function() {
+ var target = this.redoStack && this.redoStack.pop();
+ if (target) {
+ this.line.geometry.addComponent(target, this.getCurrentPointIndex());
+ this.drawFeature();
+ }
+ return !!target;
+ },
+
+ /**
+ * Method: freehandMode
+ * Determine whether to behave in freehand mode or not.
+ *
+ * Returns:
+ * {Boolean}
+ */
+ freehandMode: function(evt) {
+ return (this.freehandToggle && evt[this.freehandToggle]) ?
+ !this.freehand : this.freehand;
+ },
+
+ /**
+ * Method: modifyFeature
+ * Modify the existing geometry given the new point
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} The updated pixel location for the latest
+ * point.
+ * drawing - {Boolean} Indicate if we're currently drawing.
+ */
+ modifyFeature: function(pixel, drawing) {
+ if(!this.line) {
+ this.createFeature(pixel);
+ }
+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
+ this.point.geometry.x = lonlat.lon;
+ this.point.geometry.y = lonlat.lat;
+ this.callback("modify", [this.point.geometry, this.getSketch(), drawing]);
+ this.point.geometry.clearBounds();
+ this.drawFeature();
+ },
+
+ /**
+ * Method: drawFeature
+ * Render geometries on the temporary layer.
+ */
+ drawFeature: function() {
+ this.layer.drawFeature(this.line, this.style);
+ this.layer.drawFeature(this.point, this.style);
+ },
+
+ /**
+ * Method: getSketch
+ * Return the sketch feature.
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>}
+ */
+ getSketch: function() {
+ return this.line;
+ },
+
+ /**
+ * Method: getGeometry
+ * Return the sketch geometry. If <multi> is true, this will return
+ * a multi-part geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.LineString>}
+ */
+ getGeometry: function() {
+ var geometry = this.line && this.line.geometry;
+ if(geometry && this.multi) {
+ geometry = new OpenLayers.Geometry.MultiLineString([geometry]);
+ }
+ return geometry;
+ },
+
+ /**
+ * method: touchstart
+ * handle touchstart.
+ *
+ * parameters:
+ * evt - {event} the browser event
+ *
+ * returns:
+ * {boolean} allow event propagation
+ */
+ touchstart: function(evt) {
+ if (this.timerId &&
+ this.passesTolerance(this.lastTouchPx, evt.xy,
+ this.doubleTouchTolerance)) {
+ // double-tap, finalize the geometry
+ this.finishGeometry();
+ window.clearTimeout(this.timerId);
+ this.timerId = null;
+ return false;
+ } else {
+ if (this.timerId) {
+ window.clearTimeout(this.timerId);
+ this.timerId = null;
+ }
+ this.timerId = window.setTimeout(
+ OpenLayers.Function.bind(function() {
+ this.timerId = null;
+ }, this), 300);
+ return OpenLayers.Handler.Point.prototype.touchstart.call(this, evt);
+ }
+ },
+
+ /**
+ * Method: down
+ * Handle mousedown and touchstart. Add a new point to the geometry and
+ * render it. Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ down: function(evt) {
+ var stopDown = this.stopDown;
+ if(this.freehandMode(evt)) {
+ stopDown = true;
+ if (this.touch) {
+ this.modifyFeature(evt.xy, !!this.lastUp);
+ OpenLayers.Event.stop(evt);
+ }
+ }
+ if (!this.touch && (!this.lastDown ||
+ !this.passesTolerance(this.lastDown, evt.xy,
+ this.pixelTolerance))) {
+ this.modifyFeature(evt.xy, !!this.lastUp);
+ }
+ this.mouseDown = true;
+ this.lastDown = evt.xy;
+ this.stoppedDown = stopDown;
+ return !stopDown;
+ },
+
+ /**
+ * Method: move
+ * Handle mousemove and touchmove. Adjust the geometry and redraw.
+ * Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ move: function (evt) {
+ if(this.stoppedDown && this.freehandMode(evt)) {
+ if(this.persist) {
+ this.destroyPersistedFeature();
+ }
+ if(this.maxVertices && this.line &&
+ this.line.geometry.components.length === this.maxVertices) {
+ this.removePoint();
+ this.finalize();
+ } else {
+ this.addPoint(evt.xy);
+ }
+ return false;
+ }
+ if (!this.touch && (!this.mouseDown || this.stoppedDown)) {
+ this.modifyFeature(evt.xy, !!this.lastUp);
+ }
+ return true;
+ },
+
+ /**
+ * Method: up
+ * Handle mouseup and touchend. Send the latest point in the geometry to
+ * the control. Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ up: function (evt) {
+ if (this.mouseDown && (!this.lastUp || !this.lastUp.equals(evt.xy))) {
+ if(this.stoppedDown && this.freehandMode(evt)) {
+ if (this.persist) {
+ this.destroyPersistedFeature();
+ }
+ this.removePoint();
+ this.finalize();
+ } else {
+ if (this.passesTolerance(this.lastDown, evt.xy,
+ this.pixelTolerance)) {
+ if (this.touch) {
+ this.modifyFeature(evt.xy);
+ }
+ if(this.lastUp == null && this.persist) {
+ this.destroyPersistedFeature();
+ }
+ this.addPoint(evt.xy);
+ this.lastUp = evt.xy;
+ if(this.line.geometry.components.length === this.maxVertices + 1) {
+ this.finishGeometry();
+ }
+ }
+ }
+ }
+ this.stoppedDown = this.stopDown;
+ this.mouseDown = false;
+ return !this.stopUp;
+ },
+
+ /**
+ * APIMethod: finishGeometry
+ * Finish the geometry and send it back to the control.
+ */
+ finishGeometry: function() {
+ var index = this.line.geometry.components.length - 1;
+ this.line.geometry.removeComponent(this.line.geometry.components[index]);
+ this.removePoint();
+ this.finalize();
+ },
+
+ /**
+ * Method: dblclick
+ * Handle double-clicks.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ dblclick: function(evt) {
+ if(!this.freehandMode(evt)) {
+ this.finishGeometry();
+ }
+ return false;
+ },
+
+ CLASS_NAME: "OpenLayers.Handler.Path"
+});