mirror of
https://github.com/FoggedLens/iD.git
synced 2026-04-22 03:36:37 +02:00
allow type/id syntax in id hash param, and add support for notes
This commit is contained in:
@@ -29,7 +29,7 @@ of iD (e.g. `https://ideditor-release.netlify.app`), the following parameters ar
|
||||
optional and will be added automatically. (Note that hashtag-like strings are
|
||||
automatically detected in the `comment`).<br/>
|
||||
_Example:_ `hashtags=%23hotosm-task-592,%23MissingMaps`
|
||||
* __`id`__ - The character 'n', 'w', or 'r', followed by the OSM ID of a node, way or relation, respectively. Selects the specified entity, and, unless a `map` parameter is also provided, centers the map on it.<br/>
|
||||
* __`id`__ - Selects the specified OSM node, way, relation or note, and, unless a `map` parameter is also provided, centers the map on it. Supported formats are: a) `[nwr]<osm-id>` where the character 'n', 'w', or 'r' correspond to a OSM node, way or relation, repectively; or b) `<osm-type>/<osm-id>` which also allows to specify OSM notes (example: `note/1`).<br/>
|
||||
_Example:_ `id=n1207480649`
|
||||
* __`locale`__ - A code specifying the localization to use, affecting the language, layout, and keyboard shortcuts. Multiple codes may be specified in order of preference. The first valid code will be the locale, while the rest will be used as fallbacks if certain text hasn't been translated. The default locale preferences are set by the browser.<br/>
|
||||
_Example:_ `locale=ja`, `locale=pt-BR`, `locale=nl,fr,de`<br/>
|
||||
|
||||
@@ -39,6 +39,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
|
||||
# Unreleased (2.30.0-dev)
|
||||
|
||||
#### :tada: New Features
|
||||
* Allow to start up iD with a preselected OSM note or multiselection of OSM ids in the `id` hash parameter
|
||||
#### :sparkles: Usability & Accessibility
|
||||
* Login/logout pages are now using the user's locale ([#3595], thanks [@k-yle])
|
||||
* Sort preset-specific optional fields before universal fields in "Add field" dropdown ([#10181], thanks [@zbycz])
|
||||
|
||||
@@ -4,12 +4,13 @@ import { select as d3_select } from 'd3-selection';
|
||||
|
||||
import { geoSphericalDistance } from '../geo';
|
||||
import { modeBrowse } from '../modes/browse';
|
||||
import { modeSelect } from '../modes/select';
|
||||
import { modeSelect, modeSelectNote } from '../modes';
|
||||
import { utilDisplayLabel, utilObjectOmit, utilQsString, utilStringQs } from '../util';
|
||||
import { utilArrayIdentical } from '../util/array';
|
||||
import { t } from '../core/localizer';
|
||||
import { prefs } from '../core/preferences';
|
||||
|
||||
|
||||
export function behaviorHash(context) {
|
||||
|
||||
// cached window.location.hash
|
||||
@@ -33,6 +34,8 @@ export function behaviorHash(context) {
|
||||
});
|
||||
if (selected.length) {
|
||||
newParams.id = selected.join(',');
|
||||
} else if (context.selectedNoteID()) {
|
||||
newParams.id = `note/${context.selectedNoteID()}`;
|
||||
}
|
||||
|
||||
newParams.map = zoom.toFixed(2) +
|
||||
@@ -147,11 +150,14 @@ export function behaviorHash(context) {
|
||||
|
||||
if (q.id && mode) {
|
||||
var ids = q.id.split(',').filter(function(id) {
|
||||
return context.hasEntity(id);
|
||||
return context.hasEntity(id) || id.startsWith('note/');
|
||||
});
|
||||
if (ids.length &&
|
||||
(mode.id === 'browse' || (mode.id === 'select' && !utilArrayIdentical(mode.selectedIDs(), ids)))) {
|
||||
context.enter(modeSelect(context, ids));
|
||||
if (ids.length && ['browse', 'select-note', 'select'].includes(mode.id)) {
|
||||
if (ids.length === 1 && ids[0].startsWith('note/')) {
|
||||
context.enter(modeSelectNote(context, ids[0]));
|
||||
} else if (!utilArrayIdentical(mode.selectedIDs(), ids)) {
|
||||
context.enter(modeSelect(context, ids));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -185,10 +191,17 @@ export function behaviorHash(context) {
|
||||
var q = utilStringQs(window.location.hash);
|
||||
|
||||
if (q.id) {
|
||||
//if (!context.history().hasRestorableChanges()) {
|
||||
// targeting specific features: download, select, and zoom to them
|
||||
context.zoomToEntity(q.id.split(',')[0], !q.map);
|
||||
//}
|
||||
const selectIds = q.id.split(',');
|
||||
if (selectIds.length === 1 && selectIds[0].startsWith('note/')) {
|
||||
const noteId = selectIds[0].split('/')[1];
|
||||
context.zoomToNote(noteId, !q.map);
|
||||
} else {
|
||||
context.zoomToEntities(
|
||||
// convert ids to short form id: node/123 -> n123
|
||||
selectIds.map(id => id.replace(/([nwr])[^/]*\//, '$1')),
|
||||
!q.map);
|
||||
}
|
||||
}
|
||||
|
||||
if (q.walkthrough === 'true') {
|
||||
|
||||
+32
-9
@@ -1,4 +1,5 @@
|
||||
import _debounce from 'lodash-es/debounce';
|
||||
import _throttle from 'lodash-es/throttle';
|
||||
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { json as d3_json } from 'd3-fetch';
|
||||
@@ -14,7 +15,7 @@ import { coreHistory } from './history';
|
||||
import { coreValidator } from './validator';
|
||||
import { coreUploader } from './uploader';
|
||||
import { geoRawMercator } from '../geo/raw_mercator';
|
||||
import { modeSelect } from '../modes/select';
|
||||
import { modeSelect, modeSelectNote } from '../modes';
|
||||
import { presetManager } from '../presets';
|
||||
import { rendererBackground, rendererFeatures, rendererMap, rendererPhotos } from '../renderer';
|
||||
import { services } from '../services';
|
||||
@@ -176,6 +177,7 @@ export function coreContext() {
|
||||
_connection.loadEntityRelations(entityID, afterLoad(cid, callback));
|
||||
}
|
||||
};
|
||||
|
||||
// Download single note
|
||||
context.loadNote = (entityID, callback) => {
|
||||
if (_connection) {
|
||||
@@ -185,23 +187,26 @@ export function coreContext() {
|
||||
};
|
||||
|
||||
context.zoomToEntity = (entityID, zoomTo) => {
|
||||
context.zoomToEntities([entityID], zoomTo);
|
||||
};
|
||||
|
||||
context.zoomToEntities = (entityIDs, zoomTo) => {
|
||||
// be sure to load the entity even if we're not going to zoom to it
|
||||
context.loadEntity(entityID, (err, result) => {
|
||||
let loadedEntities = [];
|
||||
const throttledZoomTo = _throttle(() => _map.zoomTo(loadedEntities), 500);
|
||||
entityIDs.forEach(entityID => context.loadEntity(entityID, (err, result) => {
|
||||
if (err) return;
|
||||
loadedEntities.push(result.data.find(e => e.id === entityID));
|
||||
if (zoomTo !== false) {
|
||||
const entity = result.data.find(e => e.id === entityID);
|
||||
if (entity) {
|
||||
_map.zoomTo(entity);
|
||||
}
|
||||
throttledZoomTo();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
_map.on('drawn.zoomToEntity', () => {
|
||||
if (!context.hasEntity(entityID)) return;
|
||||
if (!entityIDs.every(entityID => context.hasEntity(entityID))) return;
|
||||
_map.on('drawn.zoomToEntity', null);
|
||||
context.on('enter.zoomToEntity', null);
|
||||
context.enter(modeSelect(context, [entityID]));
|
||||
context.enter(modeSelect(context, entityIDs));
|
||||
});
|
||||
|
||||
context.on('enter.zoomToEntity', () => {
|
||||
@@ -212,6 +217,24 @@ export function coreContext() {
|
||||
});
|
||||
};
|
||||
|
||||
context.zoomToNote = (noteId, zoomTo) => {
|
||||
context.loadNote(noteId, (err, result) => {
|
||||
if (err) return;
|
||||
if (zoomTo === false) return;
|
||||
const entity = result.data.find(e => e.id === noteId);
|
||||
if (entity) {
|
||||
// zoom to, used note loc
|
||||
const note = services.osm.getNote(noteId);
|
||||
context.map().centerZoom(note.loc,15);
|
||||
// open note layer
|
||||
const noteLayer = context.layers().layer('notes');
|
||||
noteLayer.enabled(true);
|
||||
// select the note
|
||||
context.enter(modeSelectNote(context, noteId));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let _minEditableZoom = 16;
|
||||
context.minEditableZoom = function(val) {
|
||||
if (!arguments.length) return _minEditableZoom;
|
||||
|
||||
+12
-2
@@ -17,6 +17,7 @@ import { utilGetDimensions } from '../util/dimensions';
|
||||
import { utilRebind } from '../util/rebind';
|
||||
import { utilZoomPan } from '../util/zoom_pan';
|
||||
import { utilDoubleUp } from '../util/double_up';
|
||||
import { isArray } from 'lodash-es';
|
||||
|
||||
// constants
|
||||
var TILESIZE = 256;
|
||||
@@ -908,8 +909,17 @@ export function rendererMap(context) {
|
||||
};
|
||||
|
||||
|
||||
map.zoomTo = function(entity) {
|
||||
var extent = entity.extent(context.graph());
|
||||
map.zoomTo = function(entities) {
|
||||
if (!isArray(entities)) {
|
||||
entities = [entities];
|
||||
}
|
||||
|
||||
if (entities.length === 0) return map;
|
||||
|
||||
var extent = entities
|
||||
.map(entity => entity.extent(context.graph()))
|
||||
.reduce((a, b) => a.extend(b));
|
||||
|
||||
if (!isFinite(extent.area())) return map;
|
||||
|
||||
var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
|
||||
|
||||
@@ -15,7 +15,6 @@ import { isColourValid } from '../osm/tags';
|
||||
import { services } from '../services';
|
||||
import { svgIcon } from '../svg/icon';
|
||||
import { uiCmd } from './cmd';
|
||||
import { modeSelectNote } from '../modes';
|
||||
|
||||
import {
|
||||
utilDisplayName,
|
||||
@@ -366,20 +365,7 @@ export function uiFeatureList(context) {
|
||||
const noteId = d.id.replace(/\D/g, '');
|
||||
|
||||
// load note
|
||||
context.loadNote(noteId, (err, result) => {
|
||||
if (err) return;
|
||||
const entity = result.data.find(e => e.id === noteId);
|
||||
if (entity) {
|
||||
// zoom to, used note loc
|
||||
const note = services.osm.getNote(noteId);
|
||||
context.map().centerZoom(note.loc,15);
|
||||
// open note layer
|
||||
const noteLayer = context.layers().layer('notes');
|
||||
noteLayer.enabled(true);
|
||||
// select the note
|
||||
context.enter(modeSelectNote(context, noteId));
|
||||
}
|
||||
});
|
||||
context.zoomToNote(noteId);
|
||||
} else {
|
||||
// download, zoom to, and select the entity with the given ID
|
||||
context.zoomToEntity(d.id);
|
||||
|
||||
Reference in New Issue
Block a user