Fork me on GitHub
Show:

File: ../src/geocode/mapquestgeocodeservice.js

define([
  'aeris/util',
  'aeris/promise',
  'aeris/jsonp',
  'aeris/geocode/config',
  'aeris/errors/invalidconfigerror',
  'aeris/geocode/geocodeserviceresponse',
  'aeris/geocode/geocodeservicestatus'
], function(_, Promise, JSONP, geocodeConfig, InvalidConfigError, GeocodeServiceResponse, GeocodeServiceStatus) {
  /**
   * MapQuest Geocoding Service
   * See http://open.mapquestapi.com/geocoding
   *
   * @publicApi
   * @class aeris.geocode.MapQuestGeocodeService
   * @implements aeris.geocode.GeocodeServiceInterface
   *
   * @constructor
   * @param {Object=} opt_options
   * @param {string} opt_options.apiId
   *                 Mapquest apiId can alternatively be configured in 'aeris/geocode/config'
   *                 via RequireJS `config` option.
   */
  var MapQuestGeocodeService = function(opt_options) {
    var options = _.defaults(opt_options || {}, {
      apiId: geocodeConfig.get('apiId')
    });


    /**
     * The MapQuest API id.
     *
     * @property apiId_
     * @type {*|options.apiId}
     * @private
     */
    this.apiId_ = options.apiId;

    /**
     * Base URL for MapQuest Geocoding service.
     * @type {string}
     * @private
     * @property serviceUrl_
     */
    this.serviceUrl_ = '//open.mapquestapi.com/geocoding/v1/address';


    /**
     * JSONP service.
     *
     * @type {Object} See {aeris.JSONP} for expected behavior.
     * @property {Function} get
     * @protected
     * @property jsonp_
     */
    this.jsonp_ = JSONP;
  };


  /**
   * @throws {aeris.errors.InvalidConfigError} If no Mapquest apiId is provided
   * @override
   * @method geocode
   */
  MapQuestGeocodeService.prototype.geocode = function(location) {
    var promise = new Promise();
    var uri = this.getMapQuestUri_();
    var query = { location: location };

    this.jsonp_.get(uri, query, _.bind(function(res) {
      if (!res || !res.info || _.isUndefined(res.info.statuscode)) {
        promise.reject(this.createUnexpectedResultsError_(res));
      }
      else if (this.isStatusCodeErrorResponse_(res)) {
        promise.reject(this.createStatusCodeError_(res));
      }
      else if (this.isNoResultsErrorResponse_(res)) {
        promise.reject(this.createNoResultsError_(res));
      }
      else {
        promise.resolve(this.createGeocodeResponse_(res));
      }
    }, this));

    return promise;
  };


  /**
   * @private
   * @return {string}
   * @method getMapQuestUri_
   */
  MapQuestGeocodeService.prototype.getMapQuestUri_ = function() {
    this.ensureApiId_();
    return this.serviceUrl_ + '?key=' + this.apiId_;
  };


  /**
   * @throws {aeris.errors.InvalidConfigError}
   * @private
   * @method ensureApiId_
   */
  MapQuestGeocodeService.prototype.ensureApiId_ = function() {
    this.apiId_ = this.apiId_ || geocodeConfig.get('apiId');

    if (!this.apiId_) {
      throw new InvalidConfigError('The MapQuestGeocodeService requires an apiId');
    }
  };


  /**
   * @private
   * @param {Object} res
   * @return {Boolean}
   * @method isStatusCodeErrorResponse_
   */
  MapQuestGeocodeService.prototype.isStatusCodeErrorResponse_ = function(res) {
    var isResDefined = res && res.info;
    var statusCode = isResDefined ? parseInt(res.info.statuscode) : null;

    return !_.isNumeric(statusCode) || statusCode >= 400;
  };


  /**
   * @private
   * @param {Object} res
   * @return {Boolean}
   * @method isNoResultsErrorResponse_
   */
  MapQuestGeocodeService.prototype.isNoResultsErrorResponse_ = function(res) {
    var isResDefined = res && res.results;
    return isResDefined &&
      (!res.results.length || !res.results[0].locations || !res.results[0].locations.length);
  };


  /**
   * @private
   * @param {Object} res
   * @return {aeris.geocode.GeocodeServiceResponse}
   * @method createUnexpectedResultsError_
   */
  MapQuestGeocodeService.prototype.createUnexpectedResultsError_ = function(res) {
    return new GeocodeServiceResponse({
      latLon: [],
      status: {
        code: GeocodeServiceStatus.API_ERROR,
        apiCode: '',
        message: 'The MapQuest Geolocation API returned an unexpected response.'
      }
    });
  };


  /**
   * @private
   * @param {Object} res
   * @return {aeris.geocode.GeocodeServiceResponse}
   * @method createStatusCodeError_
   */
  MapQuestGeocodeService.prototype.createStatusCodeError_ = function(res) {
    var statusMap = {
      0: GeocodeServiceStatus.OK,
      400: GeocodeServiceStatus.INVALID_REQUEST,
      403: GeocodeServiceStatus.API_ERROR,
      500: GeocodeServiceStatus.API_ERROR
    };

    return new GeocodeServiceResponse({
      latLon: [],
      status: {
        code: statusMap[res.info.statuscode] || GeocodeServiceStatus.API_ERROR,
        apiCode: res.info.statuscode,
        message: res.info.messages.join('; ')
      }
    });
  };


  /**
   * @private
   * @param {Object} res
   * @return {aeris.geocode.GeocodeServiceResponse}
   * @method createNoResultsError_
   */
  MapQuestGeocodeService.prototype.createNoResultsError_ = function(res) {
    return new GeocodeServiceResponse({
      latLon: [],
      status: {
        code: GeocodeServiceStatus.NO_RESULTS,
        apiCode: res.info.statuscode,
        message: res.info.messages.join('; ')
      }
    });
  };


  /**
   * @private
   * @param {Object} res
   * @return {aeris.geocode.GeocodeServiceResponse}
   * @method createGeocodeResponse_
   */
  MapQuestGeocodeService.prototype.createGeocodeResponse_ = function(res) {
    var geocodedLocation = res.results[0].locations[0];

    return new GeocodeServiceResponse({
      latLon: [
        parseFloat(geocodedLocation.latLng.lat),
        parseFloat(geocodedLocation.latLng.lng)
      ],
      status: {
        code: GeocodeServiceStatus.OK,
        apiCode: res.info.statuscode,
        message: res.info.messages.join('; ')
      }
    });
  };


  /**
   * Set jsonp service
   *
   * @param {Object} jsonp
   * @method setJSONP
   */
  MapQuestGeocodeService.prototype.setJSONP = function(jsonp) {
    this.jsonp_ = jsonp;
  };


  return _.expose(MapQuestGeocodeService, 'aeris.geocode.MapQuestGeocodeService');
});