');
+ let verifyButton = $('
' +
'' +
'');
- var editButton = $('
' +
+ let editButton = $('' +
'' +
'');
- var deleteButton = $('
' +
+ let deleteButton = $('' +
'' +
'');
const annotationId = annotation.id;
editButton.attr('id', 'annotation_edit_button_' + annotationId);
editButton.click(function(event) {
- editAnnotation(event, this, annotationId);
+ editAnnotation(this, annotationId);
});
editButton.data('annotationtypeid', annotation.annotation_type.id);
editButton.data('annotationid', annotation.id);
editButton.data('vector', annotation.vector);
editButton.data('blurred', annotation.blurred);
editButton.data('concealed', annotation.concealed);
- deleteButton.click(function(event) {
- deleteAnnotation(event, annotationId);
+ deleteButton.click(async function(event) {
+ event.preventDefault(); // the button is a link, don't follow it
+ await deleteAnnotation(annotationId);
});
annotationLinks.append(verifyButton);
annotationLinks.append(' ');
@@ -461,18 +419,12 @@ function calculateImageScale() {
existingAnnotations.removeClass('hidden');
}
- /**
- * Highlight one annotation in a different color
- * @param annotationTypeId
- * @param annotationId
- */
-
function handleMouseClick(e) {
if (e && (e.target.id === 'image' || e.target.id === 'image_canvas')) {
- var position = globals.image.offset();
- globals.mouseClickX = Math.round((e.pageX - position.left));
- globals.mouseClickY = Math.round((e.pageY - position.top));
- tool.handleMouseClick(e);
+ let position = globals.image.offset();
+ let x = Math.round((e.pageX - position.left));
+ let y = Math.round((e.pageY - position.top));
+ tool.handleMouseClick(e, x, y);
}
// remove any existing highlight
@@ -511,35 +463,32 @@ function calculateImageScale() {
*
* @param imageId
*/
- function displayImage(imageId) {
+ async function displayImage(imageId) {
imageId = parseInt(imageId);
if (gImageList.indexOf(imageId) === -1) {
console.log(
- 'skiping request to load image ' + imageId +
+ 'skipping request to display image ' + imageId +
' as it is not in current image list.');
return;
}
if (gImageCache[imageId] === undefined) {
// image is not available in cache. Load it.
- loadImageToCache(imageId);
+ await loadImageToCache(imageId);
}
// image is in cache.
- var currentImage = globals.image;
- var newImage = gImageCache[imageId];
+ let currentImage = globals.image;
+ let newImage = gImageCache[imageId];
currentImage.attr('id', '');
newImage.attr('id', 'image');
gImageId = imageId;
- preloadImages();
currentImage.replaceWith(newImage);
globals.image = newImage;
calculateImageScale();
- tool.initSelection();
- tool.resetSelection();
if (currentImage.data('imageid') !== undefined) {
// add previous image to cache
@@ -547,22 +496,34 @@ function calculateImageScale() {
}
}
+ /**
+ * Delete the current selection
+ */
+ function resetSelection() {
+ tool.resetSelection();
+ gEditAnnotationId = undefined;
+ $('.annotation').removeClass('alert-info');
+ $('#edit_active').addClass('hidden');
+ $('#concealed').prop('checked', false);
+ $('#blurred').prop('checked', false);
+ }
+
/**
* Display the images of an image list.
*
* @param imageList
*/
- function displayImageList(imageList) {
- var oldImageList = $('#image_list');
- var result = $('
');
- var imageContained = false;
+ async function displayImageList(imageList) {
+ let oldImageList = $('#image_list');
+ let result = $('
');
+ let imageContained = false;
oldImageList.html('');
- for (var i = 0; i < imageList.length; i++) {
- var image = imageList[i];
+ for (let i = 0; i < imageList.length; i++) {
+ let image = imageList[i];
- var link = $('
');
+ let link = $('');
link.attr('id', 'annotate_image_link_' + image.id);
link.attr('href', ANNOTATE_URL.replace('%s', image.id));
link.addClass('annotate_image_link');
@@ -572,9 +533,9 @@ function calculateImageScale() {
}
link.text(image.name);
link.data('imageid', image.id);
- link.click(function(event) {
- event.preventDefault();
- loadAnnotateView($(this).data('imageid'));
+ link.click(async function(event) {
+ event.preventDefault(); // do not let the browser follow the link
+ await changeImage($(this).data('imageid'));
});
result.append(link);
@@ -584,14 +545,7 @@ function calculateImageScale() {
result.attr('id', 'image_list');
oldImageList.replaceWith(result);
- gImageList = getImageList();
-
- // load first image if current image is not within image set
- if (!imageContained) {
- loadAnnotateView(imageList[0].id);
- }
-
- scrollImageList();
+ gImageList = imageList.map(image => { return image.id });
}
/**
@@ -614,28 +568,20 @@ function calculateImageScale() {
/**
* Edit an annotation.
*
- * @param event
* @param annotationElem the element which stores the edit button of the annotation
* @param annotationId
*/
- function editAnnotation(event, annotationElem, annotationId) {
+ function editAnnotation(annotationElem, annotationId) {
annotationElem = $(annotationElem);
let annotationTypeId = annotationElem.data('annotationtypeid');
$('#annotation_type_id').val(annotationTypeId);
handleAnnotationTypeChange();
- globals.editedAnnotationsId = annotationId;
- globals.editActiveContainer.removeClass('hidden');
+ gEditAnnotationId = annotationId;
+ $('#edit_active').removeClass('hidden');
- if (event !== undefined) {
- // triggered using an event handler
- event.preventDefault();
- }
$('.js_feedback').stop().addClass('hidden');
- var params = {
- annotation_id: annotationId
- };
- var annotationData = annotationElem.data('vector');
+ let annotationData = annotationElem.data('vector');
if (annotationData === undefined) {
annotationData = annotationElem.data('escapedvector');
}
@@ -644,7 +590,7 @@ function calculateImageScale() {
$('.annotation').removeClass('alert-info');
annotationElem.parent().parent().addClass('alert-info');
- var notInImage = $('#not_in_image');
+ let notInImage = $('#not_in_image');
if (annotationData === null) {
// not in image
notInImage.prop('checked', true).change();
@@ -655,27 +601,11 @@ function calculateImageScale() {
notInImage.prop('checked', false).change();
-
tool.reloadSelection(annotationId, annotationData);
$('#concealed').prop('checked', annotationElem.data('concealed')).change();
$('#blurred').prop('checked', annotationElem.data('blurred')).change();
}
- /**
- * Get the image list from all .annotate_image_link within #image_list.
- */
- function getImageList() {
- var imageList = [];
- $('#image_list').find('.annotate_image_link').each(function(key, elem) {
- var imageId = parseInt($(elem).data('imageid'));
- if (imageList.indexOf(imageId) === -1) {
- imageList.push(imageId);
- }
- });
-
- return imageList;
- }
-
/**
* Validate a vector.
*
@@ -770,7 +700,7 @@ function calculateImageScale() {
if ($('#not_in_image').is(':checked')) {
// hide the coordinate selection.
- tool.resetSelection(true);
+ resetSelection();
coordinate_table.hide();
} else {
coordinate_table.show();
@@ -799,8 +729,8 @@ function calculateImageScale() {
*/
function handleSelection(event) {
calculateImageScale();
- var cH = $('#crosshair-h'), cV = $('#crosshair-v');
- var position = globals.image.offset();
+ let cH = $('#crosshair-h'), cV = $('#crosshair-v');
+ let position = globals.image.offset();
if (event.pageX > position.left &&
event.pageX < position.left + globals.image.width() &&
event.pageY > position.top &&
@@ -835,8 +765,6 @@ function calculateImageScale() {
/**
* Handle a resize event of the window.
- *
- * @param event
*/
function handleResize() {
tool.cancelSelection();
@@ -848,38 +776,36 @@ function calculateImageScale() {
* Load the annotation view for another image.
*
* @param imageId
- * @param fromHistory
+ * @param restoreAnnotation an annotation to restore
*/
- function loadAnnotateView(imageId, fromHistory) {
- globals.editedAnnotationsId = undefined;
+ async function loadAnnotateView(imageId, restoreAnnotation) {
+ // load first image if current image is not within image set
+ if (!gImageList.includes(imageId)) {
+ console.log('Loading first image because ' + imageId + ' is not contained in the selection');
+ imageId = gImageList[0];
+ }
+ gEditAnnotationId = undefined;
imageId = parseInt(imageId);
if (gImageList.indexOf(imageId) === -1) {
console.log(
- 'skiping request to load image ' + imageId +
+ 'skipping request to load annotation view for image ' + imageId +
' as it is not in current image list.');
return;
}
- var noAnnotations = $('#no_annotations');
- var notInImage = $('#not_in_image');
- var existingAnnotations = $('#existing_annotations');
- var loading = $('#annotations_loading');
+ let noAnnotations = $('#no_annotations');
+ let existingAnnotations = $('#existing_annotations');
+ let loading = $('#annotations_loading');
existingAnnotations.addClass('hidden');
noAnnotations.addClass('hidden');
loading.removeClass('hidden');
$('#annotation_type_id').val(gAnnotationType);
- displayImage(imageId);
- if (!$('#keep_selection').prop('checked')) {
- $('#concealed').prop('checked', false);
- $('#blurred').prop('checked', false);
- }
- scrollImageList();
$('.annotate_image_link').removeClass('active');
- var link = $('#annotate_image_link_' + imageId);
+ let link = $('#annotate_image_link_' + imageId);
link.addClass('active');
$('#active_image_name').text(link.text().trim());
let next_image_id = gImageList[gImageList.indexOf(imageId) + 1];
@@ -888,38 +814,50 @@ function calculateImageScale() {
}
$('#next-image-id').attr('value', next_image_id || '');
$('#delete-image-form').attr('action', DELETE_IMAGE_URL.replace('%s', imageId));
- tool.restoreSelection(false);
- if (fromHistory !== true) {
- history.pushState({
- imageId: imageId
- }, document.title, '/annotations/' + imageId + '/');
- }
+ tool.clear();
- let handleNewAnnotations = function() {
- // image is in cache.
- globals.currentAnnotationsOfSelectedType = gCurrentAnnotations.filter(function(e) {
- return e.annotation_type.id === gAnnotationType;
- });
- loading.addClass('hidden');
- displayExistingAnnotations(gCurrentAnnotations);
- tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType);
+ await displayImage(imageId);
+ let loadImages = preloadImages();
- if (globals.restoreSelection !== undefined) {
- tool.restoreSelection();
- } else {
+ if ($('#keep_selection').prop('checked') && restoreAnnotation) {
+ if (restoreAnnotation.vector === null) {
+ // not in image
tool.resetSelection();
+ $('#not_in_image').prop('checked', true);
+ $('#coordinate_table').hide();
+ setupCBCheckboxes();
+ } else {
+ let annotation_type = $('#annotation_type_id').children('[value=' + restoreAnnotation.annotation_type_id + ']').data();
+ let vector_type = annotation_type.vectorType;
+ let node_count = annotation_type.nodeCount;
+ tool.restoreSelection({
+ vector_type: vector_type,
+ node_count: node_count,
+ vector: restoreAnnotation.vector,
+ });
}
- };
+ } else {
+ resetSelection();
+ }
+
+ try {
+ // load existing annotations for this image
+ await loadAnnotations(imageId);
- // load existing annotations for this image
- loadAnnotations(imageId, handleNewAnnotations);
+ loading.addClass('hidden');
+ displayExistingAnnotations(gCurrentAnnotations);
+ tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType);
+ } catch {
+ console.log("Unable to load annotations for image" + imageId);
+ }
+ await loadImages;
}
/**
- * Load the image list from tye server applying a new filter.
+ * Get the image list from the server applying a new filter.
*/
- function loadImageList() {
+ async function getImageList() {
let filterElem = $('#filter_annotation_type');
let filter = filterElem.val();
let params = {
@@ -933,24 +871,25 @@ function calculateImageScale() {
handleAnnotationTypeChange();
}
- $.ajax(API_IMAGES_BASE_URL + 'imageset/load/?' + $.param(params), {
- type: 'GET',
- headers: gHeaders,
- dataType: 'json',
- success: function(data, textStatus, jqXHR) {
- if (data.image_set.images.length === 0) {
- // redirect to image set view.
- displayFeedback($('#feedback_image_set_empty'));
- filterElem.val('').change();
- return;
- }
- displayImageList(data.image_set.images);
- },
- error: function() {
- $('.annotate_button').prop('disabled', false);
- displayFeedback($('#feedback_connection_error'));
+ $('.annotate_button').prop('disabled', true);
+ try {
+ let response = await fetch(API_IMAGES_BASE_URL + 'imageset/load/?' + $.param(params), {
+ method: 'GET',
+ headers: gHeaders,
+ });
+ let data = await response.json();
+ if (data.image_set.images.length === 0) {
+ // set is empty, reset filter
+ displayFeedback($('#feedback_image_set_empty'));
+ filterElem.val('').change();
+ } else {
+ return data.image_set.images;
}
- });
+ } catch {
+ displayFeedback($('#feedback_connection_error'));
+ } finally {
+ $('.annotate_button').prop('disabled', false);
+ }
}
/**
@@ -958,13 +897,13 @@ function calculateImageScale() {
*
* @param imageId
*/
- function loadImageToCache(imageId) {
+ async function loadImageToCache(imageId) {
imageId = parseInt(imageId);
if (gImageList.indexOf(imageId) === -1) {
console.log(
- 'skiping request to load image ' + imageId +
- ' as it is not in current image list.');
+ 'skipping request to load image ' + imageId +
+ ' to cache as it is not in current image list.');
return;
}
@@ -982,47 +921,42 @@ function calculateImageScale() {
* Load the annotations of an image to the cache if they are not in it already.
*
* @param imageId
- * @param cb callback for success
*/
- function loadAnnotations(imageId, cb) {
+ async function loadAnnotations(imageId) {
imageId = parseInt(imageId);
if (gImageList.indexOf(imageId) === -1) {
- console.log(
- 'skiping request to load annotations of image ' + imageId +
- ' as it is not in current image list.');
- return;
+ throw new Error(
+ 'skipping request to load annotations of image ' + imageId +
+ ' as it is not in current image list.');
}
- var params = {
+ let params = {
image_id: imageId
};
- $.ajax(API_ANNOTATIONS_BASE_URL + 'annotation/load/?' + $.param(params), {
- type: 'GET',
+ let response = await fetch(API_ANNOTATIONS_BASE_URL + 'annotation/load/?' + $.param(params), {
+ method: 'GET',
headers: gHeaders,
- dataType: 'json',
- success: function(data) {
- // save the current annotations
- gCurrentAnnotations = data.annotations;
- console.log("Saving annotations for", imageId);
- cb();
- },
- error: function() {
- console.log("Unable to load annotations for image" + imageId);
- }
});
+ let data = await response.json();
+ // save the current annotations
+ gCurrentAnnotations = data.annotations;
+ globals.currentAnnotationsOfSelectedType = gCurrentAnnotations.filter(function (e) {
+ return e.annotation_type.id === gAnnotationType;
+ });
+ console.log("Fetched annotations for", imageId);
}
/**
* Load the previous or the next image
*
* @param offset integer to add to the current image index
+ * @return id of the requested image
*/
- function loadAdjacentImage(offset) {
- var imageIndex = gImageList.indexOf(gImageId);
+ async function loadAdjacentImage(offset) {
+ let imageIndex = gImageList.indexOf(gImageId);
if (imageIndex < 0) {
- console.log('current image is not referenced from page!');
- return;
+ throw new Error('current image is not referenced from page!')
}
imageIndex += offset;
@@ -1032,21 +966,26 @@ function calculateImageScale() {
while (imageIndex > imageIndex.length) {
imageIndex -= imageIndex.length;
}
-
- loadAnnotateView(gImageList[imageIndex]);
+ gImageId = gImageList[imageIndex];
+ return gImageList[imageIndex];
}
/**
* Preload next and previous images to cache.
*/
- function preloadImages() {
- var keepImages = [];
- for (var imageId = gImageId - PRELOAD_BACKWARD;
- imageId <= gImageId + PRELOAD_FORWARD;
- imageId++) {
- keepImages.push(imageId);
- loadImageToCache(imageId);
+ async function preloadImages() {
+ let keepImages = [];
+ let cacheLoadings = [];
+ let currentImageIndex = gImageList.indexOf(gImageId);
+ for (let index = currentImageIndex - PRELOAD_BACKWARD;
+ index <= currentImageIndex + PRELOAD_FORWARD;
+ index++) {
+ if (gImageList[index] !== undefined) {
+ keepImages.push(gImageList[index]);
+ cacheLoadings.push(loadImageToCache(gImageList[index]));
+ }
}
+ await Promise.all(cacheLoadings);
pruneImageCache(keepImages);
}
@@ -1056,7 +995,7 @@ function calculateImageScale() {
* @param keep Array of the image ids which should be kept in the cache.
*/
function pruneImageCache(keep) {
- for (var imageId in gImageCache) {
+ for (let imageId in gImageCache) {
imageId = parseInt(imageId);
if (gImageCache[imageId] !== undefined && keep.indexOf(imageId) === -1) {
delete gImageCache[imageId];
@@ -1066,13 +1005,14 @@ function calculateImageScale() {
/**
* Scroll image list to make current image visible.
+ * @param imageId id of the image to scroll to
*/
- function scrollImageList() {
- var imageLink = $('#annotate_image_link_' + gImageId);
- var list = $('#image_list');
+ function scrollImageList(imageId) {
+ let imageLink = $('#annotate_image_link_' + imageId);
+ let list = $('#image_list');
- var offset = list.offset().top;
- var linkTop = imageLink.offset().top;
+ let offset = list.offset().top;
+ let linkTop = imageLink.offset().top;
// link should be (roughly) in the middle of the element
offset += parseInt(list.height() / 2);
@@ -1097,7 +1037,7 @@ function calculateImageScale() {
if (!$('#draw_annotations').is(':checked'))
return;
- var position = globals.image.offset();
+ let position = globals.image.offset();
if (event.pageX > position.left && event.pageX < position.left + globals.image.width() &&
event.pageY > position.top && event.pageY < position.top + globals.image.height())
{
@@ -1105,9 +1045,9 @@ function calculateImageScale() {
displayFeedback($('#feedback_annotation_type_missing'));
return;
}
- globals.mouseDownX = Math.round((event.pageX - position.left) * globals.imageScaleWidth);
- globals.mouseDownY = Math.round((event.pageY - position.top) * globals.imageScaleHeight);
- tool.handleMouseDown(event);
+ let x = Math.round((event.pageX - position.left) * globals.imageScaleWidth);
+ let y = Math.round((event.pageY - position.top) * globals.imageScaleHeight);
+ tool.handleMouseDown(event, x, y);
}
}
@@ -1115,35 +1055,57 @@ function calculateImageScale() {
if (!$('#draw_annotations').is(':checked'))
return;
- var position = globals.image.offset();
- globals.mouseUpX = Math.round((event.pageX - position.left)/* * globals.imageScaleWidth*/);
- globals.mouseUpY = Math.round((event.pageY - position.top)/* * globals.imageScaleHeight*/);
+ let position = globals.image.offset();
+ let x = Math.round((event.pageX - position.left)/* * globals.imageScaleWidth*/);
+ let y = Math.round((event.pageY - position.top)/* * globals.imageScaleHeight*/);
if (event.pageX > position.left && event.pageX < position.left + globals.image.width() &&
event.pageY > position.top && event.pageY < position.top + globals.image.height()) {
- tool.handleMouseUp(event);
+ tool.handleMouseUp(event, x, y);
}
}
// handle DEL key press
- function handleDelete(event) {
- if (globals.editedAnnotationsId === undefined)
+ async function handleDelete() {
+ if (gEditAnnotationId === undefined)
return;
- deleteAnnotation(event, globals.editedAnnotationsId);
+ await deleteAnnotation(gEditAnnotationId);
}
function selectAnnotationType(annotationTypeNumber) {
- var annotationTypeId = '#annotation_type_' + annotationTypeNumber;
- var option = $(annotationTypeId);
+ let annotationTypeId = '#annotation_type_' + annotationTypeNumber;
+ let option = $(annotationTypeId);
if(option.length) {
$('#annotation_type_id').val(option.val());
}
handleAnnotationTypeChange();
}
+ async function updateFilter() {
+ handleAnnotationTypeChange();
+ let imageList = await getImageList();
+ await displayImageList(imageList);
+ if (!gImageList.includes(gImageId)) {
+ gImageId = gImageList[0];
+ }
+ scrollImageList(gImageId);
+ await loadAnnotateView(gImageId);
+ }
+
+ /**
+ * Change the image that is currently shown, i.e. update the image list, the displayed image and the annotation view
+ * @param imageId the id of the image to change to
+ * @param annotation the annotation to restore at the next image if the option is selected. undefined for no restoring
+ */
+ async function changeImage(imageId, annotation) {
+ window.history.replaceState(null, document.title, ANNOTATE_URL.replace('%s', imageId));
+ scrollImageList(imageId);
+ await loadAnnotateView(imageId, annotation);
+ }
+
- $(function() {
+ $(document).ready(function() {
let get_params = decodeURIComponent(window.location.search.substring(1)).split('&');
let editAnnotationId = undefined;
for (let i = 0; i < get_params.length; i++) {
@@ -1153,7 +1115,6 @@ function calculateImageScale() {
break;
}
}
- globals.editActiveContainer = $('#edit_active');
globals.image = $('#image');
globals.drawAnnotations = $('#draw_annotations').is(':checked');
gMousepos = $('#mousepos');
@@ -1167,15 +1128,17 @@ function calculateImageScale() {
"Content-Type": 'application/json',
"X-CSRFTOKEN": gCsrfToken
};
- gImageList = getImageList();
- loadAnnotationTypeList();
- preloadImages();
- scrollImageList();
// W3C standards do not define the load event on images, we therefore need to use
// it from window (this should wait for all external sources including images)
$(window).on('load', function() {
- initTool();
+ setTool();
+ getImageList()
+ .then(displayImageList)
+ .then(r => { scrollImageList(gImageId) })
+ .then(r => { loadAnnotateView(gImageId) })
+ .then(r => { preloadImages() });
+ loadAnnotationTypeList();
}());
$('.annotation_value').on('input', function() {
@@ -1183,8 +1146,8 @@ function calculateImageScale() {
});
$('#not_in_image').on('change', handleNotInImageToggle);
handleNotInImageToggle();
- $('select#filter_annotation_type').on('change', loadImageList);
- $('#filter_update_btn').on('click', loadImageList);
+ $('select#filter_annotation_type').on('change', updateFilter);
+ $('#filter_update_btn').on('click', updateFilter);
$('select').on('change', function() {
document.activeElement.blur();
});
@@ -1196,73 +1159,89 @@ function calculateImageScale() {
handleMouseClick(e);
});
$('#cancel_edit_button').click(function() {
- tool.resetSelection(true);
- });
- $('#save_button').click(createAnnotation);
- $('#reset_button').click(function() {
- tool.resetSelection(true);
+ resetSelection();
});
- $('#last_button').click(function(event) {
+ $('#save_button').click(async function (event) {
event.preventDefault();
- createAnnotation(undefined, function() {
- loadAdjacentImage(-1);
- }, true, true);
- if (tool instanceof BoundingBoxes) {
- tool.cancelSelection();
+ try {
+ let annotation = getValidAnnotation();
+ tool.clear();
+ resetSelection();
+ await createAnnotation(annotation);
+ tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType);
+ displayExistingAnnotations(gCurrentAnnotations);
+ } catch (e) {
+ console.log(e);
}
});
- $('#back_button').click(function(event) {
- event.preventDefault();
- if (tool instanceof BoundingBoxes) {
- tool.cancelSelection();
- }
- loadAdjacentImage(-1);
+ $('#reset_button').click(function() {
+ resetSelection();
});
- $('#skip_button').click(function(event) {
- event.preventDefault();
- if (tool instanceof BoundingBoxes) {
- tool.cancelSelection();
+ $('#last_button').click(async function (event) {
+ try {
+ let annotation = getValidAnnotation(true);
+ if (await createAnnotation(annotation)) {
+ let imageId = await loadAdjacentImage(-1);
+ await changeImage(imageId, annotation);
+ } else {
+ tool.clear();
+ resetSelection();
+ tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType);
+ displayExistingAnnotations(gCurrentAnnotations);
+ }
+ } catch (e) {
+ console.log(e);
}
- loadAdjacentImage(1);
});
- $('#next_button').click(function(event) {
- event.preventDefault();
- createAnnotation(undefined, function() {
- loadAdjacentImage(1);
- }, true, true);
- if (tool instanceof BoundingBoxes) {
- tool.cancelSelection();
+ $('#back_button').click(async function (event) {
+ let imageId = await loadAdjacentImage(-1);
+ await changeImage(imageId);
+ });
+ $('#skip_button').click(async function (event) {
+ let imageId = await loadAdjacentImage(1);
+ await changeImage(imageId);
+ });
+ $('#next_button').click(async function (event) {
+ try {
+ let annotation = getValidAnnotation(true);
+ if (await createAnnotation(annotation)) {
+ let imageId = await loadAdjacentImage(1);
+ await changeImage(imageId, annotation);
+ } else {
+ tool.clear();
+ resetSelection();
+ tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType);
+ displayExistingAnnotations(gCurrentAnnotations);
+ }
+ } catch (e) {
+ console.log(e);
}
});
$('.js_feedback').mouseover(function() {
$(this).addClass('hidden');
});
- $('.annotate_image_link').click(function(event) {
- event.preventDefault();
- loadAnnotateView($(this).data('imageid'));
+ $('.annotate_image_link').click(async function(event) {
+ event.preventDefault(); // do not let the browser load the link
+ await changeImage($(this).data('imageid'));
});
// annotation buttons
$('.annotation_edit_button').each(function(key, elem) {
elem = $(elem);
elem.click(function(event) {
- editAnnotation(event, this, parseInt(elem.data('annotationid')));
+ editAnnotation(this, parseInt(elem.data('annotationid')));
});
});
$('.annotation_delete_button').each(function(key, elem) {
elem = $(elem);
- elem.click(function(event) {
- deleteAnnotation(event, parseInt(elem.data('annotationid')));
+ elem.click(async function(event) {
+ event.preventDefault(); // the button is a link, don't follow it
+ await deleteAnnotation(parseInt(elem.data('annotationid')));
});
});
$(document).on('mousemove touchmove', handleSelection);
$(window).on('resize', handleResize);
- window.onpopstate = function(event) {
- if (event.state !== undefined && event.state !== null && event.state.imageId !== undefined) {
- loadAnnotateView(event.state.imageId, true);
- }
- };
// attach listeners for mouse events
$(document).on('mousedown.annotation_edit', handleMouseDown);
@@ -1275,7 +1254,13 @@ function calculateImageScale() {
gShiftDown = true;
break;
case 27: // Escape
- tool.handleEscape();
+ if (gAnnotationType === 4) {
+ // special case multiline: close drawing
+ tool.closeDrawing();
+ } else {
+ // normally, just delete the selection
+ resetSelection();
+ }
break;
case 73: //i
if(gShiftDown) {
@@ -1337,7 +1322,7 @@ function calculateImageScale() {
break;
}
});
- $(document).keyup(function(event) {
+ $(document).keyup(async function(event) {
switch (event.keyCode){
case 16: // Shift
gShiftDown = false;
@@ -1364,7 +1349,7 @@ function calculateImageScale() {
$('#save_button').click();
break;
case 46: //'DEL'
- handleDelete(event);
+ await handleDelete();
break;
case 66: //b
$('#blurred').click();
diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js
index 272e302a..1b99154d 100644
--- a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js
+++ b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js
@@ -1,21 +1,17 @@
// JS file for bounding box internals
class BoundingBoxes {
- constructor(annotationTypeId, noSelection) {
- this.initialized = false;
+ constructor(annotationTypeId) {
this.selection = undefined;
this.vector_type = 1;
if (globals.image === '') {
globals.image = $('#image');
}
this.annotationTypeId = annotationTypeId;
- if (!noSelection) {
- this.initSelection();
- }
+ this.resetSelection();
}
drawExistingAnnotations(annotations, color) {
- this.clear();
calculateImageScale();
color = color || globals.stdColor;
let colors = [];
@@ -36,11 +32,11 @@ class BoundingBoxes {
}
// clear all boxes
- var boundingBoxes = document.getElementById('boundingBoxes');
+ let boundingBoxes = document.getElementById('boundingBoxes');
- for (var a in annotations) {
+ for (let a in annotations) {
- var annotation = annotations[a];
+ let annotation = annotations[a];
let color = colors[a];
if (annotation.annotation_type.id !== this.annotationTypeId) {
continue;
@@ -49,7 +45,7 @@ class BoundingBoxes {
continue;
}
- var boundingBox = document.createElement('div');
+ let boundingBox = document.createElement('div');
boundingBox.setAttribute('class', 'boundingBox');
boundingBox.setAttribute('id', 'boundingBox' + annotation.id);
$(boundingBox).data('annotationid', annotation.id);
@@ -80,44 +76,22 @@ class BoundingBoxes {
}
}
- unsetHighlightColor(id) {
- let highlightBox;
+ unsetHighlightColor() {
for (let box of $('.boundingBox')) {
- if ($(box).data('annotationid') === id) {
- highlightBox = box;
- break;
- }
- }
- if (highlightBox) {
- $(highlightBox).css({
+ $(box).css({
'border': '2px solid ' + globals.stdColor
});
}
}
clear() {
- var boundingBoxes = document.getElementById('boundingBoxes');
+ let boundingBoxes = document.getElementById('boundingBoxes');
while (boundingBoxes.firstChild) {
boundingBoxes.removeChild(boundingBoxes.firstChild);
}
- }
- /**
- * Initialize the selection.
- */
- initSelection() {
- this.initialized = true;
-
- this.selection = globals.image.imgAreaSelect({
- instance: true,
- show: true,
- minHeight: 2,
- minWidth: 2,
- onSelectChange: this.updateAnnotationFields,
- resizeMargin: 3
- });
- this.selection.cancelSelection();
+ this.cancelSelection();
}
/**
@@ -127,7 +101,8 @@ class BoundingBoxes {
this.setHighlightColor(annotationId);
this.selection = globals.image.imgAreaSelect({
instance: true,
- show: true
+ show: true,
+ onSelectChange: this.updateAnnotationFields,
});
if (!annotationData) {
annotationData = {
@@ -156,40 +131,34 @@ class BoundingBoxes {
/**
* Delete current selection.
*/
- resetSelection(abortEdit) {
- this.unsetHighlightColor(globals.editedAnnotationsId);
+ resetSelection() {
+ this.unsetHighlightColor();
$('.annotation_value').val(0);
- if (this.selection !== undefined) {
- this.selection.cancelSelection();
- }
-
- globals.editedAnnotationsId = undefined;
- $('.annotation').removeClass('alert-info');
- globals.editActiveContainer.addClass('hidden');
+ this.selection = globals.image.imgAreaSelect({
+ instance: true,
+ show: true,
+ minHeight: 2,
+ minWidth: 2,
+ onSelectChange: this.updateAnnotationFields,
+ resizeMargin: 3
+ });
+ this.selection.cancelSelection();
}
/**
* Restore the selection.
+ * @param selection the selection to restore, object containing the vector
*/
- restoreSelection(reset) {
- if (!$('#keep_selection').prop('checked')) {
- return;
- }
- if (globals.restoreSelection !== undefined) {
- if (globals.restoreSelection === null) {
- $('#not_in_image').prop('checked', true);
- $('#coordinate_table').hide();
- } else {
- $('#x1Field').val(globals.restoreSelection.x1);
- $('#x2Field').val(globals.restoreSelection.x2);
- $('#y1Field').val(globals.restoreSelection.y1);
- $('#y2Field').val(globals.restoreSelection.y2);
- this.reloadSelection(0, globals.restoreSelection);
- }
- }
- if (reset !== false) {
- globals.restoreSelection = undefined;
+ restoreSelection(selection) {
+ let vector = selection.vector;
+ if (vector === null) {
+ } else {
+ $('#x1Field').val(vector.x1);
+ $('#x2Field').val(vector.x2);
+ $('#y1Field').val(vector.y1);
+ $('#y2Field').val(vector.y2);
+ this.reloadSelection(0, vector);
}
}
@@ -369,32 +338,32 @@ class BoundingBoxes {
this.clear();
}
- handleMouseDown(event) { }
- handleMouseUp(event) { }
- handleEscape() { this.resetSelection(true); }
+ handleMouseDown() { }
+ handleMouseUp() { }
+ closeDrawing() { }
- handleMouseClick(event) {
+ handleMouseClick(event, x, y) {
// get current annotation type id
- var annotationType = parseInt($('#annotation_type_id').val());
+ let annotationType = parseInt($('#annotation_type_id').val());
// array with all matching annotations
- var matchingAnnotations = [];
+ let matchingAnnotations = [];
- for (var a in globals.currentAnnotationsOfSelectedType) {
- var annotation = globals.currentAnnotationsOfSelectedType[a];
+ for (let a in globals.currentAnnotationsOfSelectedType) {
+ let annotation = globals.currentAnnotationsOfSelectedType[a];
if (annotation.annotation_type.id !== annotationType) {
continue;
}
if (annotation.vector === null)
continue;
- var left = annotation.vector.x1 / globals.imageScaleWidth;
- var right = annotation.vector.x2 / globals.imageScaleWidth;
- var top = annotation.vector.y1 / globals.imageScaleHeight;
- var bottom = annotation.vector.y2 / globals.imageScaleHeight;
+ let left = annotation.vector.x1 / globals.imageScaleWidth;
+ let right = annotation.vector.x2 / globals.imageScaleWidth;
+ let top = annotation.vector.y1 / globals.imageScaleHeight;
+ let bottom = annotation.vector.y2 / globals.imageScaleHeight;
// check if we clicked inside that annotation
- if (globals.mouseClickX >= left && globals.mouseClickX <= right && globals.mouseClickY >= top && globals.mouseClickY <= bottom) {
+ if (x >= left && x <= right && y >= top && y <= bottom) {
matchingAnnotations.push(annotation);
}
}
@@ -404,7 +373,7 @@ class BoundingBoxes {
return;
}
- annotation = matchingAnnotations[0];
+ let annotation = matchingAnnotations[0];
// a single match
if (matchingAnnotations.length === 1) {
@@ -419,8 +388,8 @@ class BoundingBoxes {
// 1. prefer annotation lying inside another one completely
// 2. prefer annotation, which left border is to the left of another ones
// 3. prefer annotation, which top border is above another ones
- for (var a1 in matchingAnnotations) {
- var annotation1 = matchingAnnotations[a1];
+ for (let a1 in matchingAnnotations) {
+ let annotation1 = matchingAnnotations[a1];
if (annotation.id === annotation1.id)
continue;
diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js
index 299e6b97..06538ba3 100644
--- a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js
+++ b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js
@@ -180,16 +180,10 @@ class Drawing {
}
}
-class Point {
+class Point extends Drawing {
constructor(parent, point, id, mutable, color) {
- /* Set fields */
- this.pointCounter = 1; // The number of points that are currently set
- this.id = id;
- this.name = "drawing" + id;
- this.parent = parent;
- this.mutable = mutable;
- this.color = color || globals.stdColor;
-
+ super(parent, point, id, mutable, color);
+ this.parent.removeLayer(this.name);
/* Define layer */
let l = {
name: this.name,
@@ -203,23 +197,6 @@ class Point {
this.parent.locked = false;
this.parent.updateAnnotationFields(point);
}
- /** Set the cursor to 'drag' or 'crosshair' in mouseover
- *
- * @param bool whether the dursor is in 'drag' style
- */
- setDragCursor(bool) {}
- setMutable(mutable) {}
- getPointTuples() {
- let l = this.parent.getLayer(this.name);
- return [[l.x, l.y]];
- }
- getPoints() {
- let l = this.parent.getLayer(this.name);
- return {x1: l.x, y1: l.y};
- }
- remove() {
- this.parent.removeLayer(this.name);
- }
}
class Line extends Drawing {
@@ -332,11 +309,11 @@ class Canvas {
}
}
- mouseTooClose() {
+ mouseTooClose(x, y) {
for (let drawing of this.drawings) {
let points = drawing.getPointTuples();
for (let point of points) {
- if (Math.abs(point[0] * globals.imageScaleWidth - globals.mouseDownX) < threshold && Math.abs(point[1] * globals.imageScaleHeight - globals.mouseDownY) < threshold) {
+ if (Math.abs(point[0] * globals.imageScaleWidth - x) < threshold && Math.abs(point[1] * globals.imageScaleHeight - y) < threshold) {
return true;
}
}
@@ -371,6 +348,11 @@ class Canvas {
for (let d of this.drawings) {
d.remove();
}
+ this.unsetAnnotationFields();
+ this.currentDrawing = undefined;
+ }
+
+ unsetAnnotationFields() {
switch (this.vector_type) {
case 2: // Point
this.updateAnnotationFields({x1: 0, y1: 0});
@@ -425,7 +407,8 @@ class Canvas {
}
drawExistingAnnotations(annotations, color) {
- this.clear();
+ let currentDrawing = this.currentDrawing;
+ this.currentDrawing = undefined;
color = color || globals.stdColor;
let colors = [];
if (color.constructor === Array) {
@@ -445,8 +428,6 @@ class Canvas {
for (let i in annotations) {
let annotation = annotations[i];
let color = colors[i];
- console.log(annotation);
- console.log(color);
if (annotation.annotation_type.id !== this.annotationTypeId) {
continue;
}
@@ -485,7 +466,14 @@ class Canvas {
console.log("Unknown vector type: " + annotation.annotation_type.vector_type);
}
}
- this.currentDrawing = undefined;
+ // completely restore the drawing from before drawing the existing annotations,
+ // especially the annotation fields are overwritten during the drawing
+ this.currentDrawing = currentDrawing;
+ if (currentDrawing) {
+ this.reloadSelection(currentDrawing.id);
+ } else {
+ this.unsetAnnotationFields();
+ }
}
updateAnnotationFields(drawing) {
@@ -532,93 +520,69 @@ class Canvas {
}
}
- handleEscape() {
+ closeDrawing() {
if (this.currentDrawing && this.annotationTypeId === 4 && !this.currentDrawing.closed) {
this.currentDrawing.close();
- } else {
- this.resetSelection(true);
}
}
- resetSelection(abortEdit) {
+ resetSelection() {
$('.annotation_value').val(0);
// Make every drawing immutable
for (let drawing of this.drawings) {
drawing.setMutable(false);
}
- globals.editedAnnotationsId = undefined;
- $('.annotation').removeClass('alert-info');
- globals.editActiveContainer.addClass('hidden');
- if (abortEdit === true) {
- if (this.old && this.currentDrawing) {
- this.currentDrawing.setPoints(this.old);
- this.old = undefined;
- } else if (this.currentDrawing) {
- this.currentDrawing.remove();
- }
- this.currentDrawing = undefined;
+ if (this.old && this.currentDrawing) {
+ this.currentDrawing.setPoints(this.old);
+ this.old = undefined;
+ } else if (this.currentDrawing) {
+ this.currentDrawing.remove();
}
+ this.currentDrawing = undefined;
}
cancelSelection() {
this.resetSelection(true)
}
- initSelection() {
- this.initialized = true;
- }
-
/**
* Restore the selection.
+ * @param selection the selection to restore, object containing vector, vector_type and node_count
*/
- restoreSelection(reset) {
- if (!$('#keep_selection').prop('checked')) {
- return;
- }
- if (globals.restoreSelection !== undefined) {
- if (globals.restoreSelection === null) {
- $('#not_in_image').prop('checked', true);
- $('#coordinate_table').hide();
- let concealed = $('#concealed');
- let concealedP = $('#concealed_p');
- let blurred = $('#blurred');
- let blurredP = $('#blurred_p');
- concealedP.hide();
- concealed.prop('checked', false);
- concealed.prop('disabled', true);
- blurredP.hide();
- blurred.prop('checked', false);
- blurred.prop('disabled', true);
- } else if (globals.restoreSelectionVectorType === 4) {
- // this is not implemented for multilines, so just do nothing
- this.old = undefined;
- } else {
- let vector = {};
- for (let key in globals.restoreSelection) {
- if (key[0] === "y") {
- vector[key] = globals.restoreSelection[key] / globals.imageScaleHeight;
- } else {
- vector[key] = globals.restoreSelection[key] / globals.imageScaleWidth;
- }
+ restoreSelection(selection) {
+ let vector = selection.vector;
+ let vectorType = selection.vector_type;
+ let nodeCount = selection.node_count;
+ if (vector === null) {
+ } else if (vectorType === 4) {
+ // this is not implemented for multilines, so just do nothing
+ this.old = undefined;
+ } else {
+ let scaledVector = {};
+ for (let key in vector) {
+ if (key[0] === "y") {
+ scaledVector[key] = vector[key] / globals.imageScaleHeight;
+ } else {
+ scaledVector[key] = vector[key] / globals.imageScaleWidth;
}
- this.updateAnnotationFields(globals.restoreSelection);
- switch(globals.restoreSelectionVectorType) {
- case 2: this.drawPoint(vector, 0, true); break;
- case 3: this.drawLine(vector, 0, true); break;
- case 5: if (globals.restoreSelectionNodeCount === 0) {
- this.drawArbitraryPolygon(vector, 0, true, true);
+ }
+ this.updateAnnotationFields(vector);
+ switch (vectorType) {
+ case 2:
+ this.drawPoint(scaledVector, 0, true);
+ break;
+ case 3:
+ this.drawLine(scaledVector, 0, true);
+ break;
+ case 5:
+ if (nodeCount === 0) {
+ this.drawArbitraryPolygon(scaledVector, 0, true, true);
} else {
- this.drawPolygon(vector, 0, true, globals.restoreSelectionNodeCount, true);
+ this.drawPolygon(scaledVector, 0, true, nodeCount, true);
}
- }
- this.reloadSelection(0);
- this.old = undefined;
}
- }
- if (reset !== false) {
- globals.restoreSelection = undefined;
- globals.restoreSelectionNodeCount = 0;
- globals.restoreSelectionVectorType = 1;
+ this.reloadSelection(0);
+ this.old = undefined;
}
}
@@ -660,7 +624,7 @@ class Canvas {
increaseSelectionSizeRight() {}
increaseSelectionSizeUp() {}
- handleMouseClick(event) {
+ handleMouseClick(event, x, y) {
let position = globals.image.offset();
if (!(event.pageX > position.left && event.pageX < position.left + globals.image.width() &&
event.pageY > position.top && event.pageY < position.top + globals.image.height()) &&
@@ -669,20 +633,20 @@ class Canvas {
}
}
- handleMouseDown() {
+ handleMouseDown(event, x, y) {
// Check if we are close enough to move the point, not draw a new drawing
// we use the variable locked which is checked when we can create a new line
- if (this.mouseTooClose()) {
+ if (this.mouseTooClose(x, y)) {
this.locked = true;
}
}
- handleMouseUp() {
+ handleMouseUp(event, x, y) {
if (this.inline && this.currentDrawing) {
// We are currently drawing a drawing
// and we clicked inside of the canvas:
// add a point
- this.currentDrawing.addPoint(globals.mouseUpX, globals.mouseUpY);
+ this.currentDrawing.addPoint(x, y);
} else if (this.locked) {
// we do not create a drawing because we are
// only moving an existing one
@@ -696,19 +660,19 @@ class Canvas {
this.inline = true;
switch (this.vector_type) {
case 2: // Point
- this.drawPoint({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true);
+ this.drawPoint({x1: x, y1: y}, 0, true);
break;
case 3: // Line
- this.drawLine({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true);
+ this.drawLine({x1: x, y1: y}, 0, true);
break;
case 4: // Multiline
- this.drawMultiline({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true);
+ this.drawMultiline({x1: x, y1: y}, 0, true);
break;
case 5: // Polygon
if (this.node_count === 0) {
- this.drawArbitraryPolygon({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true, false);
+ this.drawArbitraryPolygon({x1: x, y1: y}, 0, true, false);
} else {
- this.drawPolygon({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true, this.node_count, false);
+ this.drawPolygon({x1: x, y1: y}, 0, true, this.node_count, false);
}
break;
default:
diff --git a/imagetagger/imagetagger/annotations/templates/annotations/annotate.html b/imagetagger/imagetagger/annotations/templates/annotations/annotate.html
index 7f2a9c8c..fd7329c5 100644
--- a/imagetagger/imagetagger/annotations/templates/annotations/annotate.html
+++ b/imagetagger/imagetagger/annotations/templates/annotations/annotate.html
@@ -23,6 +23,10 @@
display: none;
}
{% endif %}
+ body {
+ /* this is necessary to avoid a jumping image when the scrollbar appears */
+ overflow-y: scroll;
+ }
{% endblock additional_annotation_css %}