/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license. * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt * for the full text of the license. */ /* * TBD: * - snap to fixed grid with pixel or geographic size */ /** * @requires OpenLayers/Handler.js * * Class: OpenLayers.Handler.Snapping */ OpenLayers.Handler.Snapping = OpenLayers.Class(OpenLayers.Handler, { /** * Property: methods * {Array(String)} the snapping method */ methods: ['vertex', 'edge'], /** * Property: layers * {Array(} the layers where are the features to snap to */ layers: [], /** * Property: selfLayer * {} */ selfLayer: null, /** * Property: tolerance en pixel unit * {Integer} */ tolerancePix: 10, /** * Property: snapToSelf * {Boolean} */ snapToSelf: false, initialize: function(control, callbacks, options) { OpenLayers.Handler.prototype.initialize.apply(this, arguments); }, /* * Parameters: * lonlat - {} * * Return: * {} */ getPoint: function(lonlat) { var result = null; for (var i = 0; i < this.methods.length; i++) { switch (this.methods[i]) { case 'vertex': result = this.snapToVertex(lonlat); break; case 'edge': result = this.snapToEdges(lonlat); break; } if (result) { this.method = this.methods[i]; break; } else { this.method = ''; } } return result ? result : lonlat; }, getPointStyle: function() { return OpenLayers.Handler.Snapping.style[this.method]; }, /* * Parameters: * lonlat - {} * * Return: * {} */ snapToVertex: function(lonlat) { this.getToleranceGeo(); var vertices = []; if (this.snapToSelf && this.selfLayer) { for (var i = 0; i < this.selfLayer.features.length; i++) { var selfVertices = this.selfLayer.features[i].getVertices(); vertices = vertices.concat(selfVertices.slice(0, selfVertices.length - 1)); } } for (var i = 0; i < this.layers.length; i++) { for (var j = 0; j < this.layers[i].features.length; j++) { if (this.layers[i].features[j].atPoint(lonlat, this.toleranceGeo, this.toleranceGeo)) { vertices = vertices.concat(this.layers[i].features[j].getVertices()); } } } for (var i = 0; i < vertices.length; i++) { if (vertices[i].atPoint(lonlat, this.toleranceGeo, this.toleranceGeo)) { return new OpenLayers.LonLat(vertices[i].x, vertices[i].y); } } // no snapping point found return null; }, snapToEdges: function(lonlat) { this.getToleranceGeo(); var edges = []; if (this.snapToSelf && this.selfLayer) { for (var i = 0; i < this.selfLayer.features.length; i++) { var selfEdges = this.selfLayer.features[i].getEdges(); edges = edges.concat(selfEdges.slice(0, selfEdges.length - 2)); } } for (var i = 0; i < this.layers.length; i++) { for (var j = 0; j < this.layers[i].features.length; j++) { if (this.layers[i].features[j].atPoint(lonlat, this.toleranceGeo, this.toleranceGeo)) { edges = edges.concat(this.layers[i].features[j].getEdges()); } } } for (var i = 0; i < edges.length; i++) { var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat); var result = this.distanceToLine(point, edges[i]); if (result && result.distance < this.toleranceGeo) { return new OpenLayers.LonLat(result.cross.x, result.cross.y); } } // no snapping point found return null; }, // see: http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/source.c /* * return the minimum distance (geo unit) between 'point' and 'line' */ distanceToLine: function(point, line) { var start = line.components[0]; var stop = line.components[1]; var lineSize = start.distanceTo(stop); var U = (((point.x - start.x) * (stop.x - start.x)) + ((point.y - start.y) * (stop.y - start.y))) / (lineSize * lineSize); if (U < 0.0 || U > 1.0) { // closest point does not fall within the line segment return null; } else { var x = start.x + U * (stop.x - start.x); var y = start.y + U * (stop.y - start.y); var cross = new OpenLayers.Geometry.Point(x, y); return {distance: point.distanceTo(cross), cross: cross} } }, /* * Return: * {} */ getToleranceGeo: function() { var resolution = this.map.getResolution(); if (!this.toleranceGeo || resolution != this.resolution) { this.resolution = resolution; this.toleranceGeo = this.tolerancePix * this.resolution; } return this.toleranceGeo; } }); OpenLayers.Handler.Snapping.style = { 'vertex' : { fillColor: "#0000ff", fillOpacity: 0.4, hoverFillColor: "white", hoverFillOpacity: 0.8, strokeColor: "#0000ff", strokeOpacity: 1, strokeWidth: 1, strokeLinecap: "round", hoverStrokeColor: "red", hoverStrokeOpacity: 1, hoverStrokeWidth: 0.2, pointRadius: 6, hoverPointRadius: 1, hoverPointUnit: "%", pointerEvents: "visiblePainted" }, 'edge' : { fillColor: "#ff0000", fillOpacity: 0.4, hoverFillColor: "white", hoverFillOpacity: 0.8, strokeColor: "#ff0000", strokeOpacity: 1, strokeWidth: 1, strokeLinecap: "round", hoverStrokeColor: "red", hoverStrokeOpacity: 1, hoverStrokeWidth: 0.2, pointRadius: 6, hoverPointRadius: 1, hoverPointUnit: "%", pointerEvents: "visiblePainted" } }