mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
Merge pull request #3975 from openstreetmap/taginfo_improvements
Taginfo improvements
This commit is contained in:
@@ -6,13 +6,22 @@ import { utilQsString } from '../util/index';
|
||||
|
||||
|
||||
var apibase = 'https://nominatim.openstreetmap.org/',
|
||||
inflight = {},
|
||||
nominatimCache;
|
||||
|
||||
|
||||
export default {
|
||||
|
||||
init: function() { nominatimCache = rbush(); },
|
||||
reset: function() { nominatimCache = rbush(); },
|
||||
init: function() {
|
||||
inflight = {};
|
||||
nominatimCache = rbush();
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
_.forEach(inflight, function(req) { req.abort(); });
|
||||
inflight = {};
|
||||
nominatimCache = rbush();
|
||||
},
|
||||
|
||||
|
||||
countryCode: function (location, callback) {
|
||||
@@ -24,32 +33,37 @@ export default {
|
||||
return callback(null, countryCodes[0].data);
|
||||
}
|
||||
|
||||
d3.json(apibase + 'reverse?' +
|
||||
utilQsString({
|
||||
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);
|
||||
var params = { format: 'json', addressdetails: 1, lat: location[1], lon: location[0] };
|
||||
var url = apibase + 'reverse?' + utilQsString(params);
|
||||
if (inflight[url]) return;
|
||||
|
||||
var extent = geoExtent(location).padByMeters(1000);
|
||||
nominatimCache.insert(_.assign(extent.bbox(),
|
||||
{ data: result.address.country_code }
|
||||
));
|
||||
inflight[url] = d3.json(url, function(err, result) {
|
||||
delete inflight[url];
|
||||
|
||||
callback(null, result.address.country_code);
|
||||
}
|
||||
);
|
||||
if (err)
|
||||
return callback(err);
|
||||
else if (result && result.error)
|
||||
return callback(result.error);
|
||||
|
||||
var extent = geoExtent(location).padByMeters(1000);
|
||||
nominatimCache.insert(_.assign(extent.bbox(),
|
||||
{ data: result.address.country_code }
|
||||
));
|
||||
|
||||
callback(null, result.address.country_code);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
search: function (val, callback) {
|
||||
var searchVal = encodeURIComponent(val);
|
||||
d3.json(apibase + 'search/' + searchVal + '?limit=10&format=json', callback);
|
||||
var url = apibase + 'search/' + searchVal + '?limit=10&format=json';
|
||||
if (inflight[url]) return;
|
||||
|
||||
inflight[url] = d3.json(url, function(err, result) {
|
||||
delete inflight[url];
|
||||
callback(err, result);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
+173
-128
@@ -1,9 +1,11 @@
|
||||
import * as d3 from 'd3';
|
||||
import _ from 'lodash';
|
||||
import { utilQsString } from '../util/index';
|
||||
import { utilQsString } from '../util';
|
||||
|
||||
|
||||
var endpoint = 'https://taginfo.openstreetmap.org/api/4/',
|
||||
var apibase = 'https://taginfo.openstreetmap.org/api/4/',
|
||||
inflight = {},
|
||||
popularKeys = {},
|
||||
taginfoCache = {},
|
||||
tag_sorts = {
|
||||
point: 'count_nodes',
|
||||
@@ -33,31 +35,31 @@ var endpoint = 'https://taginfo.openstreetmap.org/api/4/',
|
||||
};
|
||||
|
||||
|
||||
function sets(parameters, n, o) {
|
||||
if (parameters.geometry && o[parameters.geometry]) {
|
||||
parameters[n] = o[parameters.geometry];
|
||||
function sets(params, n, o) {
|
||||
if (params.geometry && o[params.geometry]) {
|
||||
params[n] = o[params.geometry];
|
||||
}
|
||||
return parameters;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
function setFilter(parameters) {
|
||||
return sets(parameters, 'filter', tag_filters);
|
||||
function setFilter(params) {
|
||||
return sets(params, 'filter', tag_filters);
|
||||
}
|
||||
|
||||
|
||||
function setSort(parameters) {
|
||||
return sets(parameters, 'sortname', tag_sorts);
|
||||
function setSort(params) {
|
||||
return sets(params, 'sortname', tag_sorts);
|
||||
}
|
||||
|
||||
|
||||
function setSortMembers(parameters) {
|
||||
return sets(parameters, 'sortname', tag_sort_members);
|
||||
function setSortMembers(params) {
|
||||
return sets(params, 'sortname', tag_sort_members);
|
||||
}
|
||||
|
||||
|
||||
function clean(parameters) {
|
||||
return _.omit(parameters, 'geometry', 'debounce');
|
||||
function clean(params) {
|
||||
return _.omit(params, 'geometry', 'debounce');
|
||||
}
|
||||
|
||||
|
||||
@@ -129,146 +131,189 @@ function sortKeys(a, b) {
|
||||
}
|
||||
|
||||
|
||||
var debounced = _.debounce(d3.json, 100, true);
|
||||
var debouncedRequest = _.debounce(request, 500, { leading: false });
|
||||
|
||||
function request(url, params, exactMatch, callback, loaded) {
|
||||
if (inflight[url]) return;
|
||||
|
||||
if (checkCache(url, params, exactMatch, callback)) return;
|
||||
|
||||
inflight[url] = d3.json(url, function (err, data) {
|
||||
delete inflight[url];
|
||||
loaded(err, data);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function request(url, debounce, callback) {
|
||||
if (taginfoCache[url]) {
|
||||
callback(null, taginfoCache[url]);
|
||||
} else if (debounce) {
|
||||
debounced(url, done);
|
||||
} else {
|
||||
d3.json(url, done);
|
||||
}
|
||||
function checkCache(url, params, exactMatch, callback) {
|
||||
var rp = params.rp || 25,
|
||||
testQuery = params.query || '',
|
||||
testUrl = url;
|
||||
|
||||
function done(err, data) {
|
||||
if (!err) {
|
||||
taginfoCache[url] = data;
|
||||
do {
|
||||
var hit = taginfoCache[testUrl];
|
||||
|
||||
// exact match, or shorter match yielding fewer than max results (rp)
|
||||
if (hit && (url === testUrl || hit.length < rp)) {
|
||||
callback(null, hit);
|
||||
return true;
|
||||
}
|
||||
callback(err, data);
|
||||
}
|
||||
|
||||
// don't try to shorten the query
|
||||
if (exactMatch || !testQuery.length) return false;
|
||||
|
||||
// do shorten the query to see if we already have a cached result
|
||||
// that has returned fewer than max results (rp)
|
||||
testQuery = testQuery.slice(0, -1);
|
||||
testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
|
||||
} while (testQuery.length >= 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
|
||||
init: function() { taginfoCache = {}; },
|
||||
reset: function() { taginfoCache = {}; },
|
||||
init: function() {
|
||||
inflight = {};
|
||||
taginfoCache = {};
|
||||
popularKeys = {};
|
||||
|
||||
|
||||
keys: function(parameters, callback) {
|
||||
var debounce = parameters.debounce;
|
||||
parameters = clean(setSort(parameters));
|
||||
request(endpoint + 'keys/all?' +
|
||||
utilQsString(_.extend({
|
||||
rp: 10,
|
||||
sortname: 'count_all',
|
||||
sortorder: 'desc',
|
||||
page: 1
|
||||
}, parameters)), debounce, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
var f = filterKeys(parameters.filter);
|
||||
callback(null, d.data.filter(f).sort(sortKeys).map(valKey));
|
||||
}
|
||||
}
|
||||
);
|
||||
// Fetch popular keys. We'll exclude these from `values`
|
||||
// lookups because they stress taginfo, and they aren't likely
|
||||
// to yield meaningful autocomplete results.. see #3955
|
||||
var params = { rp: 100, sortname: 'values_all', sortorder: 'desc', page: 1, debounce: false };
|
||||
this.keys(params, function(err, data) {
|
||||
if (err) return;
|
||||
data.forEach(function(d) {
|
||||
if (d === 'opening_hours') return; // exception
|
||||
popularKeys[d.value] = true;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
multikeys: function(parameters, callback) {
|
||||
var debounce = parameters.debounce;
|
||||
parameters = clean(setSort(parameters));
|
||||
var prefix = parameters.query;
|
||||
request(endpoint + 'keys/all?' +
|
||||
utilQsString(_.extend({
|
||||
rp: 25,
|
||||
sortname: 'count_all',
|
||||
sortorder: 'desc',
|
||||
page: 1
|
||||
}, parameters)), debounce, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
var f = filterMultikeys(prefix);
|
||||
callback(null, d.data.filter(f).map(valKey));
|
||||
}
|
||||
}
|
||||
);
|
||||
reset: function() {
|
||||
_.forEach(inflight, function(req) { req.abort(); });
|
||||
inflight = {};
|
||||
},
|
||||
|
||||
|
||||
values: function(parameters, callback) {
|
||||
var debounce = parameters.debounce;
|
||||
parameters = clean(setSort(setFilter(parameters)));
|
||||
request(endpoint + 'key/values?' +
|
||||
utilQsString(_.extend({
|
||||
rp: 25,
|
||||
sortname: 'count_all',
|
||||
sortorder: 'desc',
|
||||
page: 1
|
||||
}, parameters)), debounce, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
// In most cases we prefer taginfo value results with lowercase letters.
|
||||
// A few OSM keys expect values to contain uppercase values (see #3377).
|
||||
// This is not an exhaustive list (e.g. `name` also has uppercase values)
|
||||
// but these are the fields where taginfo value lookup is most useful.
|
||||
var re = /network|taxon|genus|species|brand|grape_variety|_hours|_times/;
|
||||
var allowUpperCase = (parameters.key.match(re) !== null);
|
||||
var f = filterValues(allowUpperCase);
|
||||
callback(null, d.data.filter(f).map(valKeyDescription));
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
keys: function(params, callback) {
|
||||
var doRequest = params.debounce ? debouncedRequest : request;
|
||||
params = clean(setSort(params));
|
||||
params = _.extend({ rp: 10, sortname: 'count_all', sortorder: 'desc', page: 1 }, params);
|
||||
|
||||
|
||||
roles: function(parameters, callback) {
|
||||
var debounce = parameters.debounce;
|
||||
var geometry = parameters.geometry;
|
||||
parameters = clean(setSortMembers(parameters));
|
||||
request(endpoint + 'relation/roles?' +
|
||||
utilQsString(_.extend({
|
||||
rp: 25,
|
||||
sortname: 'count_all_members',
|
||||
sortorder: 'desc',
|
||||
page: 1
|
||||
}, parameters)), debounce, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
var f = filterRoles(geometry);
|
||||
callback(null, d.data.filter(f).map(roleKey));
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
docs: function(parameters, callback) {
|
||||
var debounce = parameters.debounce;
|
||||
parameters = clean(setSort(parameters));
|
||||
|
||||
var path = 'key/wiki_pages?';
|
||||
if (parameters.value) path = 'tag/wiki_pages?';
|
||||
else if (parameters.rtype) path = 'relation/wiki_pages?';
|
||||
|
||||
request(endpoint + path + utilQsString(parameters), debounce, function(err, d) {
|
||||
var url = apibase + 'keys/all?' + utilQsString(params);
|
||||
doRequest(url, params, false, callback, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
var f = filterKeys(params.filter);
|
||||
var result = d.data.filter(f).sort(sortKeys).map(valKey);
|
||||
taginfoCache[url] = result;
|
||||
callback(null, result);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
multikeys: function(params, callback) {
|
||||
var doRequest = params.debounce ? debouncedRequest : request;
|
||||
params = clean(setSort(params));
|
||||
params = _.extend({ rp: 25, sortname: 'count_all', sortorder: 'desc', page: 1 }, params);
|
||||
var prefix = params.query;
|
||||
|
||||
var url = apibase + 'keys/all?' + utilQsString(params);
|
||||
doRequest(url, params, true, callback, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
var f = filterMultikeys(prefix);
|
||||
var result = d.data.filter(f).map(valKey);
|
||||
taginfoCache[url] = result;
|
||||
callback(null, result);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
values: function(params, callback) {
|
||||
// Exclude popular keys from values lookups.. see #3955
|
||||
var key = params.key;
|
||||
if (key && popularKeys[key]) {
|
||||
callback(null, []);
|
||||
return;
|
||||
}
|
||||
|
||||
var doRequest = params.debounce ? debouncedRequest : request;
|
||||
params = clean(setSort(setFilter(params)));
|
||||
params = _.extend({ rp: 25, sortname: 'count_all', sortorder: 'desc', page: 1 }, params);
|
||||
|
||||
var url = apibase + 'key/values?' + utilQsString(params);
|
||||
doRequest(url, params, false, callback, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
// In most cases we prefer taginfo value results with lowercase letters.
|
||||
// A few OSM keys expect values to contain uppercase values (see #3377).
|
||||
// This is not an exhaustive list (e.g. `name` also has uppercase values)
|
||||
// but these are the fields where taginfo value lookup is most useful.
|
||||
var re = /network|taxon|genus|species|brand|grape_variety|_hours|_times/;
|
||||
var allowUpperCase = (params.key.match(re) !== null);
|
||||
var f = filterValues(allowUpperCase);
|
||||
|
||||
var result = d.data.filter(f).map(valKeyDescription);
|
||||
taginfoCache[url] = result;
|
||||
callback(null, result);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
roles: function(params, callback) {
|
||||
var doRequest = params.debounce ? debouncedRequest : request;
|
||||
var geometry = params.geometry;
|
||||
params = clean(setSortMembers(params));
|
||||
params = _.extend({ rp: 25, sortname: 'count_all_members', sortorder: 'desc', page: 1 }, params);
|
||||
|
||||
var url = apibase + 'relation/roles?' + utilQsString(params);
|
||||
doRequest(url, params, true, callback, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
var f = filterRoles(geometry);
|
||||
var result = d.data.filter(f).map(roleKey);
|
||||
taginfoCache[url] = result;
|
||||
callback(null, result);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
docs: function(params, callback) {
|
||||
var doRequest = params.debounce ? debouncedRequest : request;
|
||||
params = clean(setSort(params));
|
||||
|
||||
var path = 'key/wiki_pages?';
|
||||
if (params.value) path = 'tag/wiki_pages?';
|
||||
else if (params.rtype) path = 'relation/wiki_pages?';
|
||||
|
||||
var url = apibase + path + utilQsString(params);
|
||||
doRequest(url, params, true, callback, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
taginfoCache[url] = d.data;
|
||||
callback(null, d.data);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
endpoint: function(_) {
|
||||
if (!arguments.length) return endpoint;
|
||||
endpoint = _;
|
||||
apibase: function(_) {
|
||||
if (!arguments.length) return apibase;
|
||||
apibase = _;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+136
-61
@@ -4,7 +4,16 @@ describe('iD.serviceTaginfo', function() {
|
||||
beforeEach(function() {
|
||||
server = sinon.fakeServer.create();
|
||||
taginfo = iD.services.taginfo;
|
||||
taginfo.reset();
|
||||
|
||||
// prepopulate popular keys list with "name"
|
||||
taginfo.init();
|
||||
server.respondWith('GET',
|
||||
new RegExp('\/keys\/all.*sortname=values_all'),
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"count_all":56136034,"key":"name","count_all_fraction":0.0132}]}']
|
||||
);
|
||||
server.respond();
|
||||
server = sinon.fakeServer.create();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
@@ -15,55 +24,67 @@ describe('iD.serviceTaginfo', function() {
|
||||
return iD.utilStringQs(url.substring(url.indexOf('?') + 1));
|
||||
}
|
||||
|
||||
|
||||
describe('#keys', function() {
|
||||
it('calls the given callback with the results of the keys query', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.keys({query: 'amen'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/keys/all'),
|
||||
server.respondWith('GET', /\/keys\/all/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0}]}']);
|
||||
'{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(query(server.requests[0].url)).to.eql(
|
||||
{query: 'amen', page: '1', rp: '10', sortname: 'count_all', sortorder: 'desc'});
|
||||
expect(callback).to.have.been.calledWith(null, [{'title':'amenity', 'value':'amenity'}]);
|
||||
{query: 'amen', page: '1', rp: '10', sortname: 'count_all', sortorder: 'desc'}
|
||||
);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'title':'amenity', 'value':'amenity'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('includes popular keys', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.keys({query: 'amen'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/keys/all'),
|
||||
server.respondWith('GET', /\/keys\/all/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},'
|
||||
+ '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes_fraction":0.0}]}']);
|
||||
'{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0,"count_nodes_fraction":1.0},'
|
||||
+ '{"count_all":1,"key":"amenityother","count_all_fraction":0.0,"count_nodes_fraction":0.0}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'title':'amenity', 'value':'amenity'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'title':'amenity', 'value':'amenity'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('includes popular keys with an entity type filter', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.keys({query: 'amen', filter: 'nodes'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/keys/all'),
|
||||
server.respondWith('GET', /\/keys\/all/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"count_all":5190337,"count_nodes":500000,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},'
|
||||
+ '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes":100}]}']);
|
||||
+ '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes":100}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'title':'amenity', 'value':'amenity'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'title':'amenity', 'value':'amenity'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('includes unpopular keys with a wiki page', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.keys({query: 'amen'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/keys/all'),
|
||||
server.respondWith('GET', /\/keys\/all/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},'
|
||||
+ '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes_fraction":0.0, "in_wiki": true}]}']);
|
||||
+ '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes_fraction":0.0, "in_wiki": true}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [
|
||||
@@ -76,12 +97,16 @@ describe('iD.serviceTaginfo', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.keys({query: 'ref'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/keys/all'),
|
||||
server.respondWith('GET', /\/keys\/all/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"key":"ref:bag","count_all":9790586,"count_all_fraction":0.0028},{"key":"ref","count_all":7933528,"count_all_fraction":0.0023}]}']);
|
||||
'{"data":[{"key":"ref:bag","count_all":9790586,"count_all_fraction":0.0028},' +
|
||||
'{"key":"ref","count_all":7933528,"count_all_fraction":0.0023}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'title':'ref', 'value':'ref'},{'title':'ref:bag', 'value':'ref:bag'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'title':'ref', 'value':'ref'},{'title':'ref:bag', 'value':'ref:bag'}]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,40 +115,50 @@ describe('iD.serviceTaginfo', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.multikeys({query: 'recycling:'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/keys/all'),
|
||||
server.respondWith('GET', /\/keys\/all/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"count_all":69593,"key":"recycling:glass","count_all_fraction":0.0}]}']);
|
||||
'{"data":[{"count_all":69593,"key":"recycling:glass","count_all_fraction":0.0}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(query(server.requests[0].url)).to.eql(
|
||||
{query: 'recycling:', page: '1', rp: '25', sortname: 'count_all', sortorder: 'desc'});
|
||||
expect(callback).to.have.been.calledWith(null, [{'title':'recycling:glass', 'value':'recycling:glass'}]);
|
||||
{query: 'recycling:', page: '1', rp: '25', sortname: 'count_all', sortorder: 'desc'}
|
||||
);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'title':'recycling:glass', 'value':'recycling:glass'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('excludes multikeys with extra colons', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.multikeys({query: 'service:bicycle:'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/keys/all'),
|
||||
server.respondWith('GET', /\/keys\/all/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"count_all":4426,"key":"service:bicycle:retail","count_all_fraction":0.0},'
|
||||
+ '{"count_all":22,"key":"service:bicycle:retail:ebikes","count_all_fraction":0.0}]}']);
|
||||
'{"data":[{"count_all":4426,"key":"service:bicycle:retail","count_all_fraction":0.0},' +
|
||||
'{"count_all":22,"key":"service:bicycle:retail:ebikes","count_all_fraction":0.0}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'title':'service:bicycle:retail', 'value':'service:bicycle:retail'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'title':'service:bicycle:retail', 'value':'service:bicycle:retail'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('excludes multikeys with wrong prefix', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.multikeys({query: 'service:bicycle:'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/keys/all'),
|
||||
server.respondWith('GET', /\/keys\/all/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"count_all":4426,"key":"service:bicycle:retail","count_all_fraction":0.0},'
|
||||
+ '{"count_all":22,"key":"disused:service:bicycle","count_all_fraction":0.0}]}']);
|
||||
'{"data":[{"count_all":4426,"key":"service:bicycle:retail","count_all_fraction":0.0},' +
|
||||
'{"count_all":22,"key":"disused:service:bicycle","count_all_fraction":0.0}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'title':'service:bicycle:retail', 'value':'service:bicycle:retail'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'title':'service:bicycle:retail', 'value':'service:bicycle:retail'}]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,37 +167,59 @@ describe('iD.serviceTaginfo', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.values({key: 'amenity', query: 'par'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/key/values'),
|
||||
server.respondWith('GET', /\/key\/values/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"value":"parking","description":"A place for parking cars", "fraction":0.1}]}']);
|
||||
'{"data":[{"value":"parking","description":"A place for parking cars", "fraction":0.1}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(query(server.requests[0].url)).to.eql(
|
||||
{key: 'amenity', query: 'par', page: '1', rp: '25', sortname: 'count_all', sortorder: 'desc'});
|
||||
expect(callback).to.have.been.calledWith(null, [{'value':'parking','title':'A place for parking cars'}]);
|
||||
{key: 'amenity', query: 'par', page: '1', rp: '25', sortname: 'count_all', sortorder: 'desc'}
|
||||
);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'value':'parking','title':'A place for parking cars'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('includes popular values', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.values({key: 'amenity', query: 'par'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/key/values'),
|
||||
server.respondWith('GET', /\/key\/values/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"value":"parking","description":"A place for parking cars", "fraction":1.0},' +
|
||||
'{"value":"party","description":"A place for partying", "fraction":0.0}]}']);
|
||||
'{"value":"party","description":"A place for partying", "fraction":0.0}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'value':'parking','title':'A place for parking cars'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'value':'parking','title':'A place for parking cars'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('does not get values for extremely popular keys', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.values({key: 'name', query: 'ste'}, callback);
|
||||
|
||||
server.respondWith('GET', /\/key\/values/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"value":"Rue Pasteur","description":"", "fraction":0.0001},' +
|
||||
'{"value":"Via Trieste","description":"", "fraction":0.0001}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, []);
|
||||
});
|
||||
|
||||
it('includes unpopular values with a wiki page', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.values({key: 'amenity', query: 'par'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/key/values'),
|
||||
server.respondWith('GET', /\/key\/values/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"value":"parking","description":"A place for parking cars", "fraction":1.0},'
|
||||
+ '{"value":"party","description":"A place for partying", "fraction":0.0, "in_wiki": true}]}']);
|
||||
'{"data":[{"value":"parking","description":"A place for parking cars", "fraction":1.0},' +
|
||||
'{"value":"party","description":"A place for partying", "fraction":0.0, "in_wiki": true}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [
|
||||
@@ -175,29 +232,33 @@ describe('iD.serviceTaginfo', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.values({key: 'amenity', query: 'par'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/key/values'),
|
||||
server.respondWith('GET', /\/key\/values/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"value":"parking","description":"A place for parking cars", "fraction":0.2},'
|
||||
+ '{"value":"PArking","description":"A common mispelling", "fraction":0.2},'
|
||||
+ '{"value":"parking;partying","description":"A place for parking cars *and* partying", "fraction":0.2},'
|
||||
+ '{"value":"parking, partying","description":"A place for parking cars *and* partying", "fraction":0.2},'
|
||||
+ '{"value":"*","description":"", "fraction":0.2}]}']);
|
||||
+ '{"value":"*","description":"", "fraction":0.2}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'value':'parking','title':'A place for parking cars'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'value':'parking','title':'A place for parking cars'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('includes network values with capital letters and some punctuation', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.values({key: 'network', query: 'us'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/key/values'),
|
||||
server.respondWith('GET', /\/key\/values/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"value":"US:TX:FM","description":"Farm to Market Roads in the U.S. state of Texas.", "fraction":0.34},'
|
||||
+ '{"value":"US:KY","description":"Primary and secondary state highways in the U.S. state of Kentucky.", "fraction":0.31},'
|
||||
+ '{"value":"US:US","description":"U.S. routes in the United States.", "fraction":0.19},'
|
||||
+ '{"value":"US:I","description":"Interstate highways in the United States.", "fraction":0.11},'
|
||||
+ '{"value":"US:MD","description":"State highways in the U.S. state of Maryland.", "fraction":0.06}]}']);
|
||||
+ '{"value":"US:MD","description":"State highways in the U.S. state of Maryland.", "fraction":0.06}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [
|
||||
@@ -213,36 +274,45 @@ describe('iD.serviceTaginfo', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.values({key: 'genus', query: 'qu'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/key/values'),
|
||||
server.respondWith('GET', /\/key\/values/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"value":"Quercus","description":"Oak", "fraction":0.5}]}']);
|
||||
'{"data":[{"value":"Quercus","description":"Oak", "fraction":0.5}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'value':'Quercus','title':'Oak'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'value':'Quercus','title':'Oak'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('includes biological taxon values with capital letters', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.values({key: 'taxon', query: 'qu'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/key/values'),
|
||||
server.respondWith('GET', /\/key\/values/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"value":"Quercus robur","description":"Oak", "fraction":0.5}]}']);
|
||||
'{"data":[{"value":"Quercus robur","description":"Oak", "fraction":0.5}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'value':'Quercus robur','title':'Oak'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'value':'Quercus robur','title':'Oak'}]
|
||||
);
|
||||
});
|
||||
|
||||
it('includes biological species values with capital letters', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.values({key: 'species', query: 'qu'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/key/values'),
|
||||
server.respondWith('GET', /\/key\/values/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"value":"Quercus robur","description":"Oak", "fraction":0.5}]}']);
|
||||
'{"data":[{"value":"Quercus robur","description":"Oak", "fraction":0.5}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(callback).to.have.been.calledWith(null, [{'value':'Quercus robur','title':'Oak'}]);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'value':'Quercus robur','title':'Oak'}]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -251,14 +321,16 @@ describe('iD.serviceTaginfo', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.roles({rtype: 'route', query: 's', geometry: 'relation'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/relation/roles'),
|
||||
server.respondWith('GET', /\/relation\/roles/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"role":"stop","count_relation_members_fraction":0.1757},' +
|
||||
'{"role":"south","count_relation_members_fraction":0.0035}]}']);
|
||||
'{"role":"south","count_relation_members_fraction":0.0035}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(query(server.requests[0].url)).to.eql(
|
||||
{rtype: 'route', query: 's', page: '1', rp: '25', sortname: 'count_relation_members', sortorder: 'desc'});
|
||||
{rtype: 'route', query: 's', page: '1', rp: '25', sortname: 'count_relation_members', sortorder: 'desc'}
|
||||
);
|
||||
expect(callback).to.have.been.calledWith(null, [
|
||||
{'value': 'stop', 'title': 'stop'},
|
||||
{'value': 'south', 'title': 'south'}
|
||||
@@ -271,15 +343,18 @@ describe('iD.serviceTaginfo', function() {
|
||||
var callback = sinon.spy();
|
||||
taginfo.docs({key: 'amenity', value: 'parking'}, callback);
|
||||
|
||||
server.respondWith('GET', new RegExp('https://taginfo.openstreetmap.org/api/4/tag/wiki_page'),
|
||||
server.respondWith('GET', /\/tag\/wiki_page/,
|
||||
[200, { 'Content-Type': 'application/json' },
|
||||
'{"data":[{"on_way":false,"lang":"en","on_area":true,"image":"File:Car park2.jpg"}]}']);
|
||||
'{"data":[{"on_way":false,"lang":"en","on_area":true,"image":"File:Car park2.jpg"}]}']
|
||||
);
|
||||
server.respond();
|
||||
|
||||
expect(query(server.requests[0].url)).to.eql(
|
||||
{key: 'amenity', value: 'parking'});
|
||||
expect(callback).to.have.been.calledWith(null,
|
||||
[{'on_way':false,'lang':'en','on_area':true,'image':'File:Car park2.jpg'}]);
|
||||
{key: 'amenity', value: 'parking'}
|
||||
);
|
||||
expect(callback).to.have.been.calledWith(
|
||||
null, [{'on_way':false,'lang':'en','on_area':true,'image':'File:Car park2.jpg'}]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user