Bienvenue sur PostGIS.fr

Bienvenue sur PostGIS.fr , le site de la communauté des utilisateurs francophones de PostGIS.

PostGIS ajoute le support d'objets géographique à la base de données PostgreSQL. En effet, PostGIS "spatialise" le serverur PostgreSQL, ce qui permet de l'utiliser comme une base de données SIG.

Maintenu à jour, en fonction de nos disponibilités et des diverses sorties des outils que nous testons, nous vous proposons l'ensemble de nos travaux publiés en langue française.

source: trunk/workshop-routing-foss4g/web/OpenLayers/lib/OpenLayers/Control/Snapping.js @ 76

Revision 76, 20.3 KB checked in by djay, 12 years ago (diff)

Ajout du répertoire web

  • Property svn:executable set to *
Line 
1/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for
2 * full list of contributors). Published under the Clear BSD license. 
3 * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
4 * full text of the license. */
5
6/**
7 * @requires OpenLayers/Control.js
8 * @requires OpenLayers/Layer/Vector.js
9 */
10
11/**
12 * Class: OpenLayers.Control.Snapping
13 * Acts as a snapping agent while editing vector features.
14 *
15 * Inherits from:
16 *  - <OpenLayers.Control>
17 */
18OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, {
19
20    /**
21     * Constant: EVENT_TYPES
22     * {Array(String)} Supported application event types.  Register a listener
23     *     for a particular event with the following syntax:
24     * (code)
25     * control.events.register(type, obj, listener);
26     * (end)
27     *
28     * Listeners will be called with a reference to an event object.  The
29     *     properties of this event depends on exactly what happened.
30     *
31     * Supported control event types (in addition to those from <OpenLayers.Control>):
32     * beforesnap - Triggered before a snap occurs.  Listeners receive an
33     *     event object with *point*, *x*, *y*, *distance*, *layer*, and
34     *     *snapType* properties.  The point property will be original point
35     *     geometry considered for snapping. The x and y properties represent
36     *     coordinates the point will receive. The distance is the distance
37     *     of the snap.  The layer is the target layer.  The snapType property
38     *     will be one of "node", "vertex", or "edge". Return false to stop
39     *     snapping from occurring.
40     * snap - Triggered when a snap occurs.  Listeners receive an event with
41     *     *point*, *snapType*, *layer*, and *distance* properties.  The point
42     *     will be the location snapped to.  The snapType will be one of "node",
43     *     "vertex", or "edge".  The layer will be the target layer.  The
44     *     distance will be the distance of the snap in map units.
45     * unsnap - Triggered when a vertex is unsnapped.  Listeners receive an
46     *     event with a *point* property.
47     */
48    EVENT_TYPES: ["beforesnap", "snap", "unsnap"],
49   
50    /**
51     * CONSTANT: DEFAULTS
52     * Default target properties.
53     */
54    DEFAULTS: {
55        tolerance: 10,
56        node: true,
57        edge: true,
58        vertex: true
59    },
60   
61    /**
62     * Property: greedy
63     * {Boolean} Snap to closest feature in first layer with an eligible
64     *     feature.  Default is true.
65     */
66    greedy: true,
67   
68    /**
69     * Property: precedence
70     * {Array} List representing precedence of different snapping types.
71     *     Default is "node", "vertex", "edge".
72     */
73    precedence: ["node", "vertex", "edge"],
74   
75    /**
76     * Property: resolution
77     * {Float} The map resolution for the previously considered snap.
78     */
79    resolution: null,
80   
81    /**
82     * Property: geoToleranceCache
83     * {Object} A cache of geo-tolerances.  Tolerance values (in map units) are
84     *     calculated when the map resolution changes.
85     */
86    geoToleranceCache: null,
87   
88    /**
89     * Property: layer
90     * {<OpenLayers.Layer.Vector>} The current editable layer.  Set at
91     *     construction or after construction with <setLayer>.
92     */
93    layer: null,
94   
95    /**
96     * Property: feature
97     * {<OpenLayers.Feature.Vector>} The current editable feature.
98     */
99    feature: null,
100   
101    /**
102     * Property: point
103     * {<OpenLayers.Geometry.Point>} The currently snapped vertex.
104     */
105    point: null,
106
107    /**
108     * Constructor: OpenLayers.Control.Snapping
109     * Creates a new snapping control. A control is constructed with an editable
110     *     layer and a set of configuration objects for target layers. While the
111     *     control is active, dragging vertices while drawing new features or
112     *     modifying existing features on the editable layer will engage
113     *     snapping to features on the target layers. Whether a vertex snaps to
114     *     a feature on a target layer depends on the target layer configuration.
115     *
116     * Parameters:
117     * options - {Object} An object containing all configuration properties for
118     *     the control.
119     *
120     * Valid options:
121     * layer - {OpenLayers.Layer.Vector} The editable layer.  Features from this
122     *     layer that are digitized or modified may have vertices snapped to
123     *     features from any of the target layers.
124     * targets - {Array(Object | OpenLayers.Layer.Vector)} A list of objects for
125     *     configuring target layers.  See valid properties of the target
126     *     objects below.  If the items in the targets list are vector layers
127     *     (instead of configuration objects), the defaults from the <defaults>
128     *     property will apply.  The editable layer itself may be a target
129     *     layer - allowing newly created or edited features to be snapped to
130     *     existing features from the same layer.  If no targets are provided
131     *     the layer given in the constructor (as <layer>) will become the
132     *     initial target.
133     * defaults - {Object} An object with default properties to be applied
134     *     to all target objects.
135     * greedy - {Boolean} Snap to closest feature in first target layer that
136     *     applies.  Default is true.  If false, all features in all target
137     *     layers will be checked and the closest feature in all target layers
138     *     will be chosen.  The greedy property determines if the order of the
139     *     target layers is significant.  By default, the order of the target
140     *     layers is significant where layers earlier in the target layer list
141     *     have precedence over layers later in the list.  Within a single
142     *     layer, the closest feature is always chosen for snapping.  This
143     *     property only determines whether the search for a closer feature
144     *     continues after an eligible feature is found in a target layer.
145     *
146     * Valid target properties:
147     * layer - {OpenLayers.Layer.Vector} A target layer.  Features from this
148     *     layer will be eligible to act as snapping target for the editable
149     *     layer.
150     * tolerance - {Float} The distance (in pixels) at which snapping may occur.
151     *     Default is 10.
152     * node - {Boolean} Snap to nodes (first or last point in a geometry) in
153     *     target layer.  Default is true.
154     * nodeTolerance - {Float} Optional distance at which snapping may occur
155     *     for nodes specifically.  If none is provided, <tolerance> will be
156     *     used.
157     * vertex - {Boolean} Snap to vertices in target layer.  Default is true.
158     * vertexTolerance - {Float} Optional distance at which snapping may occur
159     *     for vertices specifically.  If none is provided, <tolerance> will be
160     *     used.
161     * edge - {Boolean} Snap to edges in target layer.  Default is true.
162     * edgeTolerance - {Float} Optional distance at which snapping may occur
163     *     for edges specifically.  If none is provided, <tolerance> will be
164     *     used.
165     * filter - {OpenLayers.Filter} Optional filter to evaluate to determine if
166     *     feature is eligible for snapping.  If filter evaluates to true for a
167     *     target feature a vertex may be snapped to the feature.
168     */
169    initialize: function(options) {
170        // concatenate events specific to measure with those from the base
171        Array.prototype.push.apply(
172            this.EVENT_TYPES, OpenLayers.Control.prototype.EVENT_TYPES
173        );
174        OpenLayers.Control.prototype.initialize.apply(this, [options]);
175        this.options = options || {}; // TODO: this could be done by the super
176       
177        // set the editable layer if provided
178        if(this.options.layer) {
179            this.setLayer(this.options.layer);
180        }
181        // configure target layers
182        var defaults = OpenLayers.Util.extend({}, this.options.defaults);
183        this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS);
184        this.setTargets(this.options.targets);
185        if(this.targets.length === 0 && this.layer) {
186            this.addTargetLayer(this.layer);
187        }
188
189        this.geoToleranceCache = {};
190    },
191   
192    /**
193     * APIMethod: setLayer
194     * Set the editable layer.  Call the setLayer method if the editable layer
195     *     changes and the same control should be used on a new editable layer.
196     *     If the control is already active, it will be active after the new
197     *     layer is set.
198     *
199     * Parameters:
200     * layer - {OpenLayers.Layer.Vector}  The new editable layer.
201     */
202    setLayer: function(layer) {
203        if(this.active) {
204            this.deactivate();
205            this.layer = layer;
206            this.activate();
207        } else {
208            this.layer = layer;
209        }
210    },
211   
212    /**
213     * Method: setTargets
214     * Set the targets for the snapping agent.
215     *
216     * Parameters:
217     * targets - {Array} An array of target configs or target layers.
218     */
219    setTargets: function(targets) {
220        this.targets = [];
221        if(targets && targets.length) {
222            var target;
223            for(var i=0, len=targets.length; i<len; ++i) {
224                target = targets[i];
225                if(target instanceof OpenLayers.Layer.Vector) {
226                    this.addTargetLayer(target);
227                } else {
228                    this.addTarget(target);
229                }
230            }
231        }
232    },
233   
234    /**
235     * Method: addTargetLayer
236     * Add a target layer with the default target config.
237     *
238     * Parameters:
239     * layer - {<OpenLayers.Layer.Vector>} A target layer.
240     */
241    addTargetLayer: function(layer) {
242        this.addTarget({layer: layer});
243    },
244   
245    /**
246     * Method: addTarget
247     * Add a configured target layer.
248     *
249     * Parameters:
250     * target - {Object} A target config.
251     */
252    addTarget: function(target) {
253        target = OpenLayers.Util.applyDefaults(target, this.defaults);
254        target.nodeTolerance = target.nodeTolerance || target.tolerance;
255        target.vertexTolerance = target.vertexTolerance || target.tolerance;
256        target.edgeTolerance = target.edgeTolerance || target.tolerance;
257        this.targets.push(target);
258    },
259   
260    /**
261     * Method: removeTargetLayer
262     * Remove a target layer.
263     *
264     * Parameters:
265     * layer - {<OpenLayers.Layer.Vector>} The target layer to remove.
266     */
267    removeTargetLayer: function(layer) {
268        var target;
269        for(var i=this.targets.length-1; i>=0; --i) {
270            target = this.targets[i];
271            if(target.layer === layer) {
272                this.removeTarget(target);
273            }
274        }
275    },
276   
277    /**
278     * Method: removeTarget
279     * Remove a target.
280     *
281     * Parameters:
282     * target - {Object} A target config.
283     *
284     * Returns:
285     * {Array} The targets array.
286     */
287    removeTarget: function(target) {
288        return OpenLayers.Util.removeItem(this.targets, target);
289    },
290   
291    /**
292     * APIMethod: activate
293     * Activate the control.  Activating the control registers listeners for
294     *     editing related events so that during feature creation and
295     *     modification, moving vertices will trigger snapping.
296     */
297    activate: function() {
298        var activated = OpenLayers.Control.prototype.activate.call(this);
299        if(activated) {
300            if(this.layer && this.layer.events) {
301                this.layer.events.on({
302                    sketchstarted: this.onSketchModified,
303                    sketchmodified: this.onSketchModified,
304                    vertexmodified: this.onVertexModified,
305                    scope: this
306                });
307            }
308        }
309        return activated;
310    },
311   
312    /**
313     * APIMethod: deactivate
314     * Deactivate the control.  Deactivating the control unregisters listeners
315     *     so feature editing may proceed without engaging the snapping agent.
316     */
317    deactivate: function() {
318        var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
319        if(deactivated) {
320            if(this.layer && this.layer.events) {
321                this.layer.events.un({
322                    sketchstarted: this.onSketchModified,
323                    sketchmodified: this.onSketchModified,
324                    vertexmodified: this.onVertexModified,
325                    scope: this
326                });
327            }
328        }
329        this.feature = null;
330        this.point = null;
331        return deactivated;
332    },
333   
334    /**
335     * Method: onSketchModified
336     * Registered as a listener for the sketchmodified event on the editable
337     *     layer.
338     *
339     * Parameters:
340     * event - {Object} The sketch modified event.
341     */
342    onSketchModified: function(event) {
343        this.feature = event.feature;
344        this.considerSnapping(event.vertex, event.vertex);
345    },
346   
347    /**
348     * Method: onVertexModified
349     * Registered as a listener for the vertexmodified event on the editable
350     *     layer.
351     *
352     * Parameters:
353     * event - {Object} The vertex modified event.
354     */
355    onVertexModified: function(event) {
356        this.feature = event.feature;
357        var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel);
358        this.considerSnapping(
359            event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat)
360        );
361    },
362
363    /**
364     * Method: considerSnapping
365     *
366     * Parameters:
367     * point - {<OpenLayers.Geometry.Point}} The vertex to be snapped (or
368     *     unsnapped).
369     * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map
370     *     coords.
371     */
372    considerSnapping: function(point, loc) {
373        var best = {
374            rank: Number.POSITIVE_INFINITY,
375            dist: Number.POSITIVE_INFINITY,
376            x: null, y: null
377        };
378        var snapped = false;
379        var result, target;
380        for(var i=0, len=this.targets.length; i<len; ++i) {
381            target = this.targets[i];
382            result = this.testTarget(target, loc);
383            if(result) {
384                if(this.greedy) {
385                    best = result;
386                    best.target = target; 
387                    snapped = true;
388                    break;
389                } else {
390                    if((result.rank < best.rank) ||
391                       (result.rank === best.rank && result.dist < best.dist)) {
392                        best = result;
393                        best.target = target;
394                        snapped = true;
395                    }
396                }
397            }
398        }
399        if(snapped) {
400            var proceed = this.events.triggerEvent("beforesnap", {
401                point: point, x: best.x, y: best.y, distance: best.dist,
402                layer: best.target.layer, snapType: this.precedence[best.rank]
403            });
404            if(proceed !== false) {
405                point.x = best.x;
406                point.y = best.y;
407                this.point = point;
408                this.events.triggerEvent("snap", {
409                    point: point,
410                    snapType: this.precedence[best.rank],
411                    layer: best.target.layer,
412                    distance: best.dist
413                });
414            } else {
415                snapped = false;
416            }
417        }
418        if(this.point && !snapped) {
419            point.x = loc.x;
420            point.y = loc.y;
421            this.point = null;
422            this.events.triggerEvent("unsnap", {point: point});
423        }
424    },
425   
426    /**
427     * Method: testTarget
428     *
429     * Parameters:
430     * target - {Object} Object with target layer configuration.
431     * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map
432     *     coords.
433     *
434     * Returns:
435     * {Object} A result object with rank, dist, x, and y properties.
436     *     Returns null if candidate is not eligible for snapping.
437     */
438    testTarget: function(target, loc) {
439        var tolerance = {
440            node: this.getGeoTolerance(target.nodeTolerance),
441            vertex: this.getGeoTolerance(target.vertexTolerance),
442            edge: this.getGeoTolerance(target.edgeTolerance)
443        };
444        // this could be cached if we don't support setting tolerance values directly
445        var maxTolerance = Math.max(
446            tolerance.node, tolerance.vertex, tolerance.edge
447        );
448        var result = {
449            rank: Number.POSITIVE_INFINITY, dist: Number.POSITIVE_INFINITY
450        };
451        var eligible = false;
452        var features = target.layer.features;
453        var feature, type, vertices, vertex, closest, dist, found;
454        var numTypes = this.precedence.length;
455        var ll = new OpenLayers.LonLat(loc.x, loc.y);
456        for(var i=0, len=features.length; i<len; ++i) {
457            feature = features[i];
458            if(feature !== this.feature && !feature._sketch &&
459               feature.state !== OpenLayers.State.DELETE &&
460               (!target.filter || target.filter.evaluate(feature.attributes))) {
461                if(feature.atPoint(ll, maxTolerance, maxTolerance)) {
462                    for(var j=0, stop=Math.min(result.rank+1, numTypes); j<stop; ++j) {
463                        type = this.precedence[j];
464                        if(target[type]) {
465                            if(type === "edge") {
466                                closest = feature.geometry.distanceTo(loc, {details: true});
467                                dist = closest.distance;
468                                if(dist <= tolerance[type] && dist < result.dist) {
469                                    result = {
470                                        rank: j, dist: dist,
471                                        x: closest.x0, y: closest.y0 // closest coords on feature
472                                    };
473                                    eligible = true;
474                                    // don't look for lower precedence types for this feature
475                                    break;
476                                }
477                            } else {
478                                // look for nodes or vertices
479                                vertices = feature.geometry.getVertices(type === "node");
480                                found = false;
481                                for(var k=0, klen=vertices.length; k<klen; ++k) {
482                                    vertex = vertices[k];
483                                    dist = vertex.distanceTo(loc);
484                                    if(dist <= tolerance[type] &&
485                                       (j < result.rank || (j === result.rank && dist < result.dist))) {
486                                        result = {
487                                            rank: j, dist: dist,
488                                            x: vertex.x, y: vertex.y
489                                        };
490                                        eligible = true;
491                                        found = true;
492                                    }
493                                }
494                                if(found) {
495                                    // don't look for lower precedence types for this feature
496                                    break;
497                                }
498                            }
499                        }
500                    }
501                }
502            }
503        }
504        return eligible ? result : null;
505    },
506   
507    /**
508     * Method: getGeoTolerance
509     * Calculate a tolerance in map units given a tolerance in pixels.  This
510     *     takes advantage of the <geoToleranceCache> when the map resolution
511     *     has not changed.
512     *     
513     * Parameters:
514     * tolerance - {Number} A tolerance value in pixels.
515     *
516     * Returns:
517     * {Number} A tolerance value in map units.
518     */
519    getGeoTolerance: function(tolerance) {
520        var resolution = this.layer.map.getResolution();
521        if(resolution !== this.resolution) {
522            this.resolution = resolution;
523            this.geoToleranceCache = {};
524        }
525        var geoTolerance = this.geoToleranceCache[tolerance];
526        if(geoTolerance === undefined) {
527            geoTolerance = tolerance * resolution;
528            this.geoToleranceCache[tolerance] = geoTolerance;
529        }
530        return geoTolerance;
531    },
532   
533    /**
534     * Method: destroy
535     * Clean up the control.
536     */
537    destroy: function() {
538        if(this.active) {
539            this.deactivate(); // TODO: this should be handled by the super
540        }
541        delete this.layer;
542        delete this.targets;
543        OpenLayers.Control.prototype.destroy.call(this);
544    },
545
546    CLASS_NAME: "OpenLayers.Control.Snapping"
547});
Note: See TracBrowser for help on using the repository browser.