Source: CensusGeocoder.js

/**
 * @module nyc/CensusGeocoder
 */

import nyc from 'nyc'
import $ from 'jquery'
import Geocoder from 'nyc/Geocoder'

const proj4 = nyc.proj4

/**
 * @desc A class for geocoding using the New York City Geoclient API
 * @public
 * @class
 * @extends module:nyc/Geocoder~Geocoder
 * @fires module:nyc/Locator~Locator#geocoded
 * @fires module:nyc/Locator~Locator#geolocated
 * @fires module:nyc/Locator~Locator#ambiguous
 * @fires module:nyc/Locator~Locator#error
 * @see https://developer.cityofnewyork.us/api/geoclient-api
 */
class CensusGeocoder extends Geocoder {
/**
 * @desc Create an instance of Geoclient
 * @public
 * @constructor
 * @param {module:nyc/Geoclient~Geoclient.Options} options Constructor options
 */
  constructor(options) {
    super(options)
    /**
     * @private
     * @member {string}
     */
    this.url = 'https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?format=json&benchmark=4&vintage=4&address='
    /**
     * @desc The epsg code
     * @public
     * @member {string}
     */
    this.projection = options && options.projection ? options.projection : 'EPSG:3857'
  }
  /**
   * @desc Geocode an input string representing a location
   * @public
   * @override
   * @method
   * @param {string} input The value to geocode
   * @returns {Promise<module:nyc/Locator~Locator.Result|module:nyc/Locator~Locator.Ambiguous>} The result of the search request
   */
  search(input) {
    const me = this
    return new Promise((resolve, reject) => {
      input = input.trim()
      if (input.length === 5 && !isNaN(input)) {
        this.resolveZip(input, resolve)
      } else if (input.length) {
        input = input.replace(/"/g, '').replace(/'/g, '').replace(/&/g, ' and ')
        $.ajax({
          url: `${me.url}${input}`,
          dataType: 'jsonp',
          success: response => {
            me.census(response, resolve)
          },
          error: me.error
        })
      }
    })
  }
  /**
   * @private
   * @method
   * @param {Array<number>} coordinate Coordinate
   * @return {Array<number>} Reprojected coordinate
   */
  project(coordinate) {
    if (coordinate && this.projection !== 'EPSG:4326') {
      return proj4('EPSG:4326', this.projection, coordinate)
    }
    return coordinate
  }
  /**
   * @private
   * @method
   * @param {Object} response Response object
   * @param {function} resolve Resolve
   */
  census(response, resolve) {
    const result = response.result
    const addressMatches = result.addressMatches
    const nothing = {
      type: 'ambiguous',
      input: result.input.address.address,
      possible: []
    }
    if (addressMatches.length) {
      if (addressMatches.length === 1) {
        const location = this.parse(addressMatches[0])
        if (location) {
          location.type = 'geocoded'
          resolve(location)
          this.trigger('geocoded', location)
        } else {
          resolve(nothing)
          this.trigger('ambiguous', nothing)
        }
      } else {
        const ambiguous = {
          type: 'ambiguous',
          input: nothing.input,
          possible: this.possible(addressMatches)
        }
        resolve(ambiguous)
        this.trigger('ambiguous', ambiguous)
      }
    } else {
      resolve(nothing)
      this.trigger('ambiguous', nothing)
    }
  }
  /**
   * @private
   * @method
   * @param {Array<module:nyc.Locator~Locator.Ambiguous>} results Results
   * @returns {Array} Possible results
   */
  possible(results) {
    const possible = []
    results.forEach(result => {
      const location = this.parse(result)
      if (location) {
        possible.push(location)
      }
    })
    return possible
  }
  /**
   * @private
   * @method
   * @param {Object} result Result
   * @return {Locator.Result} Locator result
   */
  parse(result) {
    const p = [result.coordinates.x, result.coordinates.y]
    return {
      type: 'geocoded',
      coordinate: this.project(p),
      data: result,
      name: result.matchedAddress
    }
  }
  /**
   * @private
   * @method
   * @param {function} reject Resolve
   */
  error(reject) {
    const error = {type: 'error', error: arguments}
    console.error('Geoclient error', arguments)
    reject(error)
    this.trigger('error', error)
  }
}

/**
 * @desc Constructor options for {@link module:nyc/CensusGeocoder~CensusGeocoder}
 * @public
 * @typedef {Object}
 * @property {string} [projection=EPSG:3857] The EPSG code of the projection for output geometries (i.e. EPSG:2263)
 */
CensusGeocoder.Options


export default CensusGeocoder