changeset 1538:747ed0af8663

annotations: some refactoring in shape creation; enable shape editing
author hertzhaft
date Sun, 16 Oct 2016 20:41:53 +0200
parents dc63c24c59be
children 157198c7e805
files webapp/src/main/webapp/jquery/jquery.digilib.annotator.js webapp/src/main/webapp/jquery/jquery.digilib.measure.js webapp/src/main/webapp/jquery/jquery.digilib.vector.js
diffstat 3 files changed, 167 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- a/webapp/src/main/webapp/jquery/jquery.digilib.annotator.js	Thu Oct 13 18:08:14 2016 +0200
+++ b/webapp/src/main/webapp/jquery/jquery.digilib.annotator.js	Sun Oct 16 20:41:53 2016 +0200
@@ -42,6 +42,14 @@
     // annotation shape layer
     var annotationLayer = null;
 
+    // translate shapes to Digilib
+    var shapeTypeMap = {
+      point: 'Point',
+      rectangle: 'Rectangle',
+      linestring: 'LineString',
+      polygon: 'Polygon'
+      };
+
     var buttons = {
         annotations : {
             onclick : "toggleAnnotations",
@@ -126,7 +134,7 @@
         setAnnotationMark : function (data, mpos, text) {
             if (mpos == null) {
                 // interactive
-                setAnnotationShape(data, 'Point');
+                addAnnotationShape(data, 'point');
             } else {
                 // use position and text (and user-id)
                 console.error("Sorry, currently only interactive annotations!");
@@ -143,7 +151,7 @@
         setAnnotationRect : function (data, rect, text) {
             if (rect == null) {
                 // interactive
-                setAnnotationShape(data, 'Rectangle');
+                addAnnotationShape(data, 'rectangle');
             } else {
                 // use position and text (and user-id)
                 console.error("Sorry, currently only interactive annotations!");
@@ -154,13 +162,13 @@
          * set a polygon-annotation by clicking (or giving a position and a text)
          *
          * @param data
-         * @param poly
+         * @param polygon
          * @param text
          */
-        setAnnotationPolygon : function (data, poly, text) {
-            if (poly == null) {
+        setAnnotationPolygon : function (data, polygon, text) {
+            if (polygon == null) {
                 // interactive
-                setAnnotationShape(data, 'Polygon');
+                addAnnotationShape(data, 'polygon');
             } else {
                 // use position and text (and user-id)
                 console.error("Sorry, currently only interactive annotations!");
@@ -174,10 +182,10 @@
          * @param poly
          * @param text
          */
-        setAnnotationPolyline : function (data, poly, text) {
-            if (poly == null) {
+        setAnnotationPolyline : function (data, polyline, text) {
+            if (polyline == null) {
                 // interactive
-                setAnnotationShape(data, 'LineString');
+                addAnnotationShape(data, 'linestring');
             } else {
                 // use position and text (and user-id)
                 console.error("Sorry, currently only interactive annotations!");
@@ -267,73 +275,67 @@
     /**
      * Add a shape-annotation where clicked.
      */
-    var setAnnotationShape = function (data, type) {
-        var annotator = data.annotator;
-        var shape = {'geometry': {'type': type}};
-        digilib.actions.addShape(data, shape, function (data, newshape) {
-        	console.debug("new annotation shape:", newshape);
-        	var annoShape = null;
-        	var pos = null;
-        	if (type === 'Point') {
-        		pos = geom.position(newshape.geometry.coordinates[0]);
-                // create annotation shape
-                annoShape = {'type': 'point', 'geometry': pos};
-                annoShape.geometry['units'] = 'fraction'; 
-        	} else if (type === 'Rectangle') {
-        		pos = geom.position(newshape.geometry.coordinates[0]);
-        		var pt2 = geom.position(newshape.geometry.coordinates[1]);
-        		var rect = geom.rectangle(pos, pt2);
-                // create annotation shape
-                annoShape = {'type': 'rectangle', 'geometry': rect};
-                annoShape.geometry['units'] = 'fraction'; 
-        	} else if (type === 'Polygon') {
-        		pos = geom.position(newshape.geometry.coordinates[0]);
-        		// create annotation shape
-                annoShape = {'type': 'polygon', 'geometry': {'coordinates': newshape.geometry.coordinates}};
-                annoShape.geometry['units'] = 'fraction'; 
-        	} else if (type === 'LineString') {
-        		pos = geom.position(newshape.geometry.coordinates[0]);
-        		// create annotation shape
-                annoShape = {'type': 'linestring', 'geometry': {'coordinates': newshape.geometry.coordinates}};
-                annoShape.geometry['units'] = 'fraction'; 
-        	} else {
-        		console.error("Unsupported annotation shape="+type);
-        		return;
-        	}
-            var mpos = data.imgTrafo.transform(pos);
-            createAnnotation(data, annoShape, mpos);        	
-        }, annotationLayer);
+    var addAnnotationShape = function (data, type) {
+      var onComplete = function (data, shape) {
+        if (! shape.type in data.shapeFactory) {
+          console.error("Unsupported annotation shape="+type);
+          return;
+          }
+        var newshape = {
+          type: shape.type,
+          geometry: shape.geometry
+          };
+        if (shape.type === 'Rectangle') {
+          var c = shape.geometry.coordinates;
+          newshape.geometry = geom.rectangle(c[0], c[1]);
+          }
+        newshape.geometry.units = 'fraction';
+        // screen position for annotation editor
+        var pos = geom.position(newshape.geometry.coordinates[0]);
+        var mpos = data.imgTrafo.transform(pos);
+        console.debug("creating annotation for shape:", newshape);
+        addAnnotationContent(data, newshape, mpos);
+        };
+      var shape = {
+        type: type,
+        geometry: {
+          type: shapeTypeMap[type]
+          }
+        };
+      digilib.actions.addShape(data, shape, annotationLayer, onComplete);
     };
     
     
     /**
      * Show editor and save annotation.
      */
-    var createAnnotation = function (data, shape, screenPos) {
-    	var annotator = data.annotator;
-	    annotator.selectedShapes = [shape];
-	    // create and edit new annotation
-	    var annotation = annotator.createAnnotation();
-	    var cleanup = function () {
-	    	annotator.unsubscribe('annotationEditorSubmit', save);
-	    	annotator.unsubscribe('annotationEditorHidden', cancel);
-	    };
-	    var save = function () {
-	    	console.log("annotation save.");
-	    	cleanup();
-	        annotator.setupAnnotation(annotation);
-	        // Fire annotationCreated events so that plugins can react to them
-	        annotator.publish('annotationCreated', [annotation]);
-	        renderAnnotations(data);
-	    };
-	    var cancel = function () {
-	    	console.log("annotation cancel.");
-	    	cleanup();
-            renderAnnotations(data);
-	    };
-	    annotator.subscribe('annotationEditorSubmit', save);
-	    annotator.subscribe('annotationEditorHidden', cancel);
-	    annotator.showEditor(annotation, screenPos.getAsCss());
+    var addAnnotationContent = function (data, shape, screenPos) {
+      var annotator = data.annotator;
+      annotator.selectedShapes = [shape];
+      // create and edit new annotation
+      var annotation = annotator.createAnnotation();
+      annotation.editing = true;
+      var cleanup = function () {
+        annotator.unsubscribe('annotationEditorSubmit', save);
+        annotator.unsubscribe('annotationEditorHidden', cancel);
+        delete annotation.editing;
+      };
+      var save = function () {
+        console.log("annotation save.");
+        cleanup();
+        annotator.setupAnnotation(annotation);
+        // Fire annotationCreated events so that plugins can react to them
+        annotator.publish('annotationCreated', [annotation]);
+        renderAnnotations(data);
+      };
+      var cancel = function () {
+        console.log("annotation cancel.");
+        cleanup();
+        renderAnnotations(data);
+      };
+      annotator.subscribe('annotationEditorSubmit', save);
+      annotator.subscribe('annotationEditorHidden', cancel);
+      annotator.showEditor(annotation, screenPos.getAsCss());
     };
     
     /**
@@ -351,7 +353,10 @@
         var shapes = [];
         if (data.dlOpts.isAnnotationsVisible) {
             for (var i = 0; i < annotations.length; ++i) {
-                shapes = shapes.concat(createShape(data, annotations[i]));
+                var annot = annotations[i];
+                var annoshapes = createShape(data, annot);
+                annotationLayer.shapeindex[annot.annotation.id] = annoshapes;
+                shapes = shapes.concat(annoshapes);
             }
         }
         annotationLayer.shapes = shapes;
@@ -390,19 +395,12 @@
         var shapes = [];
         if (annotation.areas != null && annotation.shapes == null) {
             console.warn("Annotation uses legacy 'areas' format! Converting...");
-            /*
-             * convert legacy annotation areas into shapes
-             */
+            // convert legacy annotation areas into shapes
             area = geom.rectangle(annotation.areas[0]);
             annoShape = {
                 'geometry' : area,
-                'units' : 'fraction'
-            };
-            if (area.isRectangle()) {
-                annoShape['type'] = 'rectangle';
-            } else {
-                annoShape['type'] = 'point';
-            }
+                'type': area.isRectangle() ? 'rectangle' : 'point'
+                };
             delete annotation.areas;
             annotation.shapes = [annoShape];
         }
@@ -415,73 +413,36 @@
             // annotation shape
             annoShape = annotation.shapes[i];
             type = annoShape.type;
+            var coordinates;
             if (type === "point") {
                 area = geom.position(annoShape.geometry);
-            	shape = {
-            			'id': id,
-            			'geometry': {
-            				'type' : 'Point',
-            				'coordinates' : [[area.x, area.y]]
-            			},
-            			'properties' : {
-                            'stroke' : 'yellow',
-                            'cssclass' : cssPrefix+'svg-annotation annotator-hl',
-                            'style' : 'pointer-events:all'
-                    	},
-                    	'annotation': annotation
-            	};
+                coordinates = [[area.x, area.y]];
             } else if (type === "rectangle") {
                 area = geom.rectangle(annoShape.geometry);
-                // render rectangle
-            	var pt1 = area.getPt1();
-            	var pt2 = area.getPt2();
-            	shape = {
-            			'id': id,
-            			'geometry': {
-            				'type' : 'Rectangle',
-            				'coordinates' : [[pt1.x, pt1.y], [pt2.x, pt2.y]]
-            			},
-            			'properties' : {
-                            'stroke' : 'yellow',
-                            'cssclass' : cssPrefix+'svg-annotationregion annotator-hl',
-                            'style' : 'pointer-events:all'
-                    	},
-                    	'annotation': annotation
-            	};
-            } else if (type === "polygon") {
-                // render polygon
-            	shape = {
-            			'id': id,
-            			'geometry': {
-            				'type' : 'Polygon',
-            				'coordinates' : annoShape.geometry.coordinates
-            			},
-            			'properties' : {
-                            'stroke' : 'yellow',
-                            'cssclass' : cssPrefix+'svg-annotationregion annotator-hl',
-                            'style' : 'pointer-events:all'
-                    	},
-                    	'annotation': annotation
-            	};
-            } else if (type === "linestring") {
-                // render polyline
-            	shape = {
-            			'id': id,
-            			'geometry': {
-            				'type' : 'LineString',
-            				'coordinates' : annoShape.geometry.coordinates
-            			},
-            			'properties' : {
-                            'stroke' : 'yellow',
-                            'cssclass' : cssPrefix+'svg-annotation annotator-hl',
-                            'style' : 'pointer-events:visiblePainted'
-                    	},
-                    	'annotation': annotation
-            	};
+                var pt1 = area.getPt1();
+                var pt2 = area.getPt2();
+                coordinates = [[pt1.x, pt1.y], [pt2.x, pt2.y]];
+            } else if (type in shapeTypeMap) {
+                coordinates = annoShape.geometry.coordinates;
             } else {
-                console.error("Unsupported annotation shape type: "+type);
+                console.error("Unsupported annotation shape="+type);
                 return;
             }
+            var shape = {
+               'id': id,
+               'annotation': annotation,
+               'geometry': {
+                 'type': shapeTypeMap[type],
+                 'units' : 'fraction',
+                 'coordinates': coordinates
+                 },
+               'properties': {
+                  'stroke': 'yellow',
+                  'cssclass': cssPrefix+'svg-annotationregion annotator-hl',
+                  'style': 'pointer-events:all'
+                  }
+               };
+            console.debug('createshape: '+type, annoShape, shape);
             shapes.push(shape);
         }
         return shapes;
@@ -515,9 +476,13 @@
         if (annoShape.cssclass != null) {
             $annotation[0].classList.add(shape.cssclass);
         }
-        // hook up Annotator events
-        $annotation.on("mouseover", annotator.onHighlightMouseover);
-        $annotation.on("mouseout", annotator.startViewerHideTimer);
+        // no mouseover when editing
+        if (shape.properties.editable || annotation.editing) {
+            $annotation[0].classList.remove('annotator-hl');
+        } else {
+          $annotation.on("mouseover", annotator.onHighlightMouseover);
+          $annotation.on("mouseout", annotator.startViewerHideTimer);
+        }
         /* $annotation.on('click.dlAnnotation', function(event) {
             $(data).trigger('annotationClick', [$annotation]);
         }); */ 
@@ -783,9 +748,12 @@
         annotationLayer = {
             'projection': 'screen', 
             'renderFn': fn.vectorDefaultRenderFn,
-            'shapes': []
+            'shapes': [],
+            'shapeindex': {}
         };
         digilib.actions.addVectorLayer(data, annotationLayer);
+        // make annotationLayer accessible for plugins
+        data.annotationLayer = annotationLayer;
         $(data).on("renderShape", handleRenderShape);
         // set up annotator (after html has been set up)
         var uri = getAnnotationPageUri(data);
--- a/webapp/src/main/webapp/jquery/jquery.digilib.measure.js	Thu Oct 13 18:08:14 2016 +0200
+++ b/webapp/src/main/webapp/jquery/jquery.digilib.measure.js	Sun Oct 16 20:41:53 2016 +0200
@@ -754,13 +754,13 @@
             Line :       { name : 'line',           display : 'length', },
             LineString : { name : 'linestring',     display : 'length'  },
             Proportion : { name : 'proportion',     display : 'length'  },
-            Rectangle :  { name : 'box',            display : 'diagonal'    },
+            Rectangle :  { name : 'box',            display : 'diagonal' },
             Rect :       { name : 'rectangle',      display : 'area'    },
             Square :     { name : 'square',         display : 'length'  },
             Polygon :    { name : 'polygon',        display : 'area'    },
             Circle :     { name : 'circle',         display : 'radius'  },
             Ellipse :    { name : 'ellipse',        display : 'area'    },
-            Oval :       { name : 'oval',           display : 'distance'  },
+            Oval :       { name : 'oval',           display : 'distance' },
             Grid :       { name : 'linegrid',       display : 'spacing' }
             },
         // currently selected shape type
@@ -832,7 +832,7 @@
             var shape = newShape(data);
             var layer = data.measureLayer;
             $(data).trigger('createShape', shape);
-            digilib.actions.addShape(data, shape, shapeCompleted, layer);
+            digilib.actions.addShape(data, shape, layer, shapeCompleted);
             console.debug('drawshape', shape);
             _debug_shape('action drawshape', shape);
             }
@@ -1401,7 +1401,8 @@
                     var $c1 = $(fn.svgElement('circle', {'id': shape.id + '-circ1', 'class': guide }));
                     var $c2 = $(fn.svgElement('circle', {'id': shape.id + '-circ2', 'class': guide }));
                     var $p1 = $(fn.svgElement('path',   {'id': shape.id + '-lines', 'class': guide }));
-                    var $p2 = $(fn.svgElement('path',   {'id': shape.id + '-constr', 'class': constr })); // debug construction
+                    var $p2 = $(fn.svgElement('path',   {'id': shape.id + '-constr', 'class': constr })); // debug construction
+
                     var $arc = $(fn.svgElement('path',   {'id': shape.id + '-arc', 'class': shapeClass, stroke: props.stroke, 'stroke-width': styles.shape['stroke-width'], fill: 'none' }));
                     $g.append($s).append($c1).append($c2).append($p1).append($p2).append($arc);
                     $g.place = function () {
--- a/webapp/src/main/webapp/jquery/jquery.digilib.vector.js	Thu Oct 13 18:08:14 2016 +0200
+++ b/webapp/src/main/webapp/jquery/jquery.digilib.vector.js	Sun Oct 16 20:41:53 2016 +0200
@@ -28,7 +28,7 @@
  * 
  * Shapes are objects with "geometry" and "properties" members.
  * geometry is an object with "type" and "coordinates" members.
- * Currently supported types: "Point", "Line", "LineString", "Rectangle", "Polygon", "Circle". 
+ * Currently supported types: "Point", "Line", "LineString", "Rectangle", "Polygon", "Circle".
  * coordinates is a list of pairs of relative coordinates.
  * properties are the SVG properties "stroke", "stroke-width", "fill" and other properties.
  * A property 'editable':true will display drag-handles to change the shape.
@@ -100,21 +100,25 @@
          * @param onComplete
          * @param layer
          */
-        addShape : function (data, shape, onComplete, layer) {
+        addShape : function (data, shape, layer, onComplete) {
             if (layer == null) {
-                // assume shape layer is 0
-                layer = data.vectorLayers[0];
+              // assume shape layer is 0
+              layer = data.vectorLayers[0];
             }
             if (layer.shapes == null) {
             	layer.shapes = [];
             }
-        	if (shape.geometry.coordinates == null) {
+            if (shape.geometry == null) {
+              shape.geometry = {};
+            }
+            if (shape.geometry.coordinates == null) {
         		// define shape interactively
-        		defineShape(data, shape, layer, onComplete);
-        	} else {
-        		layer.shapes.push(shape);
+        		  defineShape(data, shape, layer, onComplete);
+        		  console.debug('addShape', shape);
+        		} else {
+        		  layer.shapes.push(shape);
             	renderShapes(data, layer);
-        	}
+            }
         },
 
         /**
@@ -206,8 +210,13 @@
         $.extend(digilib.defaults, defaults);
         $.extend(digilib.actions, actions);
         // export functions
-        digilib.fn.vectorDefaultRenderFn = renderShapes;
-        digilib.fn.svgElement = svgElement;
+        $.extend(digilib.fn, {
+            vectorDefaultRenderFn: renderShapes,
+            svgElement: svgElement,
+            createScreenCoords: createScreenCoords,
+            editShapeBegin: addEditHandles,
+            editShapeEnd: removeEditHandles
+            });
     };
 
     /** 
@@ -295,6 +304,7 @@
         for (var i = 0; i < shapes.length; ++i) {
             var shape = shapes[i];
             data.shapeFactory[shape.geometry.type].setup(data, shape);
+            console.debug('render', shape);
         }
         // sort shapes by size descending
         shapes.sort(function (a, b) {
@@ -529,18 +539,15 @@
     };
 
     /**
-     * create handles for a shape.
+     * add adjustment handles to a shape.
      * 
-     * Creates SVG elements for each screen point and append it to the SVG element.
+     * Creates a SVG element for each screen point and append them to the SVG element.
      * 
      * @param data
      * @param shape
-     * @param svg The SVG element where to append handle elements
-     * @param func If present, use a special create function
+     * @param layer
      */
-    //create handles for a shape.
-    var createHandles = function (data, shape, layer) {
-        if (!shape.properties.editable) { return };
+    var addEditHandles = function (data, shape, layer) {
         var $svg = $(layer.svgElem);
         var trafo = data.imgTrafo;
         // type of handle can be stated in layer
@@ -570,6 +577,22 @@
     };
 
     /**
+     * remove SVG adjustment handles from a shape.
+     * 
+     * @param data
+     * @param shape
+     */
+    var removeEditHandles = function (data, shape) {
+    	// remove vertex handles
+    	if (shape.$vertexElems != null) {
+    		for (var i = 0; i < shape.$vertexElems.length; ++i) {
+    			shape.$vertexElems[i].remove();
+    			}
+    		delete shape.$vertexElems;
+    		}
+    	};
+
+    /**
      * calculate screen positions from coordinates for a shape.
      * 
      * @param data
@@ -581,6 +604,9 @@
         var screenpos = $.map(coords, function (coord) {
             return trafo.transform(geom.position(coord));
             });
+        if (shape.properties == null) {
+          shape.properties = {};
+        }
         shape.properties.screenpos = screenpos;
         return screenpos;
     };
@@ -599,7 +625,7 @@
             xmin = (x < xmin) ? x : xmin;
             xmax = (x > xmax) ? x : xmax;
             ymin = (y < ymin) ? y : ymin;
-            ymax = (y > ymax) ? y : ymax;            
+            ymax = (y > ymax) ? y : ymax;
         }
         return geom.rectangle(xmin, ymin, xmax-xmin, ymax-ymin);
     };
@@ -633,7 +659,9 @@
         $elem.place();
         // render the SVG
         $(layer.svgElem).append($elem);
-        createHandles(data, shape, layer);
+        if (shape.properties.editable) {
+            addEditHandles(data, shape, layer);
+        }
         $(data).trigger("renderShape", shape);
     };
 
@@ -646,13 +674,7 @@
      * @param shape
      */
     var unrenderShape = function (data, shape) {
-    	// remove vertex handles
-    	if (shape.$vertexElems != null) {
-    		for (var i = 0; i < shape.$vertexElems.length; ++i) {
-    			shape.$vertexElems[i].remove();
-    		}
-    		delete shape.$vertexElems;
-    	}
+      removeEditHandles(data, shape);
     	// remove SVG element
     	if (shape.$elem != null) {
     		shape.$elem.remove();
@@ -767,10 +789,6 @@
      * @onComplete function (data, shape)
      */
     var defineShape = function (data, shape, layer, onComplete) {
-        if (layer == null) {
-            // assume shape layer is 0
-            layer = data.vectorLayers[0];
-        }
         var shapeType = shape.geometry.type;
         // call setup to make sure maxvtx is set
         data.shapeFactory[shapeType].setup(data, shape);