mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-25 17:37:49 +02:00
Merge branch 'develop' into panoramax
This commit is contained in:
@@ -20,7 +20,7 @@ const _listeners = {};
|
||||
//
|
||||
/**
|
||||
* @param {string} k
|
||||
* @param {string?} v
|
||||
* @param {string?} [v]
|
||||
* @returns {boolean} true if the action succeeded
|
||||
*/
|
||||
function corePreferences(k, v) {
|
||||
|
||||
@@ -76,8 +76,8 @@ export function coreTree(head) {
|
||||
});
|
||||
|
||||
head.parentRelations(entity).forEach(function(relation) {
|
||||
if (memo[entity.id]) return;
|
||||
memo[entity.id] = true;
|
||||
if (memo[relation.id]) return;
|
||||
memo[relation.id] = true;
|
||||
if (_bboxes[relation.id]) {
|
||||
removeEntity(relation);
|
||||
insertions[relation.id] = relation;
|
||||
|
||||
+4
-2
@@ -58,7 +58,8 @@ export var osmAreaKeysExceptions = {
|
||||
station: true,
|
||||
traverser: true,
|
||||
turntable: true,
|
||||
wash: true
|
||||
wash: true,
|
||||
ventilation_shaft: true
|
||||
},
|
||||
waterway: {
|
||||
dam: true
|
||||
@@ -167,6 +168,7 @@ export var osmOneWayTags = {
|
||||
'ditch': true,
|
||||
'drain': true,
|
||||
'fish_pass': true,
|
||||
'flowline': true,
|
||||
'pressurised': true,
|
||||
'river': true,
|
||||
'spillway': true,
|
||||
@@ -245,7 +247,7 @@ export var osmRailwayTrackTagValues = {
|
||||
|
||||
// "waterway" tag values for line features representing water flow
|
||||
export var osmFlowingWaterwayTagValues = {
|
||||
canal: true, ditch: true, drain: true, fish_pass: true, river: true, stream: true, tidal_channel: true
|
||||
canal: true, ditch: true, drain: true, fish_pass: true, flowline: true, river: true, stream: true, tidal_channel: true
|
||||
};
|
||||
|
||||
// Tags which values should be considered case sensitive when offering tag suggestions
|
||||
|
||||
@@ -71,10 +71,17 @@ export function rendererFeatures(context) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @callback FilterFunction
|
||||
* @param {Record<string, string>} tags
|
||||
* @param {string} [geometry]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} k
|
||||
* @param {(tags: Record<string, string>, geometry: string) => boolean} filter
|
||||
* @param {?number} max
|
||||
* @param {FilterFunction} filter
|
||||
* @param {number} [max]
|
||||
*/
|
||||
function defineRule(k, filter, max) {
|
||||
var isEnabled = true;
|
||||
@@ -124,11 +131,11 @@ export function rendererFeatures(context) {
|
||||
}, 250);
|
||||
|
||||
defineRule('building_parts', function isBuildingPart(tags) {
|
||||
return tags['building:part'];
|
||||
return !!tags['building:part'];
|
||||
});
|
||||
|
||||
defineRule('indoor', function isIndoor(tags) {
|
||||
return tags.indoor;
|
||||
return !!tags.indoor;
|
||||
});
|
||||
|
||||
defineRule('landuse', function isLanduse(tags, geometry) {
|
||||
@@ -194,7 +201,7 @@ export function rendererFeatures(context) {
|
||||
});
|
||||
|
||||
defineRule('aerialways', function isAerialways(tags) {
|
||||
return tags.aerialway &&
|
||||
return !!tags?.aerialway &&
|
||||
tags.aerialway !== 'yes' &&
|
||||
tags.aerialway !== 'station';
|
||||
});
|
||||
@@ -260,7 +267,7 @@ export function rendererFeatures(context) {
|
||||
if (!arguments.length) {
|
||||
return _keys.filter(function(k) { return _rules[k].hidden(); });
|
||||
}
|
||||
return _rules[k] && _rules[k].hidden();
|
||||
return _rules[k]?.hidden();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import stringify from 'fast-json-stable-stringify';
|
||||
import polygonClipping from 'polygon-clipping';
|
||||
|
||||
import Protobuf from 'pbf';
|
||||
import vt from '@mapbox/vector-tile';
|
||||
import { VectorTile } from '@mapbox/vector-tile';
|
||||
|
||||
import { utilHashcode, utilRebind, utilTiler } from '../util';
|
||||
|
||||
@@ -22,7 +22,7 @@ function abortRequest(controller) {
|
||||
|
||||
|
||||
function vtToGeoJSON(data, tile, mergeCache) {
|
||||
var vectorTile = new vt.VectorTile(new Protobuf(data));
|
||||
var vectorTile = new VectorTile(new Protobuf(data));
|
||||
var layers = Object.keys(vectorTile.layers);
|
||||
if (!Array.isArray(layers)) { layers = [layers]; }
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { uiTooltip } from './tooltip';
|
||||
import { geoExtent } from '../geo/extent';
|
||||
import { uiFieldHelp } from './field_help';
|
||||
import { uiFields } from './fields';
|
||||
import { LANGUAGE_SUFFIX_REGEX } from './fields/localized';
|
||||
import { uiTagReference } from './tag_reference';
|
||||
import { utilRebind, utilUniqueDomId } from '../util';
|
||||
|
||||
@@ -97,6 +98,15 @@ export function uiField(context, presetField, entityIDs, options) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (field.type === 'localized') {
|
||||
for (let tagKey in _tags) {
|
||||
// matches for field:<code>, where <code> is a BCP 47 locale code
|
||||
let match = tagKey.match(LANGUAGE_SUFFIX_REGEX);
|
||||
if (match && match[1] === field.key && match[2]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _tags[key] !== undefined;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { uiLengthIndicator } from '../length_indicator';
|
||||
|
||||
var _languagesArray = [];
|
||||
|
||||
export const LANGUAGE_SUFFIX_REGEX = /^(.*):([a-z]{2,3}(?:-[A-Z][a-z]{3})?(?:-[A-Z]{2})?)$/;
|
||||
|
||||
export function uiFieldLocalized(field, context) {
|
||||
var dispatch = d3_dispatch('change', 'input');
|
||||
@@ -127,7 +128,7 @@ export function uiFieldLocalized(field, context) {
|
||||
// matches for field:<code>, where <code> is a BCP 47 locale code
|
||||
// motivation is to avoid matching on similarly formatted tags that are
|
||||
// not for languages, e.g. name:left, name:source, etc.
|
||||
var m = k.match(/^(.*):([a-z]{2,3}(?:-[A-Z][a-z]{3})?(?:-[A-Z]{2})?)$/);
|
||||
var m = k.match(LANGUAGE_SUFFIX_REGEX);
|
||||
if (m && m[1] === field.key && m[2]) {
|
||||
var item = { lang: m[2], value: tags[k] };
|
||||
if (existingLangs.has(item.lang)) {
|
||||
|
||||
+127
-1
@@ -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) {
|
||||
|
||||
@@ -62,6 +65,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;
|
||||
|
||||
Reference in New Issue
Block a user