Source: bemap-services-v2/search/bemap-geocoder-v2.js

/**
 * BeNomad BeMap JavaScript API - Geocoder v1.5 extension
 *
 * Augments the existing `bemap.Geocoder` prototype with a Promise-based
 * `geocode(request)` and `revGeocode(request)`. Dispatch is `instanceof`-based,
 * so legacy callers passing `{searchInfo, success, failed}` keep their behaviour.
 *
 * Wire format note: the BeMap v1.5 backend does not expose a dedicated forward
 * geocoding endpoint. The closest equivalent is
 * `POST service/geocoding/autocomplete/1.0` with `addressDetails: true` — the
 * response contains the structured PostalAddress + Coordinate per item, which
 * is what `bemap.Geocoder.geocode()` returns. This matches the Flutter SDK's
 * decision (no `Geocoder` class exists there — only `Autocomplete` +
 * `ReverseGeocoder`).
 *
 * Reverse geocoding goes through `bemap.ReverseGeocoder` (which posts to
 * `service/geocoding/1.0/reverse`).
 *
 * @since 1.5.0
 */
(function() {

    if (typeof bemap === 'undefined' || !bemap.Geocoder) return;

    /** @private */
    bemap.Geocoder.prototype._v2 = function() {
        if (!this._v2Instance) {
            this._v2Instance = new bemap.ServiceV2(this.ctx, {});
        }
        return this._v2Instance;
    };

    var legacyGeocode = bemap.Geocoder.prototype.geocode;
    var legacyRevGeocode = bemap.Geocoder.prototype.revGeocode;

    /**
     * Geocode a structured or free-text request.
     *
     * @public
     * @param {bemap.GeocodingRequest|Object} request When a `GeocodingRequest`
     *   is passed the call returns a Promise. Any other shape falls through to
     *   the legacy callback-style implementation.
     * @param {Object} [options]
     * @param {AbortSignal} [options.signal]
     * @return {Promise<bemap.GeocodingResponse>|bemap.Geocoder}
     */
    bemap.Geocoder.prototype.geocode = function(request, options) {
        if (!(request instanceof bemap.GeocodingRequest)) {
            return legacyGeocode.call(this, request, options);
        }

        // Map GeocodingRequest → AutocompleteGeocodingRequest (forward geocoding
        // is served by the autocomplete endpoint per the Flutter SDK contract).
        var place = request.place;
        if (!place && request.address) {
            // Compose a one-line place string from the structured address.
            var addr = (typeof request.address.toJson === 'function')
                ? request.address.toJson() : request.address;
            var parts = [];
            if (addr.streetNumber) parts.push(addr.streetNumber);
            if (addr.street) parts.push(addr.street);
            if (addr.postalCode) parts.push(addr.postalCode);
            if (addr.city) parts.push(addr.city);
            if (addr.country) parts.push(addr.country);
            place = parts.join(' ');
        }

        var ac = new bemap.AutocompleteGeocodingRequest({
            place: place || '',
            language: request.language || 'en',
            coordinate: request.proximity,
            boundingBox: request.boundingBox,
            countryCode: (request.address && request.address.countryCode) || null,
            addressDetails: (request.addressDetails === undefined) ? true : !!request.addressDetails
        });

        var body = ac.toJson();
        if (!body.geoserver) body.geoserver = (options && options.geoserver) || this.ctx.getGeoserver();

        return this._v2()._post('service/geocoding/autocomplete/1.0', body, options || {})
            .then(function(json) {
                // The autocomplete response uses `items[]` with per-item place /
                // postalAddress / coordinate — exactly what parseGeocodingResponseV2
                // already handles.
                return bemap.parseGeocodingResponseV2(json);
            });
    };

    /**
     * Reverse-geocode through the same channel `bemap.ReverseGeocoder` uses
     * (`POST service/geocoding/1.0/reverse`). Accepts either a
     * `ReverseGeocodingRequest` (typed v1.5 path) or the legacy
     * `{searchInfo: RevGeoSearchInfo, success, failed}` shape.
     *
     * @public
     * @param {bemap.ReverseGeocodingRequest|Object} request
     * @param {Object} [options]
     * @return {Promise<bemap.GeocodingResponse>|bemap.Geocoder}
     */
    bemap.Geocoder.prototype.revGeocode = function(request, options) {
        if (!(request instanceof bemap.ReverseGeocodingRequest)) {
            return legacyRevGeocode.call(this, request, options);
        }
        var body = request.toJson();
        if (!body.geoserver) body.geoserver = (options && options.geoserver) || this.ctx.getGeoserver();

        return this._v2()._post('service/geocoding/1.0/reverse', body, options || {})
            .then(function(json) { return bemap.parseGeocodingResponseV2(json); });
    };

    /**
     * Convenience alias for the typed reverse-geocoding path.
     *
     * @public
     * @param {bemap.Coordinate|bemap.CoordinateSat} coordinate
     * @param {Number} [radius=50]
     * @param {Object} [options] Extra fields forwarded to ReverseGeocodingRequest.
     * @return {Promise<bemap.GeocodingResponse>}
     */
    bemap.Geocoder.prototype.reverseGeocode = function(coordinate, radius, options) {
        var opts = options || {};
        var req = new bemap.ReverseGeocodingRequest({
            coordinate: coordinate,
            radius: (typeof radius === 'number') ? radius : 50,
            transportMode: opts.transportMode,
            language: opts.language || this.language,
            maxResult: opts.maxResult || this.maxResult,
            options: opts.options,
            geoserver: opts.geoserver
        });
        return this.revGeocode(req, { signal: opts.signal });
    };

})();