Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions app/formats/json/ExploreFormats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ object ExploreFormats {
)
case class LabelSubmission(
gsvPanoramaId: String,
gsvCaptureDate: Option[String],
auditTaskId: Int,
labelType: String,
deleted: Boolean,
Expand Down Expand Up @@ -242,6 +243,7 @@ object ExploreFormats {

implicit val labelSubmissionReads: Reads[LabelSubmission] = (
(JsPath \ "gsv_panorama_id").read[String] and
(JsPath \ "gsv_capture_date").readNullable[String] and
(JsPath \ "audit_task_id").read[Int] and
(JsPath \ "label_type").read[String] and
(JsPath \ "deleted").read[Boolean] and
Expand Down
2 changes: 2 additions & 0 deletions app/formats/json/LabelFormats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ object LabelFormats {
(__ \ "mission_id").write[Int] and
(__ \ "user_id").write[String] and
(__ \ "gsv_panorama_id").write[String] and
(__ \ "gsv_capture_date").writeNullable[String] and
(__ \ "label_type_id").write[Int] and
(__ \ "deleted").write[Boolean] and
(__ \ "temporary_label_id").write[Int] and
Expand Down Expand Up @@ -186,6 +187,7 @@ object LabelFormats {
Json.obj(
"labelId" -> label.labelData.labelId,
"labelType" -> label.labelType,
"panoCaptureDate" -> label.labelData.gsvCaptureDate,
"panoId" -> label.labelData.gsvPanoramaId,
"panoLat" -> label.panoLat,
"panoLng" -> label.panoLng,
Expand Down
4 changes: 3 additions & 1 deletion app/models/label/LabelTable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ case class Label(
missionId: Int,
userId: String,
gsvPanoramaId: String,
gsvCaptureDate: Option[String],
labelTypeId: Int,
deleted: Boolean,
temporaryLabelId: Int,
Expand Down Expand Up @@ -229,6 +230,7 @@ class LabelTableDef(tag: slick.lifted.Tag) extends Table[Label](tag, "label") {
def missionId: Rep[Int] = column[Int]("mission_id")
def userId: Rep[String] = column[String]("user_id")
def gsvPanoramaId: Rep[String] = column[String]("gsv_panorama_id")
def gsvCaptureDate: Rep[Option[String]] = column[Option[String]]("gsv_capture_date")
def labelTypeId: Rep[Int] = column[Int]("label_type_id")
def deleted: Rep[Boolean] = column[Boolean]("deleted")
def temporaryLabelId: Rep[Int] = column[Int]("temporary_label_id")
Expand All @@ -244,7 +246,7 @@ class LabelTableDef(tag: slick.lifted.Tag) extends Table[Label](tag, "label") {
def description: Rep[Option[String]] = column[Option[String]]("description")
def tags: Rep[List[String]] = column[List[String]]("tags", O.Default(List()))

def * = (labelId, auditTaskId, missionId, userId, gsvPanoramaId, labelTypeId, deleted, temporaryLabelId, timeCreated,
def * = (labelId, auditTaskId, missionId, userId, gsvPanoramaId, gsvCaptureDate, labelTypeId, deleted, temporaryLabelId, timeCreated,
tutorial, streetEdgeId, agreeCount, disagreeCount, unsureCount, correct, severity, temporary, description,
tags) <> ((Label.apply _).tupled, Label.unapply)

Expand Down
1 change: 1 addition & 0 deletions app/service/ExploreService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ class ExploreServiceImpl @Inject() (
missionId = missionId,
userId = userId,
gsvPanoramaId = label.gsvPanoramaId,
gsvCaptureDate = label.gsvCaptureDate,
labelTypeId = LabelTypeEnum.labelTypeToId(label.labelType),
deleted = label.deleted,
temporaryLabelId = label.temporaryLabelId,
Expand Down
9 changes: 9 additions & 0 deletions conf/evolutions/default/276.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# --- !Ups
ALTER TABLE label ADD COLUMN gsv_capture_date TEXT;
UPDATE label
SET gsv_capture_date = gsv_data.capture_date
FROM gsv_data
WHERE label.gsv_panorama_id = gsv_data.gsv_panorama_id;

# --- !Downs
ALTER TABLE label DROP COLUMN gsv_capture_date;
6 changes: 6 additions & 0 deletions public/javascripts/SVLabel/src/SVLabel/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ function Main (params) {
});
logPageFocus();

svl.overlay = new google.maps.OverlayView();
svl.overlay.onAdd = function () {};
svl.overlay.draw = function () {};
svl.overlay.onRemove = function () {};
svl.overlay.setMap(svl.panorama);

// Modals
var modalMissionCompleteMap = new ModalMissionCompleteMap(svl.ui.modalMissionComplete);
var modalMissionCompleteProgressBar = new ModalMissionCompleteProgressBar(svl.ui.modalMissionComplete);
Expand Down
2 changes: 1 addition & 1 deletion public/javascripts/SVLabel/src/SVLabel/canvas/Canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ function Canvas(ribbon) {
function setOnlyLabelsOnPanoAsVisible(panoramaId) {
var labels = svl.labelContainer.getCanvasLabels();
for (var i = 0; i < labels.length; i += 1) {
if (labels[i].getPanoId() === panoramaId && !labels[i].isDeleted()) {
if (!labels[i].isDeleted()) {
labels[i].setVisibility('visible');
} else {
labels[i].setVisibility('hidden');
Expand Down
1 change: 1 addition & 0 deletions public/javascripts/SVLabel/src/SVLabel/data/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ function Form (labelContainer, missionModel, missionContainer, navigationModel,
temporary_label_id: tempLabelId,
audit_task_id: auditTaskId,
gsv_panorama_id : prop.panoId,
gsv_capture_date: prop.panoCaptureDate ? prop.panoCaptureDate : null,
severity: label.getProperty('severity'),
temporary: label.getProperty('temporaryLabel'),
tag_ids: label.getProperty('tagIds'),
Expand Down
119 changes: 83 additions & 36 deletions public/javascripts/SVLabel/src/SVLabel/label/Label.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function Label(params) {
panoId: undefined,
panoLat: undefined,
panoLng: undefined,
panoCaptureDate: undefined,
cameraHeading: undefined,
panoWidth: undefined,
panoHeight: undefined,
Expand Down Expand Up @@ -94,6 +95,7 @@ function Label(params) {
properties.panoXY = util.panomarker.calculatePanoXYFromPov(
properties.povOfLabelIfCentered, properties.cameraHeading, properties.panoWidth, properties.panoHeight
);
properties.panoCaptureDate = panoData.imageDate;
}

// Create the marker on the minimap.
Expand Down Expand Up @@ -203,9 +205,26 @@ function Label(params) {

// Update the coordinates of the label on the canvas.
if (svl.map.getPovChangeStatus()) {
properties.currCanvasXY = util.panomarker.getCanvasCoordinate(
properties.povOfLabelIfCentered, pov, util.EXPLORE_CANVAS_WIDTH, util.EXPLORE_CANVAS_HEIGHT, svl.LABEL_ICON_RADIUS
);
if(svl.map.getPanoId() == properties.panoId) {
properties.currCanvasXY = util.panomarker.getCanvasCoordinate(
properties.povOfLabelIfCentered, pov, util.EXPLORE_CANVAS_WIDTH, util.EXPLORE_CANVAS_HEIGHT, svl.LABEL_ICON_RADIUS
);
} else {
const latLng = this.toLatLng();
const projection = svl.overlay.getProjection();
const canvasXY = projection.fromLatLngToContainerPixel(latLng);
if(canvasXY != null) {
properties.currCanvasXY = {
x: canvasXY.x,
y: canvasXY.y
};
} else {
properties.currCanvasXY = {
x: null,
y: null
};
}
}
}

// Draw the label icon if it's in the visible part of the pano.
Expand Down Expand Up @@ -353,39 +372,67 @@ function Label(params) {
*/
function toLatLng() {
if (!properties.labelLat) {
// Estimate the latlng point from the camera position and the heading when point cloud data isn't available.
var panoLat = getProperty("panoLat");
var panoLng = getProperty("panoLng");
var panoHeading = getProperty("originalPov").heading;
var zoom = Math.round(getProperty("originalPov").zoom); // Need to round specifically for Safari.
var canvasX = getProperty('originalCanvasXY').x;
var canvasY = getProperty('originalCanvasXY').y;
var panoY = getProperty('panoXY').y;
var panoHeight = getProperty('panoHeight');
// Estimate heading diff and distance from pano using output from a regression analysis.
// https://github.com/ProjectSidewalk/label-latlng-estimation/blob/master/scripts/label-latlng-estimation.md#results
var estHeadingDiff =
LATLNG_ESTIMATION_PARAMS[zoom].headingIntercept +
LATLNG_ESTIMATION_PARAMS[zoom].headingCanvasXSlope * canvasX;
var estDistanceFromPanoKm = Math.max(0,
LATLNG_ESTIMATION_PARAMS[zoom].distanceIntercept +
LATLNG_ESTIMATION_PARAMS[zoom].distancePanoYSlope * (panoHeight / 2 - panoY) +
LATLNG_ESTIMATION_PARAMS[zoom].distanceCanvasYSlope * canvasY
) / 1000.0;
var estHeading = panoHeading + estHeadingDiff;
var startPoint = turf.point([panoLng, panoLat]);

// Use the pano location, distance from pano estimate, and heading estimate, calculate label location.
var destination = turf.destination(startPoint, estDistanceFromPanoKm, estHeading, { units: 'kilometers' });
var latlng = {
lat: destination.geometry.coordinates[1],
lng: destination.geometry.coordinates[0],
latLngComputationMethod: 'approximation2'
};
setProperty('labelLat', latlng.lat);
setProperty('labelLng', latlng.lng);
setProperty('latLngComputationMethod', latlng.latLngComputationMethod);
return latlng;
let gsvEstimatedLatLng = null;
if(properties.currCanvasXY.x) {
try {
const projection = svl.overlay.getProjection();

const latLng = projection.fromContainerPixelToLatLng({
x: properties.currCanvasXY.x,
y: properties.currCanvasXY.y
});

gsvEstimatedLatLng = {lat: latLng.lat(), lng: latLng.lng()}
} catch (e) {
console.error('Error estimating GSV lat/lng for label:', e);
}
}
if(gsvEstimatedLatLng) {
// If we have a GSV estimated latlng, use it.
var latlng = {
lat: gsvEstimatedLatLng.lat,
lng: gsvEstimatedLatLng.lng,
latLngComputationMethod: 'gsv'
};
setProperty('labelLat', latlng.lat);
setProperty('labelLng', latlng.lng);
setProperty('latLngComputationMethod', latlng.latLngComputationMethod);
return latlng;
} else {
// Estimate the latlng point from the camera position and the heading
var panoLat = getProperty("panoLat");
var panoLng = getProperty("panoLng");
var panoHeading = getProperty("originalPov").heading;
var zoom = Math.round(getProperty("originalPov").zoom); // Need to round specifically for Safari.
var canvasX = getProperty('originalCanvasXY').x;
var canvasY = getProperty('originalCanvasXY').y;
var panoY = getProperty('panoXY').y;
var panoHeight = getProperty('panoHeight');
// Estimate heading diff and distance from pano using output from a regression analysis.
// https://github.com/ProjectSidewalk/label-latlng-estimation/blob/master/scripts/label-latlng-estimation.md#results
var estHeadingDiff =
LATLNG_ESTIMATION_PARAMS[zoom].headingIntercept +
LATLNG_ESTIMATION_PARAMS[zoom].headingCanvasXSlope * canvasX;
var estDistanceFromPanoKm = Math.max(0,
LATLNG_ESTIMATION_PARAMS[zoom].distanceIntercept +
LATLNG_ESTIMATION_PARAMS[zoom].distancePanoYSlope * (panoHeight / 2 - panoY) +
LATLNG_ESTIMATION_PARAMS[zoom].distanceCanvasYSlope * canvasY
) / 1000.0;
var estHeading = panoHeading + estHeadingDiff;
var startPoint = turf.point([panoLng, panoLat]);

// Use the pano location, distance from pano estimate, and heading estimate, calculate label location.
var destination = turf.destination(startPoint, estDistanceFromPanoKm, estHeading, { units: 'kilometers' });
var latlng = {
lat: destination.geometry.coordinates[1],
lng: destination.geometry.coordinates[0],
latLngComputationMethod: 'approximation2'
};
setProperty('labelLat', latlng.lat);
setProperty('labelLng', latlng.lng);
setProperty('latLngComputationMethod', latlng.latLngComputationMethod);
return latlng;
}
} else {
// Return the cached value.
return {
Expand Down
27 changes: 24 additions & 3 deletions public/javascripts/SVLabel/src/SVLabel/label/LabelContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,32 @@ function LabelContainer ($, nextTemporaryLabelId) {
}

/**
* Returns labels for the current pano ID.
* Returns labels for the current view.
*/
this.getCanvasLabels = function () {
let panoId = svl.map.getPanoId();
return allLabels[panoId] ? allLabels[panoId] : [];
return this.getAllLabels().filter(function (label) {
// Extract dates of panoramas.
const panoramas = svl.panoramaContainer.getPanoramas();
const panoIdToDate = {};
for(const pano of panoramas) {
panoIdToDate[pano.data().location.pano] = pano.data().imageDate;
}
// Is the label in the current pano?
const presentInPano = label.getPanoId() === svl.map.getPanoId();
// Calculate distance between label and current pano.
const labelLatLng = label.toLatLng();
const panoLatLng = svl.map.getPosition();
const distance = turf.distance(turf.point([labelLatLng.lng, labelLatLng.lat]),
turf.point([panoLatLng.lng, panoLatLng.lat]), { units: "meters" });
// Check if date of label and pano match.
const panoDate = panoIdToDate[svl.map.getPanoId()];
const labelDate = label.getProperty('panoCaptureDate');
const dateMatches = panoDate === labelDate;

// We may want to do further testing to determine the optimal distance threshold.
// For now, we use 25m as a threshold.
return presentInPano || (distance < 25 && dateMatches);
});
};

/**
Expand Down