Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = function(grunt) {
},
dist_admin: {
src: [
'public/javascripts/common/AiLabelIndicator.js',
'public/javascripts/Admin/src/*.js',
'public/javascripts/common/UtilitiesSidewalk.js',
'public/javascripts/common/Panomarker.js',
Expand All @@ -62,6 +63,7 @@ module.exports = function(grunt) {
},
dist_validate: {
src: [
'public/javascripts/common/AiLabelIndicator.js',
'public/javascripts/SVValidate/src/*.js',
'public/javascripts/SVValidate/src/data/*.js',
'public/javascripts/SVValidate/src/keyboard/*.js',
Expand All @@ -84,6 +86,7 @@ module.exports = function(grunt) {
},
dist_gallery: {
src: [
'public/javascripts/common/AiLabelIndicator.js',
'public/javascripts/Gallery/src/cards/*.js',
'public/javascripts/Gallery/src/data/*.js',
'public/javascripts/Gallery/src/filter/*.js',
Expand Down
6 changes: 4 additions & 2 deletions app/controllers/AdminController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ class AdminController @Inject() (
"label_type" -> label.labelType,
"severity" -> label.severity,
"correct" -> label.correct,
"high_quality_user" -> label.highQualityUser
"high_quality_user" -> label.highQualityUser,
"ai_generated" -> label.aiGenerated
)
)
}.seq
Expand Down Expand Up @@ -187,7 +188,8 @@ class AdminController @Inject() (
"has_validations" -> label.hasValidations,
"ai_validation" -> label.aiValidation.map(LabelValidationTable.validationOptions.get),
"expired" -> label.expired,
"high_quality_user" -> label.highQualityUser
"high_quality_user" -> label.highQualityUser,
"ai_generated" -> label.aiGenerated
)
)
}.seq
Expand Down
7 changes: 5 additions & 2 deletions app/formats/json/LabelFormats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ object LabelFormats {
(__ \ "validations").write[Map[String, Int]] and
(__ \ "tags").write[List[String]] and
(__ \ "low_quality_incomplete_stale_flags").write[(Boolean, Boolean, Boolean)] and
(__ \ "comments").write[Option[Seq[String]]]
(__ \ "comments").write[Option[Seq[String]]] and
(__ \ "ai_generated").write[Boolean]
)(unlift(LabelMetadata.unapply))

def validationLabelMetadataToJson(
Expand Down Expand Up @@ -95,6 +96,7 @@ object LabelFormats {
"ai_validation" -> labelMetadata.aiValidation.map(LabelValidationTable.validationOptions.get),
"tags" -> labelMetadata.tags,
"ai_tags" -> labelMetadata.aiTags,
"ai_generated" -> labelMetadata.aiGenerated,
"admin_data" -> adminData.map(ad =>
Json.obj(
"username" -> ad.username,
Expand Down Expand Up @@ -133,7 +135,8 @@ object LabelFormats {
"num_disagree" -> labelMetadata.validations("disagree"),
"num_unsure" -> labelMetadata.validations("unsure"),
"comments" -> labelMetadata.comments,
"tags" -> labelMetadata.tags
"tags" -> labelMetadata.tags,
"ai_generated" -> labelMetadata.aiGenerated
)
}

Expand Down
154 changes: 92 additions & 62 deletions app/models/label/LabelTable.scala

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion public/javascripts/Admin/src/Admin.GSVCommentView.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,17 @@ function AdminGSVCommentView(admin) {
}

function setLabel(labelMetadata) {
const isAiGenerated = labelMetadata['ai_generated'] === true;
var adminPanoramaLabel = AdminPanoramaLabel(labelMetadata['label_id'], labelMetadata['label_type'],
labelMetadata['canvas_x'], labelMetadata['canvas_y'], util.EXPLORE_CANVAS_WIDTH, util.EXPLORE_CANVAS_HEIGHT,
labelMetadata['heading'], labelMetadata['pitch'], labelMetadata['zoom']);
labelMetadata['heading'], labelMetadata['pitch'], labelMetadata['zoom'], labelMetadata['street_edge_id'],
labelMetadata['severity'], labelMetadata['tags'], isAiGenerated);
self.panorama.setLabel(adminPanoramaLabel);
if (isAiGenerated && self.panorama && self.panorama.labelMarkers && self.panorama.labelMarkers.length) {
const lastMarker = self.panorama.labelMarkers[self.panorama.labelMarkers.length - 1];
const indicator = lastMarker.marker.querySelector('.admin-ai-icon-marker');
ensureAiTooltip(indicator);
}
}

_init();
Expand Down
23 changes: 13 additions & 10 deletions public/javascripts/Admin/src/Admin.GSVLabelView.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,21 +384,23 @@ function AdminGSVLabelView(admin, source) {
* @private
*/
function _setAiValidationRow(aiValidation) {
// Remove any existing AI icon before adding a new one.
self.modalAiValidationHeader.find('.label-view-ai-icon').remove();

if (aiValidation) {
self.modalAiValidation.html(i18next.t('labelmap:ai-val-included', { aiVal: aiValidation.toLowerCase() }));
const normalizedAiVal = aiValidation.toLowerCase();
self.modalAiValidation.html(i18next.t('labelmap:ai-val-included', { aiVal: normalizedAiVal }));

// Create the AI icon.
let aiIcon = document.createElement('img')
aiIcon.className = 'label-view-ai-icon';
// Create the AI validation icon with the correct tooltip text.
const aiIcon = new Image();
aiIcon.src = '/assets/images/icons/ai-icon-transparent-small.png';
aiIcon.alt = 'AI indicator';
self.modalAiValidationHeader.append(aiIcon);

// Create the AI validation tooltip that we show in the header.
aiIcon.classList.add('label-view-ai-icon');
aiIcon.setAttribute('data-toggle', 'tooltip');
aiIcon.setAttribute('data-placement', 'top');
aiIcon.setAttribute('title', i18next.t('common:ai-disclaimer', { aiVal: aiValidation.toLowerCase() }));
$(aiIcon).tooltip('hide');
aiIcon.setAttribute('title', i18next.t('common:ai-disclaimer', { aiVal: normalizedAiVal }));
ensureAiTooltip(aiIcon);
self.modalAiValidationHeader.append(aiIcon);
} else {
self.modalAiValidation.html(i18next.t('common:none'));
}
Expand Down Expand Up @@ -565,10 +567,11 @@ function AdminGSVLabelView(admin, source) {
self.panorama.setPano(labelMetadata['gsv_panorama_id'], labelMetadata['heading'],
labelMetadata['pitch'], labelMetadata['zoom'], panoCallback);

const isAiGenerated = Boolean(labelMetadata['ai_generated']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You missed that you used Boolean() here. You should just be able to pass in labelMetadata['ai_generated'] directly to AdminPanoramaLabel() like we do for all the other parameters (you shouldn't need to check if it's equal to true either).

var adminPanoramaLabel = AdminPanoramaLabel(labelMetadata['label_id'], labelMetadata['label_type'],
labelMetadata['canvas_x'], labelMetadata['canvas_y'], util.EXPLORE_CANVAS_WIDTH, util.EXPLORE_CANVAS_HEIGHT,
labelMetadata['heading'], labelMetadata['pitch'], labelMetadata['zoom'], labelMetadata['street_edge_id'],
labelMetadata['severity'], labelMetadata['tags']);
labelMetadata['severity'], labelMetadata['tags'], isAiGenerated);
self.panorama.setLabel(adminPanoramaLabel);

self.validationCounts['Agree'] = labelMetadata['num_agree'];
Expand Down
5 changes: 3 additions & 2 deletions public/javascripts/Admin/src/Admin.Panorama.Label.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* @constructor
*/
function AdminPanoramaLabel(labelId, labelType, canvasX, canvasY, originalCanvasWidth, originalCanvasHeight,
heading, pitch, zoom, streetEdgeId, severity, tags) {
heading, pitch, zoom, streetEdgeId, severity, tags, aiGenerated = false) {
var self = { className: "AdminPanoramaLabel" };

/**
Expand All @@ -37,10 +37,11 @@ function AdminPanoramaLabel(labelId, labelType, canvasX, canvasY, originalCanvas
self.newSeverity = severity;
self.oldTags = tags;
self.newTags = tags;
self.aiGenerated = aiGenerated;
return this;
}

_init();

return self;
}
}
38 changes: 30 additions & 8 deletions public/javascripts/Admin/src/Admin.Panorama.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,17 +209,19 @@ function AdminPanorama(svHolder, buttonHolder, admin) {
var url = icons[label['label_type']];
var pos = getPosition(label['canvasX'], label['canvasY'], label['originalCanvasWidth'],
label['originalCanvasHeight'], label['zoom'], label['heading'], label['pitch']);
const panoMarker = new PanoMarker({
container: self.panoCanvas,
pano: self.panorama,
position: {heading: pos.heading, pitch: pos.pitch},
icon: url,
size: new google.maps.Size(20, 20),
anchor: new google.maps.Point(10, 10)
});
self.labelMarkers.push({
panoId: self.panorama.getPano(),
marker: new PanoMarker({
container: self.panoCanvas,
pano: self.panorama,
position: {heading: pos.heading, pitch: pos.pitch},
icon: url,
size: new google.maps.Size(20, 20),
anchor: new google.maps.Point(10, 10)
})
marker: panoMarker
});
attachAiIndicatorToMarker(panoMarker, label.aiGenerated);
return this;
}

Expand Down Expand Up @@ -275,6 +277,26 @@ function AdminPanorama(svHolder, buttonHolder, admin) {
};
}

function attachAiIndicatorToMarker(panoMarker, showIndicator, retryCount = 0) {
if (!showIndicator) return;

if (!panoMarker.marker_) {
if (retryCount < 5) {
setTimeout(() => attachAiIndicatorToMarker(panoMarker, showIndicator, retryCount + 1), 100);
}
return;
}

if (!panoMarker.marker_.querySelector('.admin-ai-icon-marker')) {
const indicator = AiLabelIndicator(['admin-ai-icon-marker'], 'top', true);
panoMarker.marker_.appendChild(indicator);
const $indicator = ensureAiTooltip(indicator);
// Fall back to parent events in case Google marker swallows child events.
$(panoMarker.marker_).on('mouseenter', () => $indicator.tooltip('show'));
$(panoMarker.marker_).on('mouseleave', () => $indicator.tooltip('hide'));
}
}

/**
* Calculates heading & position for placing this Label onto the pano from the same POV when the label was placed.
* @returns {{heading: number, pitch: number}}
Expand Down
22 changes: 19 additions & 3 deletions public/javascripts/Gallery/css/cards.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ with respect to the image holder. This allows the validation menu to be placed o
cursor: pointer;
position: relative;
overflow: hidden;
--gallery-marker-size: clamp(18px, 1.15vw, 26px);
}

.gallery-card:hover:after {
Expand Down Expand Up @@ -164,12 +165,27 @@ with respect to the image holder. This allows the validation menu to be placed o
color: black;
}

.label-icon-gallery {
--iconWidth: 1.1vw;
width: var(--iconWidth);
.gallery-marker-wrapper {
position: absolute;
width: var(--gallery-marker-size);
height: var(--gallery-marker-size);
pointer-events: none;
}

.gallery-marker-wrapper .label-icon-gallery {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 1;
}

.gallery-marker-wrapper .ai-icon-marker {
pointer-events: auto;
}


.card-validation-info {
display: flex;
align-items: center;
Expand Down
7 changes: 7 additions & 0 deletions public/javascripts/Gallery/css/expanded-view.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@
font-family: 'raleway-bold', sans-serif;
font-size: 1.5vw;
color: #2d2a3f;
display: flex;
align-items: center;
column-gap: 10px;
}

.gallery-expanded-view-header-label {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This CSS class isn't used anywhere, is it? I think that since you removed the icon from the header, all the edits from this file can likely be removed?

flex: 0 1 auto;
}

.gallery-expanded-view-pano {
Expand Down
19 changes: 19 additions & 0 deletions public/javascripts/Gallery/css/gallery.css
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,22 @@ We should allow the pointer events to pass through and behave the same way as th
.facial-feature {
fill: #2D2A3F;
}

.ai-icon-marker {
position: absolute;
width: clamp(12px, 0.75vw, 16px);
height: clamp(12px, 0.75vw, 16px);
z-index: 3;
pointer-events: auto;
cursor: pointer;
}

.ai-icon-marker-card {
top: -6px;
left: -5px;
}

.ai-icon-marker-expanded {
top: -6px;
left: -4px;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaand just missing the newline here. Most minor thing in the world lol. But I would strongly recommend looking through the "Files changed" section of any PR before asking for reviews, since that's what the reviewers will be looking at! Will help you catch all sorts of mistakes

29 changes: 24 additions & 5 deletions public/javascripts/Gallery/src/cards/Card.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ function Card (params, imageUrl, expandedView) {
correctness: undefined,
user_validation: undefined,
ai_validation: undefined,
tags: []
tags: [],
ai_generated: false
};

// Paths to label icon images.
Expand Down Expand Up @@ -76,6 +77,9 @@ function Card (params, imageUrl, expandedView) {
properties[attrName] = param[attrName];
}
}
if ("ai_generated" in param) {
properties.ai_generated = Boolean(param.ai_generated);
}
properties.original_canvas_x = param.canvas_x;
properties.original_canvas_y = param.canvas_y;
properties.val_counts = {
Expand All @@ -91,8 +95,6 @@ function Card (params, imageUrl, expandedView) {
// Place label icon.
labelIcon.src = iconImagePaths[getLabelType()];
labelIcon.classList.add("label-icon", "label-icon-gallery");
labelIcon.style.left = `calc(${100 * properties.original_canvas_x / (util.EXPLORE_CANVAS_WIDTH)}% - var(--iconWidth) / 2)`;
labelIcon.style.top = `calc(${100 * properties.original_canvas_y / (util.EXPLORE_CANVAS_HEIGHT)}% - var(--iconWidth) / 2)`;

// Create an element for the image in the card.
imageId = "label_id_" + properties.label_id;
Expand Down Expand Up @@ -132,7 +134,7 @@ function Card (params, imageUrl, expandedView) {
let cardValidationInfo = document.createElement('div');
cardValidationInfo.className = 'card-validation-info';
self.validationInfoDisplay = new ValidationInfoDisplay(
cardValidationInfo, properties.val_counts['Agree'], properties.val_counts['Disagree'], properties.ai_validation
cardValidationInfo, properties.val_counts['Agree'], properties.val_counts['Disagree'], properties.ai_validation, false
);
cardData.appendChild(cardValidationInfo);

Expand All @@ -145,7 +147,24 @@ function Card (params, imageUrl, expandedView) {
cardData.appendChild(cardTags);

// Append the overlays for label information on top of the image.
imageHolder.appendChild(labelIcon);
const markerLeftPercent = 100 * properties.original_canvas_x / (util.EXPLORE_CANVAS_WIDTH);
const markerTopPercent = 100 * properties.original_canvas_y / (util.EXPLORE_CANVAS_HEIGHT);
const markerWrapper = document.createElement('div');
markerWrapper.className = 'gallery-marker-wrapper';
markerWrapper.style.left = `calc(${markerLeftPercent}% - var(--gallery-marker-size) / 2)`;
markerWrapper.style.top = `calc(${markerTopPercent}% - var(--gallery-marker-size) / 2)`;
markerWrapper.appendChild(labelIcon);
if (properties.ai_generated) {
const aiIndicator = AiLabelIndicator(['ai-icon', 'ai-icon-marker', 'ai-icon-marker-card']);
markerWrapper.appendChild(aiIndicator);
$(aiIndicator)
.tooltip({
template: '<div class="tooltip ai-tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
container: 'body'
})
.tooltip('hide');
}
imageHolder.appendChild(markerWrapper);
imageHolder.appendChild(panoImage);
card.appendChild(cardInfo);
validationMenu = new ValidationMenu(self, $(imageHolder), properties, expandedView, false);
Expand Down
18 changes: 14 additions & 4 deletions public/javascripts/Gallery/src/cards/CardContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,26 @@ function CardContainer(uiCardContainer, initialFilters) {
// Creates the ExpandedView object in the DOM element currently present.
expandedView = new ExpandedView($('.gallery-expanded-view'));
// Add the click event for opening the ExpandedView when a card is clicked.
sg.ui.cardContainer.holder.on('click', '.static-gallery-image, .additional-count', (event) => {
sg.ui.cardContainer.holder.on('click', '.static-gallery-image, .additional-count, .ai-icon-marker-card', (event) => {
$('.gallery-expanded-view').css('display', 'flex');
$('.grid-container').css("grid-template-columns", "1fr 5fr");
// If the user clicks on the image body in the card, just use the provided id.
// If they click the AI icon, use the image id from the same card.
// Otherwise, the user will have clicked on an existing "+n" icon on the card, meaning we need to acquire
// the cardId from the card-tags DOM element (as well as perform an additional prepend to put the ID in
// the correct form).
let clickedImage = event.target.classList.contains("static-gallery-image")
let cardId = clickedImage ? event.target.id :
"label_id_" + event.target.closest(".card-tags").id;
let clickedImage = event.target.classList.contains("static-gallery-image");
let cardId;
if (event.target.classList.contains("ai-icon-marker-card")) {
let imageHolder = event.target.closest(".image-holder");
let parentImage = imageHolder ? imageHolder.querySelector(".static-gallery-image") : null;
cardId = parentImage ? parentImage.id : null;
} else if (clickedImage) {
cardId = event.target.id;
} else {
cardId = "label_id_" + event.target.closest(".card-tags").id;
}
if (!cardId) return;
// Sets/Updates the label being displayed in the expanded view.
expandedView.updateCardIndex(findCardIndex(cardId));
});
Expand Down
Loading