From 1c303edf18eb90710bdf139b50a52446a86a56a0 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 29 Jun 2017 01:23:33 -0400 Subject: [PATCH] Add generic reverse geocoder function to nominatim service --- modules/services/nominatim.js | 34 ++++++++++++++++++++---------- test/spec/services/nominatim.js | 37 ++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/modules/services/nominatim.js b/modules/services/nominatim.js index b5434306a..4eb13eedd 100644 --- a/modules/services/nominatim.js +++ b/modules/services/nominatim.js @@ -25,32 +25,44 @@ export default { countryCode: function (location, callback) { - var countryCodes = nominatimCache.search( + this.reverse(location, function(err, result) { + if (err) { + return callback(err); + } else if (result.address) { + return callback(null, result.address.country_code); + } else { + return callback('Unable to geocode', null); + } + }); + }, + + + reverse: function (location, callback) { + var cached = nominatimCache.search( { minX: location[0], minY: location[1], maxX: location[0], maxY: location[1] } ); - if (countryCodes.length > 0) { - return callback(null, countryCodes[0].data); + if (cached.length > 0) { + return callback(null, cached[0].data); } - var params = { format: 'json', addressdetails: 1, lat: location[1], lon: location[0] }; + var params = { zoom: 13, format: 'json', addressdetails: 1, lat: location[1], lon: location[0] }; var url = apibase + 'reverse?' + utilQsString(params); if (inflight[url]) return; inflight[url] = d3.json(url, function(err, result) { delete inflight[url]; - if (err) + if (err) { return callback(err); - else if (result && result.error) + } 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 } - )); + var extent = geoExtent(location).padByMeters(200); + nominatimCache.insert(_.assign(extent.bbox(), {data: result})); - callback(null, result.address.country_code); + callback(null, result); }); }, diff --git a/test/spec/services/nominatim.js b/test/spec/services/nominatim.js index 554479834..37e5a60ee 100644 --- a/test/spec/services/nominatim.js +++ b/test/spec/services/nominatim.js @@ -17,7 +17,6 @@ describe('iD.serviceNominatim', function() { describe('#countryCode', function() { - it('calls the given callback with the results of the country code query', function() { var callback = sinon.spy(); nominatim.countryCode([16, 48], callback); @@ -28,13 +27,15 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '48', lon: '16'}); + {zoom: '13', format: 'json', addressdetails: '1', lat: '48', lon: '16'}); expect(callback).to.have.been.calledWithExactly(null, 'at'); }); + }); - it('should not cache the first country code result', function() { + describe('#reverse', function() { + it('should not cache distant result', function() { var callback = sinon.spy(); - nominatim.countryCode([16, 48], callback); + nominatim.reverse([16, 48], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, @@ -42,13 +43,14 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '48', lon: '16'}); - expect(callback).to.have.been.calledWithExactly(null, 'at'); + {zoom: '13', format: 'json', addressdetails: '1', lat: '48', lon: '16'}); + expect(callback).to.have.been.calledWithExactly(null, {address: {country_code:'at'}}); server.restore(); server = sinon.fakeServer.create(); - nominatim.countryCode([17, 49], callback); + callback = sinon.spy(); + nominatim.reverse([17, 49], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, @@ -56,13 +58,13 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '49', lon: '17'}); - expect(callback).to.have.been.calledWithExactly(null, 'cz'); + {zoom: '13', format: 'json', addressdetails: '1', lat: '49', lon: '17'}); + expect(callback).to.have.been.calledWithExactly(null, {address: {country_code:'cz'}}); }); - it('should cache the first country code result', function() { + it('should cache nearby result', function() { var callback = sinon.spy(); - nominatim.countryCode([16, 48], callback); + nominatim.reverse([16, 48], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, @@ -70,24 +72,25 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '48', lon: '16'}); - expect(callback).to.have.been.calledWithExactly(null, 'at'); + {zoom: '13', format: 'json', addressdetails: '1', lat: '48', lon: '16'}); + expect(callback).to.have.been.calledWithExactly(null, {address: {country_code:'at'}}); server.restore(); server = sinon.fakeServer.create(); - nominatim.countryCode([16.01, 48.01], callback); + callback = sinon.spy(); + nominatim.reverse([16.000001, 48.000001], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, '{"address":{"country_code":"cz"}}']); server.respond(); - expect(callback).to.have.been.calledWithExactly(null, 'at'); + expect(callback).to.have.been.calledWithExactly(null, {address: {country_code:'at'}}); }); it('calls the given callback with an error', function() { var callback = sinon.spy(); - nominatim.countryCode([1000, 1000], callback); + nominatim.reverse([1000, 1000], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, @@ -95,7 +98,7 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '1000', lon: '1000'}); + {zoom: '13', format: 'json', addressdetails: '1', lat: '1000', lon: '1000'}); expect(callback).to.have.been.calledWithExactly('Unable to geocode'); }); });