[Mapbender-commits] r10113 - trunk/mapbender/lib

svn_mapbender at osgeo.org svn_mapbender at osgeo.org
Wed Apr 24 02:21:06 PDT 2019


Author: armin11
Date: 2019-04-24 02:21:06 -0700 (Wed, 24 Apr 2019)
New Revision: 10113

Modified:
   trunk/mapbender/lib/mb.ui.displayKmlFeatures.js
Log:
New option to integrate wms getfeature info and selection of digitized objects - thanx to federal german state hesse ;-)

Modified: trunk/mapbender/lib/mb.ui.displayKmlFeatures.js
===================================================================
--- trunk/mapbender/lib/mb.ui.displayKmlFeatures.js	2019-04-24 05:54:16 UTC (rev 10112)
+++ trunk/mapbender/lib/mb.ui.displayKmlFeatures.js	2019-04-24 09:21:06 UTC (rev 10113)
@@ -57,54 +57,39 @@
             self.render();
         });
 
-        this.element.bind('click', function(e) {
-            if (self.lastWasBox) {
-                self.lastWasBox = false;
-                return;
-            }
-            var map = self.element.mapbender();
-            var pos = map.getMousePosition(e);
-            var clickPoint = map.convertPixelToReal(new Point(pos.x, pos.y));
-            var wgspt = Proj4js.transform(self.targetProj, self.wgs84, {
-                x: clickPoint.x,
-                y: clickPoint.y
-            });
-            var res = map.getScale() / mb_resolution / 100;
+        var delta = 2;
+        var lastX, lastY;
+        var box;
 
+        this.element.bind('mousedown', function(e) {
             if (!self.queriedLayer) return;
-            var matchedIds = self.findFeatures(pos);
-
-            self.updateSelectedFeatures(matchedIds, e.ctrlKey);
-        });
-
-        var boxfun;
-
-        this.element.bind('mousedown', boxfun = function(e) {
-            if (!self.queriedLayer) return;
-            var box = new Mapbender.Box({
+            lastX = e.clientX;
+            lastY = e.clientY;
+            box = new Mapbender.Box({
                 target: 'mapframe1'
             });
             box.start(e);
             $('#mapframe1').css("cursor", "crosshair")
-                .mouseup(function(e) {
-                    box.stop(e, function(extent) {
-                        if (typeof extent === "undefined") {
-                            return;
-                        }
+        });
+            
+        $('#mapframe1').mouseup(function(e) {
+            if (!self.queriedLayer) return;
+            var matchedIds;
+            var extent = box.stop(e);
+            $('#mapframe1').css("cursor", "default");
+            if (Math.abs(lastX - e.clientX) <= delta && Math.abs(lastY - e.clientY) <= delta) {
+                // click
+                matchedIds = self.findFeaturesAtClick(e);
+            } else {
+                // drag
+                if (typeof extent === "undefined") {
+                    return;
+                }
 
-                        var matchedIds = self.findFeatures(extent);
-                        self.updateSelectedFeatures(matchedIds, e.ctrlKey);
-                        self.lastWasBox = true;
-
-                        $('#mapframe1')
-                            .css("cursor", "default")
-                            .unbind("mousedown")
-                            .unbind("mouseup")
-                            .unbind("mousemove")
-                            .bind('mousedown', boxfun);
-                    });
-                    return false;
-                });
+                matchedIds = self.findFeaturesInExtent(extent);
+            }
+            self.updateSelectedFeatures(matchedIds, e.ctrlKey);
+            return false;
         });
 
         self.element.bind('kml:loaded', function(event, obj) {
@@ -149,95 +134,90 @@
             self._load(o.url);
         }
     },
-
-    findFeatures: function(posOrBox) {
-        var matchedIds = [];
-
-        var self = this;
-        var map = self.element.mapbender();
-        var ispoint;
-        var clickPoint, wgspt, clickBox, min, max;
-        if (posOrBox.x) {
-            clickPoint = map.convertPixelToReal(new Point(posOrBox.x, posOrBox.y));
-            wgspt = Proj4js.transform(self.targetProj, self.wgs84, {
-                x: clickPoint.x,
-                y: clickPoint.y
-            });
-            ispoint = true;
+    
+    /**
+     * Check if the point lies inside the box.
+     * @param {type} point
+     * @param {type} box
+     * @returns {Boolean}
+     */
+    pointInBox: function (point, box) {
+        if (point.x >= box.minx && point.x <= box.maxx 
+                && point.y >= box.miny && point.y <= box.maxy) {
+            return true;
         } else {
-            clickBox = posOrBox;
-            min = Proj4js.transform(self.targetProj, self.wgs84, {
-                x: clickBox.minx,
-                y: clickBox.miny
-            });
-            max = Proj4js.transform(self.targetProj, self.wgs84, {
-                x: clickBox.maxx,
-                y: clickBox.maxy
-            });
-            ispoint = false;
+            return false;
         }
-        var res = map.getScale() / mb_resolution / 100;
+    },
 
-        $.each(self._kmls, function(_, itm) {
-            $.each(itm.data.features, function(k, v) {
-                if (ispoint) {
-                    //console.log( "isPoint" );
-                    self.matchFeatureToPoint(v, clickPoint, matchedIds, res, itm, wgspt, k);
-                } else {
-                    self.matchFeatureToBox(v, {
-                        min: min,
-                        max: max
-                    }, matchedIds, itm, k);
-                }
-            });
-        });
-        return matchedIds;
+    /**
+     * Checks if value x is between y1 and y2. It works also if y2 < y1.
+     * @param {type} x
+     * @param {type} y1
+     * @param {type} y2
+     * @returns {Number}
+     */
+    valueIsBetween: function (x, y1, y2) {
+        return (y1 - x) * (y2 - x) <= 0;
     },
 
+    /**
+     * calculates the Y value of line for a given X.
+     * @param {type} line
+     * @param {type} y
+     * @returns {Number}
+     */
+    lineYAtX: function (line, x) {
+        return line.y2 + (x - line.x2) * (line.y1 - line.y2) / (line.x1 - line.x2) 
+    },
 
-    inBox: function(minx, miny, maxx, maxy, selectBoxMinx, selectBoxMaxx, selectBoxMiny, selectBoxMaxy) {
+    /**
+     * calculates the X value of line for a given Y.
+     * @param {type} line
+     * @param {type} y
+     * @returns {Number}
+     */
+    lineXAtY: function (line, y) {
+        return line.x2 + (y - line.y2) * (line.x1 - line.x2) / (line.y1 - line.y2) 
+    },
 
-        // ############## proof the min-point ##################
-        // proof hit on the x-axis
-        if ( ( minx >= selectBoxMinx && minx <= selectBoxMaxx ) ) {
-            // if x hit, proof y
-            if ( ( miny >= selectBoxMiny && miny <= selectBoxMaxy ) ) {
-                return true;
-            }
+    /**
+     * First checks if line is completely contained inside of the box. If not it checks
+     * if one of the sides of the box intersects with the line.
+     * @param {type} line
+     * @param {type} box
+     * @returns {Boolean}
+     */
+    lineIntersectsBox: function (line, box) {
+        if (this.pointInBox({ x: line.x1, y: line.y1 }, box)
+                || this.pointInBox({ x: line.x1, y: line.y1 }, box)) {
+            return true;
         }
-        // proof hit on the y-axis
-        if ( ( miny >= selectBoxMiny && miny <= selectBoxMaxy ) ) {
-            // if y hit, proof x
-            if ( ( minx >= selectBoxMinx && minx <= selectBoxMaxx ) ) {
+
+        if (this.valueIsBetween(box.minx, line.x1, line.x2)) {
+            var y = this.lineYAtX(line, box.minx);
+            if (y >= box.miny && y <= box.maxy) {
                 return true;
             }
         }
-        // ############## proof the max-point ##################
-        if ( ( maxx >= selectBoxMinx && maxx <= selectBoxMaxx ) ) {
-            // if x hit, proof y
-            if ( ( maxy >= selectBoxMiny && maxy <= selectBoxMaxy ) ) {
-                return true;
-            }
-        }
 
-        if ( ( maxy >= selectBoxMiny && maxy <= selectBoxMaxy ) ) {
-            // if y hit, proof x
-            if ( ( maxx >= selectBoxMinx && maxx <= selectBoxMaxx ) ) {
+        if (this.valueIsBetween(box.maxx, line.x1, line.x2)) {
+            var y = this.lineYAtX(line, box.maxx);
+            if (y >= box.miny && y <= box.maxy) {
                 return true;
             }
         }
-        // ############## proof if selectbox contains the feature geometry   ##################
-        // proof on x-axis
-        if (  minx >= selectBoxMinx && minx <= selectBoxMaxx  && maxx >= selectBoxMinx && maxx <= selectBoxMaxx ) {
 
-            if ( miny <= selectBoxMiny && maxy >= selectBoxMaxy ) {
+        if (this.valueIsBetween(box.miny, line.y1, line.y2)) {
+            var x = this.lineXAtY(line, box.miny);
+            if (x >= box.minx && x <= box.maxx) {
                 return true;
             }
         }
-        // proof on y-axis
-        if (  miny >= selectBoxMiny && miny <= selectBoxMaxy  && maxy >= selectBoxMiny && maxx <= selectBoxMaxy ) {
 
-            if ( minx <= selectBoxMinx && maxx >= selectBoxMaxx ) {
+        if (this.valueIsBetween(box.maxy, line.y1, line.y2)) {
+            var x = this.lineXAtY(line, box.maxy);
+            if (x >= box.minx && x <= box.maxx) {
                 return true;
             }
         }
@@ -244,92 +224,209 @@
 
         return false;
     },
+   
+    getLines: function (points, connect) {
+        lines = [];
+        for (var i = 0; i < points.length - 1; i ++) {
+            lines.push({
+                x1: points[i][0],
+                y1: points[i][1],
+                x2: points[i + 1][0],
+                y2: points[i + 1][1]
+            });
+        }
+        if (connect) {
+            lines.push({
+                x1: points[points.length - 1][0],
+                y1: points[points.length - 1][1],
+                x2: points[0][0],
+                y2: points[0][1]
+            })
+        }
+        return lines;
+    },
+   
+    lineStringIntersectsBox: function (lineStringPoints, box) {
+        var self = this;
+        return this.getLines(lineStringPoints).some(function (line) {
+            return self.lineIntersectsBox(line, box);
+        });
+    },
 
-    pointInBox: function(pointX, pointY, selectBoxMinx, selectBoxMaxx, selectBoxMiny, selectBoxMaxy) {
+    /**
+     * Checks whether a polygon intersects a box.
+     * On the one hand it checks if any of the lines on the polygon ring intersect
+     * with the box and on the other hand it checks how many times a ray cast outwards
+     * from the middle of the box intersects with one of the lines. If it intersects
+     * an odd number of times it lays inside the polygon.
+     * @param {type} polygon
+     * @param {type} box
+     * @returns {Boolean}
+     */
+    polygonIntersectsBox: function (polygonRings, box) {
+        var rayPoint = {
+            x: box.minx + (box.maxx - box.minx) / 2,
+            y: box.miny + (box.maxy - box.miny) / 2
+        };
+        var rayIntersections = 0;
 
-        if ( ( pointX >= selectBoxMinx && pointX <= selectBoxMaxx ) && ( pointY >= selectBoxMiny && pointY <= selectBoxMaxy )) {
+        var self = this;
+        var lines = polygonRings.reduce(function (lines, ring) {
+            Array.prototype.push.apply(lines, self.getLines(ring, true));
+            return lines;
+        }, [])
+
+        for (var i = 0; i < lines.length; i++) {
+            if (this.lineIntersectsBox(lines[i], box)) {
+                return true;
+            }
+            if (this.valueIsBetween(rayPoint.y, lines[i].y1, lines[i].y2)) {
+                var x = this.lineXAtY(lines[i], rayPoint.y);
+                if (x >= rayPoint.x) {
+                    rayIntersections++;
+                }
+            }
+        }
+
+        if (rayIntersections % 2 === 1) {
             return true;
+        } else {
+            return false;
         }
     },
-
-    matchFeatureToPoint: function(feat, clickPoint, matchedIds, res, itm, wgspt, idx) {
-        //console.log( "point" );
-        if (feat.geometry.type.match(/point/i)) {
-            var pt = Proj4js.transform(this.wgs84, this.targetProj, {
-                x: feat.geometry.coordinates[0],
-                y: feat.geometry.coordinates[1]
-            });
-            var minx = pt.x - 20 * res;
-            var miny = pt.y - 20 * res;
-            var maxx = minx + 40 * res;
-            var maxy = miny + 40 * res;
-            if (minx < clickPoint.x && maxx > clickPoint.x && miny < clickPoint.y && maxy > clickPoint.y) {
-                matchedIds.push({
-                    url: itm.url,
-                    id: idx
-                });
-            }
+   
+    boxInBox: function (boxA, boxB) {
+         return this.pointInBox({ x: boxA.minx, y: boxA.miny }, boxB) &&
+              this.pointInBox({ x: boxA.maxx, y: boxA.maxy }, boxB);
+    },
+    
+    
+    /**
+     * Checks whether the geometry of the feature intersects with the box
+     * @param {type} feature
+     * @param {type} box
+     * @returns {Boolean}
+     */
+    featureIntersectsBox: function (feature, box) {
+        var geometryType = feature.geometry.type.toLowerCase();
+        switch (geometryType) {
+            case 'point':
+                return this.pointInBox({
+                    x: feature.geometry.coordinates[0],
+                    y: feature.geometry.coordinates[1]
+                }, box);
+            case 'linestring':
+                return this.lineStringIntersectsBox(feature.geometry.coordinates, box);
+            case 'polygon':
+                return this.polygonIntersectsBox(feature.geometry.coordinates, box);
+            default:
+              throw new Error('feature geometry type not supported');  
+        }
+    },
+    
+    featureInBox: function (feature, box) {
+        if (feature.geometry.type.toLowerCase() === 'point') {
+            return this.pointInBox({
+                x: feature.geometry.coordinates[0],
+                y: feature.geometry.coordinates[1]
+            }, box);
         } else {
-            var box = this.getBbox(feat);
-            if (this.inBox(box[0], box[1], box[2], box[3], wgspt.x, wgspt.y)) {
-                matchedIds.push({
-                    url: itm.url,
-                    id: idx
-                });
+            var arrayBox = this.getBbox(feature);
+            return this.boxInBox({
+                minx: arrayBox[0],
+                miny: arrayBox[1],
+                maxx: arrayBox[2],
+                maxy: arrayBox[3]
+            }, box);
+        }
+    },
+    
+    
+    
+    /**
+     * Finds all features that intersect with a buffer around a point or a given box.
+     * @param {type} posOrBox
+     * @returns {Array|displayFeatures.findIntersectingFeatures.ids}
+     */
+    findFeaturesAtClick: function (e) {
+        var map = this.element.mapbender();
+        var pos = map.getMousePosition(e);
+        
+        var min = {
+            x: pos.x - 20,
+            y: pos.y + 20 // screen y is in opposite direction of map y
+        };
+        var max = {
+            x: pos.x + 20,
+            y: pos.y - 20
+        };
+        
+        min = map.convertPixelToReal(min);
+        max = map.convertPixelToReal(max);
+        
+        min = Proj4js.transform(this.targetProj, this.wgs84, min);
+        max = Proj4js.transform(this.targetProj, this.wgs84, max);
+        
+        var box = {
+            minx: min.x,
+            miny: min.y,
+            maxx: max.x,
+            maxy: max.y
+        };
+        
+        var self = this;
+        var matches = [];
+        for (var kmlId in this._kmls) {
+            if (this._kmls.hasOwnProperty(kmlId)) {
+                var kml = this._kmls[kmlId];
+                matches = kml.data.features.reduce(function (matches, feature, index) {
+                    if (self.featureIntersectsBox(feature, box)) {
+                        matches.push({
+                            url: kml.url,
+                            id: index
+                        });
+                    }
+                    return matches;
+                }, matches);
             }
         }
+        return matches;
     },
 
-    matchFeatureToBox: function(feat, wgsbox, matchedIds, itm, idx) {
-        if (feat.geometry.type.match(/point/i)) {
-            //console.log( feat.geometry );
-            if (this.pointInBox(feat.geometry.coordinates[0], feat.geometry.coordinates[1], wgsbox.min.x, wgsbox.max.x, wgsbox.min.y, wgsbox.max.y)) {
-                matchedIds.push({
-                    url: itm.url,
-                    id: idx
-                });
+    findFeaturesInExtent: function(extent) {
+        var min = Proj4js.transform(this.targetProj, this.wgs84, {
+            x: extent.minx,
+            y: extent.miny
+        });
+        var max = Proj4js.transform(this.targetProj, this.wgs84, {
+            x: extent.maxx,
+            y: extent.maxy
+        });
+        
+        var box = {
+            minx: min.x,
+            miny: min.y,
+            maxx: max.x,
+            maxy: max.y
+        };
+        
+        var matches = [];
+        var self = this;
+        for (var kmlId in this._kmls) {
+            if (this._kmls.hasOwnProperty(kmlId)) {
+                var kml = this._kmls[kmlId];
+                matches = kml.data.features.reduce(function (matches, feature, index) {
+                    if (self.featureInBox(feature, box)) {
+                        matches.push({
+                            url: kml.url,
+                            id: index
+                        });
+                    }
+                    return matches;
+                }, matches);
             }
-        } else {
-            var box = this.getBbox(feat);
-            if (this.inBox(box[0], box[1], box[2], box[3], wgsbox.min.x, wgsbox.max.x, wgsbox.min.y, wgsbox.max.y) ) {
-                matchedIds.push({
-                    url: itm.url,
-                    id: idx
-                });
-                return;
-            }
-            if ( this.inBox(box[0], box[1], box[2], box[3], wgsbox.min.x, wgsbox.max.x, wgsbox.min.x, wgsbox.max.x) ) {
-                matchedIds.push({
-                    url: itm.url,
-                    id: idx
-                });
-            }
-            // if (wgsbox.min.x < box[0] && wgsbox.max.x > box[2]) {
-            //     if (wgsbox.min.y > box[1] && wgsbox.min.y < box[3] || wgsbox.max.y > box[1] && wgsbox.max.y < box[3]) {
-            //         matchedIds.push({
-            //             url: itm.url,
-            //             id: idx
-            //         });
-            //         return;
-            //     }
-            // }
-            // if (wgsbox.min.y < box[1] && wgsbox.max.y > box[3]) {
-            //     if (wgsbox.min.x > box[0] && wgsbox.min.x < box[2] || wgsbox.max.x > box[0] && wgsbox.max.x < box[2]) {
-            //         matchedIds.push({
-            //             url: itm.url,
-            //             id: idx
-            //         });
-            //         return;
-            //     }
-            // }
-            // if (wgsbox.min.x < box[0] && wgsbox.max.x > box[2] && wgsbox.min.y > box[1] && wgsbox.max.y < box[3]) {
-            //     matchedIds.push({
-            //         url: itm.url,
-            //         id: idx
-            //     });
-            //     return;
-            // }
         }
+        return matches;
     },
 
     updateSelectedFeatures: function(ids, append) {
@@ -735,8 +832,6 @@
                 }
             },
             error: function(XMLHttpRequest, textStatus, errorThrown) {
-
-
                 self.element.trigger('kml:error', "Problem talking to server: " + errorThrown);
             }
         });
@@ -841,7 +936,6 @@
     getBbox: function(feature) {
         switch (feature.geometry.type.toLowerCase()) {
             case 'point':
-                var map = $(this.element).mapbender();
                 var minx = feature.geometry.coordinates[0] - 0.001;
                 var miny = feature.geometry.coordinates[1] - 0.001;
                 return [minx, miny, minx + 0.002, miny + 0.002];



More information about the Mapbender_commits mailing list