Merge pull request #10046 from laigyu/add-button-to-mapillary

Add a button in the mapillary image photo viewer to "add the current image id as field to feature"
This commit is contained in:
Martin Raifer
2024-07-16 22:14:21 +02:00
committed by GitHub
5 changed files with 154 additions and 1 deletions
+2
View File
@@ -89,6 +89,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
* Add button to fully load incompletely downloaded relations ([#5420])
#### :camera: Street-Level
* Show Mapillary username and deep link to external viewer on Mapillary photos ([#10135], thanks [@Sushil642])
* Add button to directly attach the id of a mapillary photo as the `mapillary` tag of selected map features ([#9339])
#### :white_check_mark: Validation
* Drop validation which checks for [old style multipolygons](https://wiki.openstreetmap.org/wiki/Old_style_multipolygons), as these have long been [fixed](https://blog.jochentopf.com/2017-08-28-polygon-fixing-effort-concluded.html) in OSM
* Upgrade closed ways with `traffic_calming=island` to `area:highway=traffic_island` ([id-tagging-schema#1162])
@@ -106,6 +107,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
[#5420]: https://github.com/openstreetmap/iD/issues/5420
[#7653]: https://github.com/openstreetmap/iD/issues/7653
[#8415]: https://github.com/openstreetmap/iD/issues/8415
[#9339]: https://github.com/openstreetmap/iD/issues/9339
[#9439]: https://github.com/openstreetmap/iD/issues/9439
[#10135]: https://github.com/openstreetmap/iD/issues/10135
[#10145]: https://github.com/openstreetmap/iD/issues/10145
+9
View File
@@ -33,6 +33,15 @@
z-index: 50;
}
.photoviewer button.set-photo-from-viewer {
border-radius: 0;
padding: 5px;
position: absolute;
left: 5px;
top: 5px;
z-index: 50;
}
.photoviewer button.resize-handle-xy {
border-radius: 0;
position: absolute;
+5
View File
@@ -788,6 +788,11 @@ en:
inch: in
max_length_reached: "This string is longer than the maximum length of {maxChars} characters. Anything exceeding that length will be truncated."
set_today: "Sets the value to today."
set_photo_from_viewer:
enable: "Tag this photo id on the currently selected map feature"
disable:
already_set: "This photo is already tagged on the selected map feature"
too_far: "This photo is too far away from the selected map feature"
background:
title: Background
description: Background Settings
+11
View File
@@ -30,6 +30,7 @@ let _mlyShowFeatureDetections = false;
let _mlyShowSignDetections = false;
let _mlyViewer;
let _mlyViewerFilter = ['all'];
let _isViewerOpen = false;
// Load all data for the specified type from Mapillary vector tiles
@@ -478,6 +479,8 @@ export default {
_mlyViewer.resize();
}
_isViewerOpen = true;
return this;
},
@@ -504,10 +507,18 @@ export default {
dispatch.call('loadedMapFeatures');
dispatch.call('loadedSigns');
_isViewerOpen = false;
return this.setStyles(context, null);
},
// Get viewer status
isViewerOpen: function() {
return _isViewerOpen;
},
// Update the URL with current image id
updateUrlImage: function(imageId) {
if (!window.mocha) {
+127 -1
View File
@@ -6,8 +6,11 @@ import { t } from '../core/localizer';
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { svgIcon } from '../svg/icon';
import { utilGetDimensions } from '../util/dimensions';
import { utilRebind } from '../util';
import { utilRebind, utilStringQs } from '../util';
import { services } from '../services';
import { uiTooltip } from './tooltip';
import { actionChangeTags } from '../actions';
import { geoSphericalDistance } from '../geo';
export function uiPhotoviewer(context) {
@@ -61,6 +64,129 @@ export function uiPhotoviewer(context) {
buildResizeListener(selection, 'resize', dispatch, { resizeOnY: true })
);
// update sett_photo_from_viewer button on selection change and when tags change
context.features().on('change.setPhotoFromViewer', function() {
setPhotoFromViewerButton();
});
context.history().on('change.setPhotoFromViewer', function() {
setPhotoFromViewerButton();
});
function setPhotoFromViewerButton() {
if (services.mapillary.isViewerOpen()) {
if (context.mode().id !== 'select' || !(layerStatus('mapillary') && getServiceId() === 'mapillary')) {
buttonRemove();
} else {
if (selection.select('.set-photo-from-viewer').empty()) {
const button = buttonCreate();
button.on('click', function (e) {
e.preventDefault();
e.stopPropagation();
setMapillaryPhotoId();
buttonDisable('already_set');
});
}
buttonShowHide();
}
function setMapillaryPhotoId() {
const service = services.mapillary;
const image = service.getActiveImage();
const action = graph =>
context.selectedIDs().reduce((graph, entityID) => {
const tags = graph.entity(entityID).tags;
const action = actionChangeTags(entityID, {...tags, mapillary: image.id});
return action(graph);
}, graph);
const annotation = t('operations.change_tags.annotation');
context.perform(action, annotation);
}
}
function layerStatus(which) {
const layers = context.layers();
const layer = layers.layer(which);
return layer.enabled();
}
function getServiceId() {
const hash = utilStringQs(window.location.hash);
let serviceId;
if (hash.photo) {
let result = hash.photo.split('/');
serviceId = result[0];
}
return serviceId;
}
function buttonCreate() {
const button = selection.selectAll('.set-photo-from-viewer').data([0]);
const buttonEnter = button.enter()
.append('button')
.attr('class', 'set-photo-from-viewer')
.call(svgIcon('#iD-icon-plus'))
.call(uiTooltip()
.title(() => t.append('inspector.set_photo_from_viewer'))
.placement('right')
);
buttonEnter.select('.tooltip')
.classed('dark', true)
.style('width', '300px');
return buttonEnter;
}
function buttonRemove() {
const button = selection.selectAll('.set-photo-from-viewer').data([0]);
button.remove();
}
function buttonShowHide() {
const activeImage = services.mapillary.getActiveImage();
const graph = context.graph();
const entities = context.selectedIDs()
.map(id => graph.entity(id));
if (entities.map(entity => entity.tags.mapillary)
.every(value => value === activeImage?.id)) {
buttonDisable('already_set');
} else if (activeImage && entities.map(entity => entity.extent().center())
.every(loc => geoSphericalDistance(loc, activeImage.loc) > 100)) {
buttonDisable('too_far');
} else {
buttonDisable(false);
}
}
function buttonDisable(reason) {
const disabled = reason !== false;
const button = selection.selectAll('.set-photo-from-viewer').data([0]);
button.attr('disabled', disabled ? 'true' : null);
button.classed('disabled', disabled);
button.call(uiTooltip().destroyAny);
if (disabled) {
button.call(uiTooltip()
.title(() => t.append(`inspector.set_photo_from_viewer.disable.${reason}`))
.placement('right')
);
} else {
button.call(uiTooltip()
.title(() => t.append('inspector.set_photo_from_viewer.enable'))
.placement('right')
);
}
button.select('.tooltip')
.classed('dark', true)
.style('width', '300px');
}
}
function buildResizeListener(target, eventName, dispatch, options) {
var resizeOnX = !!options.resizeOnX;