diff --git a/API.md b/API.md index d5420f0a3..b31ead90d 100644 --- a/API.md +++ b/API.md @@ -1,14 +1,15 @@ -This file documents efforts toward establishing a public API for iD, one that -can support plugin development. +This file documents efforts toward establishing a public API for iD. ## URL parameters +##### iD Standalone + iD supports several URL parameters. When constructing a URL to a standalone instance of iD (e.g. `http://openstreetmap.us/iD/release/`), the following parameters are available in the hash portion of the URL: -* `map` - A slash separated zoom level, longitude, and latitude. Example: - `map=20.00/-77.02271/38.90085`. +* `map` - A slash separated `zoom/longitude/latitude`. Example: + `map=20.00/-77.02271/38.90085` * `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. @@ -17,20 +18,32 @@ in the hash portion of the URL: or a custom tile URL. A custom URL is specified in the format `custom:`, where the URL can contain the standard tile URL placeholders `{x}`, `{y}` and `{z}`/`{zoom}`, `{ty}` for flipped TMS-style Y coordinates, and `{switch:a,b,c}` for - DNS multiplexing. Example: + DNS multiplexing. Example: `background=custom:http://{switch:a,b,c}.tiles.mapbox.com/v4/examples.map-4l7djmvo/{z}/{x}/{y}.png` +* `gpx` - A custom URL for loading a gpx track. Specifying a `gpx` parameter will + automatically enable the gpx layer for display. Example: + `gpx=https://tasks.hotosm.org/project/592/task/16.gpx` +* `offset` - imagery offset in meters, formatted as `east,north`. Example: + `offset=-10,5` * `comment` - Prefills the changeset comment box, for use when integrating iD with - external task management or quality assurance tools. Example: + external task management or quality assurance tools. Example: `comment=CAR%20crisis%2C%20refugee%20areas%20in%20Cameroon%20%23hotosm-task-592`. +##### iD on openstreetmap.org (Rails Port) + When constructing a URL to an instance of iD embedded in the OpenStreetMap Rails Port (e.g. `http://www.openstreetmap.org/edit?editor=id`), the following parameters are available as regular URL query parameters: +* `map` - slash separated `zoom/latitude/longitude`. Example: + `map=20.00/38.90085/-77.02271`. * `lat`, `lon`, `zoom` - Self-explanatory. * `node`, `way`, `relation` - Select the specified entity. +* `background` - same as standalone +* `gpx` - same as standalone +* `offset` - same as standalone +* `comment` - same as standalone -In addition, the `background` parameter is available as a hash parameter as above. ## CSS selectors diff --git a/js/id/geo.js b/js/id/geo.js index 826da1090..bbb0beb90 100644 --- a/js/id/geo.js +++ b/js/id/geo.js @@ -48,6 +48,28 @@ iD.geo.metersToLon = function(m, atLat) { m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180))); }; +iD.geo.offsetToMeters = function(offset) { + var equatRadius = 6356752.314245179, + polarRadius = 6378137.0, + tileSize = 256; + + return [ + offset[0] * 2 * Math.PI * equatRadius / tileSize, + -offset[1] * 2 * Math.PI * polarRadius / tileSize + ]; +}; + +iD.geo.metersToOffset = function(meters) { + var equatRadius = 6356752.314245179, + polarRadius = 6378137.0, + tileSize = 256; + + return [ + meters[0] * tileSize / (2 * Math.PI * equatRadius), + -meters[1] * tileSize / (2 * Math.PI * polarRadius) + ]; +}; + // Equirectangular approximation of spherical distances on Earth iD.geo.sphericalDistance = function(a, b) { var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2), diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index 37f28c556..ddee41351 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -197,12 +197,26 @@ iD.Background = function(context) { var overlays = (q.overlays || '').split(','); overlays.forEach(function(overlay) { overlay = findSource(overlay); - if (overlay) background.toggleOverlayLayer(overlay); + if (overlay) { + background.toggleOverlayLayer(overlay); + } }); if (q.gpx) { var gpx = context.layers().layer('gpx'); - if (gpx) { gpx.url(q.gpx); } + if (gpx) { + gpx.url(q.gpx); + } + } + + if (q.offset) { + var offset = q.offset.replace(/;/g, ',').split(',').map(function(n) { + return !isNaN(n) && n; + }); + + if (offset.length === 2) { + background.offset(iD.geo.metersToOffset(offset)); + } } }; diff --git a/js/id/ui/background.js b/js/id/ui/background.js index 4bcb72d0d..d516291d8 100644 --- a/js/id/ui/background.js +++ b/js/id/ui/background.js @@ -140,30 +140,8 @@ iD.ui.Background = function(context) { updateOffsetVal(); } - function offsetToMeters(offset) { - var equatRadius = 6356752.314245179, - polarRadius = 6378137.0, - tileSize = 256; - - return [ - offset[0] * 2 * Math.PI * equatRadius / tileSize, - -offset[1] * 2 * Math.PI * polarRadius / tileSize - ]; - } - - function metersToOffset(meters) { - var equatRadius = 6356752.314245179, - polarRadius = 6378137.0, - tileSize = 256; - - return [ - meters[0] * tileSize / (2 * Math.PI * equatRadius), - -meters[1] * tileSize / (2 * Math.PI * polarRadius) - ]; - } - function updateOffsetVal() { - var meters = offsetToMeters(context.background().offset()), + var meters = iD.geo.offsetToMeters(context.background().offset()), x = +meters[0].toFixed(2), y = +meters[1].toFixed(2); @@ -219,7 +197,7 @@ iD.ui.Background = function(context) { return; } - context.background().offset(metersToOffset(d)); + context.background().offset(iD.geo.metersToOffset(d)); updateOffsetVal(); } diff --git a/test/spec/geo.js b/test/spec/geo.js index ccb4180af..854caf984 100644 --- a/test/spec/geo.js +++ b/test/spec/geo.js @@ -131,6 +131,32 @@ describe('iD.geo', function() { }); }); + describe('.offsetToMeters', function() { + it('[0, 0] pixel offset is [0, -0] meter offset', function() { + var meters = iD.geo.offsetToMeters([0, 0]); + expect(meters[0]).to.eql(0); + expect(meters[1]).to.eql(-0); + }); + it('[0.00064, -0.00064] pixel offset is roughly [100, 100] meter offset', function() { + var meters = iD.geo.offsetToMeters([0.00064, -0.00064]); + expect(meters[0]).to.be.within(99.5, 100.5); + expect(meters[1]).to.be.within(99.5, 100.5); + }); + }); + + describe('.metersToOffset', function() { + it('[0, 0] meter offset is [0, -0] pixel offset', function() { + var offset = iD.geo.metersToOffset([0, 0]); + expect(offset[0]).to.eql(0); + expect(offset[1]).to.eql(-0); + }); + it('[100, 100] meter offset is roughly [0.00064, -0.00064] pixel offset', function() { + var offset = iD.geo.metersToOffset([100, 100]); + expect(offset[0]).to.be.within(0.000635, 0.000645); + expect(offset[1]).to.be.within(-0.000645, -0.000635); + }); + }); + describe('.sphericalDistance', function() { it('distance between two same points is zero', function() { var a = [0, 0],