Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for OpenCage geocoder #7015

Merged
merged 5 commits into from
Sep 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Change Log

##### Additions :tada:
* Added `cartographicLimitRectangle` to `Globe`. Use this to limit terrain and imagery to a specific `Rectangle` area. [#6987](https://github.com/AnalyticalGraphicsInc/cesium/pull/6987)
* Added `OpenCageGeocoderService`, which provides geocoding via [OpenCage](https://opencagedata.com/). [#7015](https://github.com/AnalyticalGraphicsInc/cesium/pull/7015)

##### Fixes :wrench:
* Fixed an issue in the 3D Tiles traversal where empty tiles would be selected instead of their nearest loaded ancestors. [#7011](https://github.com/AnalyticalGraphicsInc/cesium/pull/7011)
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu
* [Jeremy Marzano](https://github.com/JeremyMarzano-ISPA/)
* [Orbit Logic](http://www.orbitlogic.com)
* [Roderick Green](https://github.com/roderickgreen/)
* [Hexastack](https://www.hexastack.com)
* [Mohamed Marrouchi](https://github.com/marrouchi/)

## [Individual CLA](Documentation/Contributors/CLAs/individual-cla-agi-v1.0.txt)
* [Victor Berchet](https://github.com/vicb)
Expand Down
1 change: 1 addition & 0 deletions Source/Core/GeocoderService.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ define([
*
* @see BingMapsGeocoderService
* @see PeliasGeocoderService
* @see OpenCageGeocoderService
*/
function GeocoderService() {
}
Expand Down
128 changes: 128 additions & 0 deletions Source/Core/OpenCageGeocoderService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
define([
'./Cartesian3',
'./Check',
'./defaultValue',
'./defined',
'./defineProperties',
'./GeocodeType',
'./Rectangle',
'./Resource'
], function (
Cartesian3,
Check,
defaultValue,
defined,
defineProperties,
GeocodeType,
Rectangle,
Resource) {
'use strict';

/**
* Provides geocoding via a {@link https://opencagedata.com/|OpenCage} server.
* @alias OpenCageGeocoderService
* @constructor
*
* @param {Resource|String} url The endpoint to the OpenCage server.
* @param {String} apiKey The OpenCage API Key.
* @param {Object} [params] An object with the following properties (See https://opencagedata.com/api#forward-opt):
* @param {Number} [params.abbrv] When set to 1 we attempt to abbreviate and shorten the formatted string we return.
* @param {Number} [options.add_request] When set to 1 the various request parameters are added to the response for ease of debugging.
* @param {String} [options.bounds] Provides the geocoder with a hint to the region that the query resides in.
* @param {String} [options.countrycode] Restricts the results to the specified country or countries (as defined by the ISO 3166-1 Alpha 2 standard).
* @param {String} [options.jsonp] Wraps the returned JSON with a function name.
* @param {String} [options.language] An IETF format language code.
* @param {Number} [options.limit] The maximum number of results we should return.
* @param {Number} [options.min_confidence] An integer from 1-10. Only results with at least this confidence will be returned.
* @param {Number} [options.no_annotations] When set to 1 results will not contain annotations.
* @param {Number} [options.no_dedupe] When set to 1 results will not be deduplicated.
* @param {Number} [options.no_record] When set to 1 the query contents are not logged.
* @param {Number} [options.pretty] When set to 1 results are 'pretty' printed for easier reading. Useful for debugging.
* @param {String} [options.proximity] Provides the geocoder with a hint to bias results in favour of those closer to the specified location (For example: 41.40139,2.12870).
*
* @example
* // Configure a Viewer to use the OpenCage Geocoder
* var viewer = new Cesium.Viewer('cesiumContainer', {
* geocoder: new Cesium.OpenCageGeocoderService('https://api.opencagedata.com/geocode/v1/', '<API key>')
* });
*/
function OpenCageGeocoderService(url, apiKey, params) {
//>>includeStart('debug', pragmas.debug);
Check.defined('url', url);
Check.defined('apiKey', apiKey);
if (defined(params)) {
Check.typeOf.object('params', params);
}
//>>includeEnd('debug');

url = Resource.createIfNeeded(url);
url.appendForwardSlash();
url.setQueryParameters({key: apiKey});
this._url = url;
this._params = defaultValue(params, {});
}

defineProperties(OpenCageGeocoderService.prototype, {
/**
* The Resource used to access the OpenCage endpoint.
* @type {Resource}
* @memberof {OpenCageGeocoderService.prototype}
* @readonly
*/
url: {
get: function () {
return this._url;
}
},
/**
* Optional params passed to OpenCage in order to customize geocoding
* @type {Object}
* @memberof {OpenCageGeocoderService.prototype}
* @readonly
*/
params: {
get: function () {
return this._params;
}
}
});

/**
* @function
*
* @param {String} query The query to be sent to the geocoder service
* @returns {Promise<GeocoderService~Result[]>}
*/
OpenCageGeocoderService.prototype.geocode = function(query) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.string('query', query);
//>>includeEnd('debug');

var resource = this._url.getDerivedResource({
url: 'json',
queryParameters: Object.assign(this._params, {q: query})
});
return resource.fetchJson()
.then(function (response) {
return response.results.map(function (resultObject) {
var destination;
var bounds = resultObject.bounds;

if (defined(bounds)) {
destination = Rectangle.fromDegrees(bounds.southwest.lng, bounds.southwest.lat, bounds.northeast.lng, bounds.northeast.lat);
} else {
var lon = resultObject.geometry.lat;
var lat = resultObject.geometry.lng;
destination = Cartesian3.fromDegrees(lon, lat);
}

return {
displayName: resultObject.formatted,
destination: destination
};
});
});
};

return OpenCageGeocoderService;
});
75 changes: 75 additions & 0 deletions Specs/Core/OpenCageGeocoderServiceSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
defineSuite([
'Core/OpenCageGeocoderService',
'Core/GeocodeType',
'Core/Cartesian3',
'Core/Resource',
'ThirdParty/when'
], function(
OpenCageGeocoderService,
GeocodeType,
Cartesian3,
Resource,
when) {
'use strict';

var endpoint = 'https://api.opencagedata.com/geocode/v1/';
var apiKey = 'c2a490d593b14612aefa6ec2e6b77c47';

it('constructor throws without url', function() {
expect(function() {
return new OpenCageGeocoderService(undefined);
}).toThrowDeveloperError();
});

it('constructor throws without API Key', function() {
expect(function() {
return new OpenCageGeocoderService(endpoint, undefined);
}).toThrowDeveloperError();
});

it('returns geocoder results', function () {
var service = new OpenCageGeocoderService(endpoint, apiKey);

var query = '-22.6792,+14.5272';
var data = {
results: [{
bounds: {
northeast: {
lat: -22.6790826,
lng: 14.5269016
},
southwest: {
lat: -22.6792826,
lng: 14.5267016
}
},
formatted: 'Beryl\'s Restaurant, Woermann St, Swakopmund, Namibia',
geometry: {
lat: -22.6795394,
lng: 14.5276006
}
}]
};
spyOn(Resource.prototype, 'fetchJson').and.returnValue(when.resolve(data));

return service.geocode(query)
.then(function(results) {
expect(results.length).toEqual(1);
expect(results[0].displayName).toEqual(data.results[0].formatted);
expect(results[0].destination).toBeDefined();
});
});

it('returns no geocoder results if OpenCage has no results', function() {
var service = new OpenCageGeocoderService(endpoint, apiKey);

var query = '';
var data = { results: [] };
spyOn(Resource.prototype, 'fetchJson').and.returnValue(when.resolve(data));

return service.geocode(query)
.then(function(results) {
expect(results.length).toEqual(0);
});
});
});