Commit ee4f1139 authored by Bryan Housel's avatar Bryan Housel Committed by GitHub

Merge pull request #3231 from openstreetmap/module_services

External modules for services
parents a80e6839 5e57ff07
......@@ -44,15 +44,14 @@ $(BUILDJS_TARGETS): $(BUILDJS_SOURCES) build.js
MODULE_TARGETS = \
js/lib/id/index.js \
js/lib/id/services.js
js/lib/id/index.js: $(shell find modules -type f)
@rm -f $@
node_modules/.bin/rollup -c rollup.config.js -f umd -n iD modules/index.js --no-strict -o $@
js/lib/id/services.js: $(shell find modules/services -type f)
@rm -f $@
node_modules/.bin/rollup -c rollup.config.js -f umd -n iD.services modules/services/index.js --no-strict -o $@
#
# js/lib/id/services.js: $(shell find modules/services -type f)
# @rm -f $@
# node_modules/.bin/rollup -c rollup.config.js -f umd -n iD.services modules/services/index.js --no-strict -o $@
dist/iD.js: \
js/lib/bootstrap-tooltip.js \
......
......@@ -30,8 +30,6 @@
<script src='js/lib/id/index.js'></script>
<script src='js/lib/id/services.js'></script>
<script src='data/data_dev.js'></script>
<script src='js/lib/locale.js'></script>
......@@ -45,7 +43,7 @@
id = iD()
.presets(iD.data.presets)
.imagery(iD.data.imagery)
.taginfo(iD.services.taginfo())
.taginfo(iD.services.taginfo.init())
.assetPath('dist/');
d3.select('#id-container')
......
This diff is collapsed.
......@@ -4,6 +4,7 @@ import * as geo from './geo/index';
import * as modes from './modes/index';
import * as operations from './operations/index';
import * as presets from './presets/index';
import * as services from './services/index';
import * as svg from './svg/index';
import * as ui from './ui/index';
import * as util from './util/index';
......@@ -35,6 +36,7 @@ export {
modes,
operations,
presets,
services,
svg,
util,
ui,
......
export { mapillary } from './mapillary';
export { nominatim } from './nominatim';
export { taginfo } from './taginfo';
export { wikidata } from './wikidata';
export { wikipedia } from './wikipedia';
import * as mapillary from './mapillary';
import * as nominatim from './nominatim';
import * as taginfo from './taginfo';
import * as wikidata from './wikidata';
import * as wikipedia from './wikipedia';
export { mapillary, taginfo, nominatim, wikidata, wikipedia};
This diff is collapsed.
import { Extent } from '../geo/index';
import { qsString } from '../util/index';
import rbush from 'rbush';
export function nominatim() {
var nominatim = {},
endpoint = 'https://nominatim.openstreetmap.org/reverse?';
var endpoint, cache;
export function init() {
endpoint = 'https://nominatim.openstreetmap.org/reverse?';
if (!cache) {
reset();
}
}
nominatim.countryCode = function(location, callback) {
var cache = iD.services.nominatim.cache,
countryCodes = cache.search({ minX: location[0], minY: location[1], maxX: location[0], maxY: location[1] });
if (countryCodes.length > 0)
return callback(null, countryCodes[0].data);
d3.json(endpoint +
iD.util.qsString({
format: 'json',
addressdetails: 1,
lat: location[1],
lon: location[0]
}), function(err, result) {
if (err)
return callback(err);
else if (result && result.error)
return callback(result.error);
export function reset() {
cache = rbush();
}
var extent = iD.geo.Extent(location).padByMeters(1000);
export function countryCode(location, callback) {
var countryCodes = cache.search({ minX: location[0], minY: location[1], maxX: location[0], maxY: location[1] });
cache.insert(Object.assign(extent.bbox(), { data: result.address.country_code }));
if (countryCodes.length > 0)
return callback(null, countryCodes[0].data);
callback(null, result.address.country_code);
});
};
d3.json(endpoint +
qsString({
format: 'json',
addressdetails: 1,
lat: location[1],
lon: location[0]
}), function(err, result) {
if (err)
return callback(err);
else if (result && result.error)
return callback(result.error);
nominatim.reset = function() {
iD.services.nominatim.cache = rbush();
return this;
};
var extent = Extent(location).padByMeters(1000);
if (!iD.services.nominatim.cache) {
nominatim.reset();
}
cache.insert(Object.assign(extent.bbox(), { data: result.address.country_code }));
return nominatim;
callback(null, result.address.country_code);
});
}
export function taginfo() {
var taginfo = {},
endpoint = 'https://taginfo.openstreetmap.org/api/4/',
tag_sorts = {
point: 'count_nodes',
vertex: 'count_nodes',
area: 'count_ways',
line: 'count_ways'
},
tag_filters = {
point: 'nodes',
vertex: 'nodes',
area: 'ways',
line: 'ways'
};
function sets(parameters, n, o) {
if (parameters.geometry && o[parameters.geometry]) {
parameters[n] = o[parameters.geometry];
}
return parameters;
}
import { qsString } from '../util/index';
var taginfo = {},
endpoint = 'https://taginfo.openstreetmap.org/api/4/',
tag_sorts = {
point: 'count_nodes',
vertex: 'count_nodes',
area: 'count_ways',
line: 'count_ways'
},
tag_filters = {
point: 'nodes',
vertex: 'nodes',
area: 'ways',
line: 'ways'
};
function setFilter(parameters) {
return sets(parameters, 'filter', tag_filters);
}
function setSort(parameters) {
return sets(parameters, 'sortname', tag_sorts);
function sets(parameters, n, o) {
if (parameters.geometry && o[parameters.geometry]) {
parameters[n] = o[parameters.geometry];
}
return parameters;
}
function clean(parameters) {
return _.omit(parameters, 'geometry', 'debounce');
}
function setFilter(parameters) {
return sets(parameters, 'filter', tag_filters);
}
function filterKeys(type) {
var count_type = type ? 'count_' + type : 'count_all';
return function(d) {
return parseFloat(d[count_type]) > 2500 || d.in_wiki;
};
}
function setSort(parameters) {
return sets(parameters, 'sortname', tag_sorts);
}
function filterMultikeys() {
return function(d) {
return (d.key.match(/:/g) || []).length === 1; // exactly one ':'
};
}
function clean(parameters) {
return _.omit(parameters, 'geometry', 'debounce');
}
function filterValues() {
return function(d) {
if (d.value.match(/[A-Z*;,]/) !== null) return false; // exclude some punctuation, uppercase letters
return parseFloat(d.fraction) > 0.0 || d.in_wiki;
};
}
function filterKeys(type) {
var count_type = type ? 'count_' + type : 'count_all';
return function(d) {
return parseFloat(d[count_type]) > 2500 || d.in_wiki;
};
}
function valKey(d) {
return {
value: d.key,
title: d.key
};
}
function filterMultikeys() {
return function(d) {
return (d.key.match(/:/g) || []).length === 1; // exactly one ':'
};
}
function valKeyDescription(d) {
return {
value: d.value,
title: d.description || d.value
};
}
function filterValues() {
return function(d) {
if (d.value.match(/[A-Z*;,]/) !== null) return false; // exclude some punctuation, uppercase letters
return parseFloat(d.fraction) > 0.0 || d.in_wiki;
};
}
// sort keys with ':' lower than keys without ':'
function sortKeys(a, b) {
return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
: (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
: 0;
}
function valKey(d) {
return {
value: d.key,
title: d.key
};
}
var debounced = _.debounce(d3.json, 100, true);
function valKeyDescription(d) {
return {
value: d.value,
title: d.description || d.value
};
}
// sort keys with ':' lower than keys without ':'
function sortKeys(a, b) {
return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
: (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
: 0;
}
function request(url, debounce, callback) {
var cache = iD.services.taginfo.cache;
var debounced = _.debounce(d3.json, 100, true);
if (cache[url]) {
callback(null, cache[url]);
} else if (debounce) {
debounced(url, done);
} else {
d3.json(url, done);
}
function request(url, debounce, callback) {
var cache = taginfo.cache;
function done(err, data) {
if (!err) cache[url] = data;
callback(err, data);
}
if (cache[url]) {
callback(null, cache[url]);
} else if (debounce) {
debounced(url, done);
} else {
d3.json(url, done);
}
function done(err, data) {
if (!err) cache[url] = data;
callback(err, data);
}
}
export function init() {
taginfo.keys = function(parameters, callback) {
var debounce = parameters.debounce;
parameters = clean(setSort(parameters));
request(endpoint + 'keys/all?' +
iD.util.qsString(_.extend({
qsString(_.extend({
rp: 10,
sortname: 'count_all',
sortorder: 'desc',
......@@ -114,7 +116,7 @@ export function taginfo() {
var debounce = parameters.debounce;
parameters = clean(setSort(parameters));
request(endpoint + 'keys/all?' +
iD.util.qsString(_.extend({
qsString(_.extend({
rp: 25,
sortname: 'count_all',
sortorder: 'desc',
......@@ -130,7 +132,7 @@ export function taginfo() {
var debounce = parameters.debounce;
parameters = clean(setSort(setFilter(parameters)));
request(endpoint + 'key/values?' +
iD.util.qsString(_.extend({
qsString(_.extend({
rp: 25,
sortname: 'count_all',
sortorder: 'desc',
......@@ -150,7 +152,7 @@ export function taginfo() {
if (parameters.value) path = 'tag/wiki_pages?';
else if (parameters.rtype) path = 'relation/wiki_pages?';
request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
request(endpoint + path + qsString(parameters), debounce, function(err, d) {
if (err) return callback(err);
callback(null, d.data);
});
......@@ -163,12 +165,12 @@ export function taginfo() {
};
taginfo.reset = function() {
iD.services.taginfo.cache = {};
taginfo.cache = {};
return taginfo;
};
if (!iD.services.taginfo.cache) {
if (!taginfo.cache) {
taginfo.reset();
}
......
export function wikidata() {
var wikidata = {},
endpoint = 'https://www.wikidata.org/w/api.php?';
import { qsString } from '../util/index';
var wikidata = {},
endpoint = 'https://www.wikidata.org/w/api.php?';
export function init() {
// Given a Wikipedia language and article title, return an array of
// corresponding Wikidata entities.
wikidata.itemsByTitle = function(lang, title, callback) {
lang = lang || 'en';
d3.jsonp(endpoint + iD.util.qsString({
d3.jsonp(endpoint + qsString({
action: 'wbgetentities',
format: 'json',
sites: lang.replace(/-/g, '_') + 'wiki',
......@@ -18,6 +19,5 @@ export function wikidata() {
callback(title, data.entities || {});
});
};
return wikidata;
}
export function wikipedia() {
var wikipedia = {},
endpoint = 'https://en.wikipedia.org/w/api.php?';
import { qsString } from '../util/index';
var wikipedia = {},
endpoint = 'https://en.wikipedia.org/w/api.php?';
export function init() {
wikipedia.search = function(lang, query, callback) {
lang = lang || 'en';
d3.jsonp(endpoint.replace('en', lang) +
iD.util.qsString({
qsString({
action: 'query',
list: 'search',
srlimit: '10',
......@@ -25,7 +26,7 @@ export function wikipedia() {
wikipedia.suggestions = function(lang, query, callback) {
lang = lang || 'en';
d3.jsonp(endpoint.replace('en', lang) +
iD.util.qsString({
qsString({
action: 'opensearch',
namespace: 0,
suggest: '',
......@@ -39,7 +40,7 @@ export function wikipedia() {
wikipedia.translations = function(lang, title, callback) {
d3.jsonp(endpoint.replace('en', lang) +
iD.util.qsString({
qsString({
action: 'query',
prop: 'langlinks',
format: 'json',
......
......@@ -15,8 +15,8 @@ export function MapillaryImages(projection, context, dispatch) {
function getMapillary() {
if (iD.services.mapillary && !_mapillary) {
_mapillary = iD.services.mapillary();
_mapillary.on('loadedImages', debouncedRedraw);
_mapillary = iD.services.mapillary.init();
_mapillary.event.on('loadedImages', debouncedRedraw);
} else if (!iD.services.mapillary && _mapillary) {
_mapillary = null;
}
......
......@@ -6,7 +6,6 @@ export function MapillarySigns(projection, context, dispatch) {
layer = d3.select(null),
_mapillary;
function init() {
if (MapillarySigns.initialized) return; // run once
MapillarySigns.enabled = false;
......@@ -15,7 +14,8 @@ export function MapillarySigns(projection, context, dispatch) {
function getMapillary() {
if (iD.services.mapillary && !_mapillary) {
_mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
_mapillary = iD.services.mapillary.init();
_mapillary.event.on('loadedSigns', debouncedRedraw);
} else if (!iD.services.mapillary && _mapillary) {
_mapillary = null;
}
......
......@@ -110,8 +110,8 @@ export function address(field, context) {
var center = entity.extent(context.graph()).center(),
addressFormat;
iD.services.nominatim().countryCode(center, function (err, countryCode) {
iD.services.nominatim.init();
iD.services.nominatim.countryCode(center, function (err, countryCode) {
addressFormat = _.find(iD.data.addressFormats, function (a) {
return a && a.countryCodes && _.includes(a.countryCodes, countryCode);
}) || _.first(iD.data.addressFormats);
......
......@@ -28,7 +28,8 @@ export function url(field, context) {
if (field.type === 'tel') {
var center = entity.extent(context.graph()).center();
iD.services.nominatim().countryCode(center, function (err, countryCode) {
iD.services.nominatim.init();
iD.services.nominatim.countryCode(center, function (err, countryCode) {
if (err || !iD.data.phoneFormats[countryCode]) return;
selection.selectAll('#' + fieldId)
.attr('placeholder', iD.data.phoneFormats[countryCode]);
......
......@@ -2,7 +2,7 @@ import { Icon } from '../../svg/index';
import { SuggestNames } from '../../util/index';
export function localized(field, context) {
var dispatch = d3.dispatch('change', 'input'),
wikipedia = iD.services.wikipedia(),
wikipedia = iD.services.wikipedia.init(),
input, localizedInputs, wikiTitles,
entity;
......
......@@ -3,8 +3,8 @@ import { Icon } from '../../svg/index';
export function wikipedia(field, context) {
var dispatch = d3.dispatch('change'),
wikipedia = iD.services.wikipedia(),
wikidata = iD.services.wikidata(),
wikipedia = iD.services.wikipedia.init(),
wikidata = iD.services.wikidata.init(),
link, entity, lang, title;
function wiki(selection) {
......
......@@ -37,7 +37,6 @@
<script src='../js/id/id.js'></script>
<script src='../js/lib/id/index.js'></script>
<script src='../js/lib/id/services.js'></script>
<script src='../js/lib/locale.js'></script>
......
......@@ -8,7 +8,7 @@ describe('iD.services.mapillary', function() {
context.projection.translate([-116508, 0]); // 10,0
server = sinon.fakeServer.create();
mapillary = iD.services.mapillary();
mapillary = iD.services.mapillary.init();
mapillary.reset();
});
......@@ -19,12 +19,12 @@ describe('iD.services.mapillary', function() {
describe('Mapillary service', function() {
it('Initializes cache one time', function() {
var cache = iD.services.mapillary.cache;
var cache = iD.services.mapillary.getMapillary().cache;
expect(cache).to.have.property('images');
expect(cache).to.have.property('signs');
iD.services.mapillary();
var cache2 = iD.services.mapillary.cache;
iD.services.mapillary.init();
var cache2 = iD.services.mapillary.getMapillary().cache;
expect(cache).to.equal(cache2);
});
});
......@@ -123,7 +123,7 @@ describe('iD.services.mapillary', function() {
});
server.respond();
var sign_defs = iD.services.mapillary.sign_defs;
var sign_defs = iD.services.mapillary.getMapillary().sign_defs;
expect(sign_defs).to.have.property('au')
.that.is.an('object')
......@@ -252,7 +252,7 @@ describe('iD.services.mapillary', function() {
{ minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10,1], ca: 90 } }
];
iD.services.mapillary.cache.images.rtree.load(features);
iD.services.mapillary.getMapillary().cache.images.rtree.load(features);
var res = mapillary.images(context.projection, dimensions);
expect(res).to.deep.eql([
......@@ -270,7 +270,7 @@ describe('iD.services.mapillary', function() {
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90 } }
];
iD.services.mapillary.cache.images.rtree.load(features);
iD.services.mapillary.getMapillary().cache.images.rtree.load(features);
var res = mapillary.images(context.projection, dimensions);
expect(res).to.have.length.of.at.most(3);
});
......@@ -291,7 +291,7 @@ describe('iD.services.mapillary', function() {
{ minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10,1], signs: signs } }
];
iD.services.mapillary.cache.signs.rtree.load(features);
iD.services.mapillary.getMapillary().cache.signs.rtree.load(features);
var res = mapillary.signs(context.projection, dimensions);
expect(res).to.deep.eql([
......@@ -316,7 +316,7 @@ describe('iD.services.mapillary', function() {
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], signs: signs } }
];
iD.services.mapillary.cache.signs.rtree.load(features);
iD.services.mapillary.getMapillary().cache.signs.rtree.load(features);
var res = mapillary.signs(context.projection, dimensions);
expect(res).to.have.length.of.at.most(3);
});
......@@ -340,7 +340,7 @@ describe('iD.services.mapillary', function() {
describe('#signHTML', function() {
it('returns sign HTML', function() {
iD.services.mapillary.sign_defs = {
iD.services.mapillary.getMapillary().sign_defs = {
us: {'regulatory--maximum-speed-limit-65--us': '<span class="t">65</span>'}
};
......@@ -363,25 +363,25 @@ describe('iD.services.mapillary', function() {
describe('#setSelectedImage', function() {
it('sets selected image', function() {
mapillary.setSelectedImage('foo');
expect(iD.services.mapillary.image).to.eql('foo');
expect(iD.services.mapillary.getMapillary().image).to.eql('foo');
});
});
describe('#getSelectedImage', function() {
it('gets selected image', function() {
iD.services.mapillary.image = 'bar';
iD.services.mapillary.getMapillary().image = 'bar';
expect(mapillary.getSelectedImage()).to.eql('bar');
});
});
describe('#reset', function() {
it('resets cache and image', function() {
iD.services.mapillary.cache.foo = 'bar';
iD.services.mapillary.image = 'bar';
iD.services.mapillary.getMapillary().cache.foo = 'bar';
iD.services.mapillary.getMapillary().image = 'bar';
mapillary.reset();
expect(iD.services.mapillary.cache).to.not.have.property('foo');
expect(iD.services.mapillary.image).to.be.null;
expect(iD.services.mapillary.getMapillary().cache).to.not.have.property('foo');
expect(iD.services.mapillary.getMapillary().image).to.be.null;
});
});
......
......@@ -3,8 +3,9 @@ describe('iD.services.nominatim', function() {
beforeEach(function() {
server = sinon.fakeServer.create();
nominatim = iD.services.nominatim();
nominatim.reset();
iD.services.nominatim.init();
nominatim = iD.services.nominatim;
iD.services.nominatim.reset();
});
afterEach(function() {
......
......@@ -3,7 +3,8 @@ describe('iD.services.taginfo', function() {
beforeEach(function() {
server = sinon.fakeServer.create();
taginfo = iD.services.taginfo().reset();
taginfo = iD.services.taginfo.init();
taginfo.reset();
});
afterEach(function() {
......
describe('wikipedia', function() {
var entity, context, selection, field, wikiDelay, selectedId;
function wikidataStub() {
wikidataStub.itemsByTitle = function(lang, title, callback) {
var data = {Q216353: {id: 'Q216353'}};
if (wikiDelay) {
window.setTimeout(function () { callback(title, data); }, wikiDelay);
function jsonp(url, callback) {
var args = [{
entities: {
Q216353: { id: 'Q216353' }
}
else {
callback(title, data);
}
};
return wikidataStub;
}];
if (wikiDelay) {
window.setTimeout(function () {
callback.apply(null, args);
}, wikiDelay);
} else {
callback.apply(null, args);
}
}
function changeTags(changed) {
......@@ -29,12 +31,12 @@ describe('wikipedia', function() {
field = context.presets(iD.data.presets).presets().field('wikipedia');
wikiDelay = 0;
sinon.stub(iD.services, 'wikidata', wikidataStub);
sinon.stub(d3, 'jsonp', jsonp);
sinon.stub(context, 'selectedIDs', function() { return [selectedId]; });
});
afterEach(function() {
iD.services.wikidata.restore();
d3.jsonp.restore();
context.selectedIDs.restore();
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment