Skip to content

Commit 57118b9

Browse files
support v6 api in MapboxGeocoder class
1 parent 5ac5826 commit 57118b9

File tree

3 files changed

+127
-33
lines changed

3 files changed

+127
-33
lines changed

debug/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ var geocoder = new MapboxGeocoder({
8585
.then(response => response.json())
8686
}
8787
},
88-
mapboxgl: mapboxgl
88+
mapboxgl: mapboxgl,
89+
version: 'v5',
8990
});
9091

9192
map.addControl(geocoder)

lib/index.js

Lines changed: 91 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ var EventEmitter = require('events').EventEmitter;
77
var exceptions = require('./exceptions');
88
var MapboxClient = require('@mapbox/mapbox-sdk');
99
var mbxGeocoder = require('@mapbox/mapbox-sdk/services/geocoding');
10+
var mbxGeocoderV6 = require("@mapbox/mapbox-sdk/services/geocoding-v6");
1011
var MapboxEventManager = require('./events');
1112
var localization = require('./localization');
1213
var subtag = require('subtag');
@@ -31,6 +32,36 @@ function getFooterNode() {
3132
return div;
3233
}
3334

35+
function getItemValue(item) {
36+
return item.place_name
37+
}
38+
39+
function render(item) {
40+
var placeName = item.place_name.split(',');
41+
return '<div class="mapboxgl-ctrl-geocoder--suggestion"><div class="mapboxgl-ctrl-geocoder--suggestion-title">' + placeName[0]+ '</div><div class="mapboxgl-ctrl-geocoder--suggestion-address">' + placeName.splice(1, placeName.length).join(',') + '</div></div>';
42+
}
43+
44+
function getItemValueV6(item) {
45+
const { name, place_formatted } = item.properties;
46+
return name + (place_formatted ? `, ${place_formatted}` : "");
47+
}
48+
49+
function renderV6(item) {
50+
const { name, place_formatted } = item.properties;
51+
return `<div class="mapboxgl-ctrl-geocoder--suggestion">
52+
${
53+
name
54+
? `<div class="mapboxgl-ctrl-geocoder--suggestion-title">${name}</div>`
55+
: ""
56+
}
57+
${
58+
place_formatted
59+
? `<div class="mapboxgl-ctrl-geocoder--suggestion-address">${place_formatted}</div>`
60+
: ""
61+
}
62+
</div>`;
63+
}
64+
3465
/**
3566
* A geocoder component using the [Mapbox Geocoding API](https://docs.mapbox.com/api/search/#geocoding)
3667
* @class MapboxGeocoder
@@ -75,6 +106,7 @@ function getFooterNode() {
75106
* @param {Boolean} [options.routing=false] Specify whether to request additional metadata about the recommended navigation destination corresponding to the feature or not. Only applicable for address features.
76107
* @param {String} [options.worldview="us"] Filter results to geographic features whose characteristics are defined differently by audiences belonging to various regional, cultural, or political groups.
77108
* @param {Boolean} [options.enableGeolocation=false] If `true` enable user geolocation feature.
109+
* @param {'v5'|'v6'} [options.version='v5'] If `v6` use v6 endpoint instead of v5. The default value is set to v5 API.
78110
* @param {('address'|'street'|'place'|'country')} [options.addressAccuracy="street"] The accuracy for the geolocation feature with which we define the address line to fill. The browser API returns the user's position with accuracy, and sometimes we can get the neighbor's address. To prevent receiving an incorrect address, you can reduce the accuracy of the definition.
79111
* @example
80112
* var geocoder = new MapboxGeocoder({ accessToken: mapboxgl.accessToken });
@@ -85,11 +117,15 @@ function getFooterNode() {
85117

86118
function MapboxGeocoder(options) {
87119
this._eventEmitter = new EventEmitter();
88-
this.options = extend({}, this.options, options);
120+
this.options = extend({}, this.options, {
121+
getItemValue: options.version === 'v6' ? getItemValueV6 : getItemValue,
122+
render: options.version === 'v6' ? renderV6 : render,
123+
}, options);
89124
this.inputString = '';
90125
this.fresh = true;
91126
this.lastSelected = null;
92127
this.geolocation = new Geolocation();
128+
this.geocoderFactory = this.options.version === 'v6' ? mbxGeocoderV6 : mbxGeocoder;
93129
}
94130

95131
MapboxGeocoder.prototype = {
@@ -110,13 +146,7 @@ MapboxGeocoder.prototype = {
110146
clearOnBlur: false,
111147
enableGeolocation: false,
112148
addressAccuracy: 'street',
113-
getItemValue: function(item) {
114-
return item.place_name
115-
},
116-
render: function(item) {
117-
var placeName = item.place_name.split(',');
118-
return '<div class="mapboxgl-ctrl-geocoder--suggestion"><div class="mapboxgl-ctrl-geocoder--suggestion-title">' + placeName[0]+ '</div><div class="mapboxgl-ctrl-geocoder--suggestion-address">' + placeName.splice(1, placeName.length).join(',') + '</div></div>';
119-
}
149+
version: 'v5'
120150
},
121151

122152
/**
@@ -181,7 +211,7 @@ MapboxGeocoder.prototype = {
181211
this.setLanguage();
182212

183213
if (!this.options.localGeocoderOnly){
184-
this.geocoderService = mbxGeocoder(
214+
this.geocoderService = this.geocoderFactory(
185215
MapboxClient({
186216
accessToken: this.options.accessToken,
187217
origin: this.options.origin
@@ -340,7 +370,13 @@ MapboxGeocoder.prototype = {
340370
this._showClearButton();
341371
this.fresh = false;
342372

343-
const config = {
373+
const config = this.options.version === 'v6' ? {
374+
longitude: geojson.geometry.coordinates[0],
375+
latitude: geojson.geometry.coordinates[1],
376+
limit: 1,
377+
language: this.options.language,
378+
types: ["address"],
379+
} : {
344380
limit: 1,
345381
language: [this.options.language],
346382
query: geojson.geometry.coordinates,
@@ -532,7 +568,11 @@ MapboxGeocoder.prototype = {
532568

533569
_fly: function(selected) {
534570
var flyOptions;
535-
if (selected.properties && exceptions[selected.properties.short_code]) {
571+
const { context, short_code } = selected.properties;
572+
const countryCode = this.options.version === 'v6' ?
573+
context && context.country && context.country.country_code.toLowerCase() :
574+
short_code;
575+
if (selected.properties && exceptions[countryCode]) {
536576
// Certain geocoder search results return (and therefore zoom to fit)
537577
// an unexpectedly large bounding box: for example, both Russia and the
538578
// USA span both sides of -180/180, or France includes the island of
@@ -541,7 +581,7 @@ MapboxGeocoder.prototype = {
541581
// short-term solution; this may be amended as necessary.
542582
flyOptions = extend({}, this.options.flyTo);
543583
if (this._map){
544-
this._map.fitBounds(exceptions[selected.properties.short_code].bbox, flyOptions);
584+
this._map.fitBounds(exceptions[countryCode].bbox, flyOptions);
545585
}
546586
} else if (selected.bbox) {
547587
var bbox = selected.bbox;
@@ -581,32 +621,36 @@ MapboxGeocoder.prototype = {
581621

582622
_setupConfig: function(requestType, search) {
583623
// Possible config properties to pass to client
624+
const v5Keys = [
625+
'fuzzyMatch',
626+
'routing',
627+
'reverseMode',
628+
];
584629
const keys = [
585630
'bbox',
586631
'limit',
587632
'proximity',
588633
'countries',
589634
'types',
590635
'language',
591-
'reverseMode',
592636
'mode',
593637
'autocomplete',
594-
'fuzzyMatch',
595-
'routing',
596638
'worldview'
597639
];
640+
const allKeys = [...(this.options.version === 'v6' ? [] : v5Keys), ...keys];
641+
// countries, types, and language need to be passed in as arrays to client
642+
// https://github.com/mapbox/mapbox-sdk-js/blob/master/services/geocoding.js#L38-L47
643+
const arrayParameters = ['countries', 'types', ...(this.options.version === 'v6' ? [] : ['language'])];
598644
const spacesOrCommaRgx = /[\s,]+/;
599645

600646
var self = this;
601-
var config = keys.reduce(function(config, key) {
647+
var config = allKeys.reduce(function(config, key) {
602648
// don't include undefined/null params, but allow boolean, among other, values
603649
if (self.options[key] === undefined || self.options[key] === null) {
604650
return config;
605651
}
606652

607-
// countries, types, and language need to be passed in as arrays to client
608-
// https://github.com/mapbox/mapbox-sdk-js/blob/master/services/geocoding.js#L38-L47
609-
['countries', 'types', 'language'].indexOf(key) > -1
653+
arrayParameters.indexOf(key) > -1
610654
? (config[key] = self.options[key].split(spacesOrCommaRgx))
611655
: (config[key] = self.options[key]);
612656

@@ -626,6 +670,7 @@ MapboxGeocoder.prototype = {
626670

627671
switch (requestType) {
628672
case GEOCODE_REQUEST_TYPE.REVERSE: {
673+
// expected to be coordinates in the form `lat, lon`
629674
var coords = search.split(spacesOrCommaRgx).map(function(c) {
630675
return parseFloat(c, 10);
631676
})
@@ -634,16 +679,13 @@ MapboxGeocoder.prototype = {
634679
}
635680

636681
// client only accepts one type for reverseGeocode, so
637-
// use first config type if one, if not default to poi
638-
config.types ? [config.types[0]] : ["poi"];
639-
config = extend(config, { query: coords, limit: 1 });
640-
641-
// Remove config options not supported by the reverseGeocoder
642-
['proximity', 'autocomplete', 'fuzzyMatch', 'bbox'].forEach(function(key) {
643-
if (key in config) {
644-
delete config[key]
645-
}
646-
});
682+
// use first config type if one, if not default to poi for v5 and address for v6
683+
const defaultType = this.options.version === 'v6' ? ["address"] : ["poi"];
684+
config.types = config.types ? [config.types[0]] : defaultType;
685+
config = extend(config,
686+
this.options.version === 'v6' ? { longitude: coords[0], latitude: coords[1]} : { query: coords },
687+
{limit: 1 }
688+
);
647689
} break;
648690
case GEOCODE_REQUEST_TYPE.FORWARD: {
649691
// Ensure that any reverse geocoding looking request is cleaned up
@@ -657,6 +699,23 @@ MapboxGeocoder.prototype = {
657699
} break;
658700
}
659701

702+
if(this.options.version === 'v6' && config.mode) {
703+
config.permanent = config.mode === 'mapbox.places-permanent';
704+
delete config.mode;
705+
}
706+
707+
// Remove config options not supported by the reverseGeocoder and v5 keys if v6 mode chosen
708+
const unsupportedKeys = [
709+
...(requestType === GEOCODE_REQUEST_TYPE.REVERSE ? ['proximity', 'autocomplete', 'fuzzyMatch', 'bbox'] : []),
710+
...(this.options.version === 'v6' ? v5Keys : [])
711+
];
712+
713+
unsupportedKeys.forEach(function(key) {
714+
if (key in config) {
715+
delete config[key]
716+
}
717+
});
718+
660719
return config;
661720
},
662721

@@ -840,7 +899,7 @@ MapboxGeocoder.prototype = {
840899
if (!results.features.length) return;
841900
var result = results.features[0];
842901
this._typeahead.selected = result;
843-
this._inputEl.value = result.place_name;
902+
this._inputEl.value = this.options.version === 'v6' ? result.properties.name : result.place_name;
844903
this._onChange();
845904
},
846905

@@ -1177,7 +1236,7 @@ MapboxGeocoder.prototype = {
11771236
*/
11781237
setOrigin: function(origin){
11791238
this.options.origin = origin;
1180-
this.geocoderService = mbxGeocoder(
1239+
this.geocoderService = this.geocoderFactory(
11811240
MapboxClient({
11821241
accessToken: this.options.accessToken,
11831242
origin: this.options.origin
@@ -1201,7 +1260,7 @@ MapboxGeocoder.prototype = {
12011260
*/
12021261
setAccessToken: function(accessToken){
12031262
this.options.accessToken = accessToken;
1204-
this.geocoderService = mbxGeocoder(
1263+
this.geocoderService = this.geocoderFactory(
12051264
MapboxClient({
12061265
accessToken: this.options.accessToken,
12071266
origin: this.options.origin

lib/utils.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,18 @@ function transformFeatureToGeolocationText(feature, accuracy) {
3434
return acc + addrInfo[name];
3535
}, '');
3636
}
37+
38+
const isV5Feature = (feature) => "id" in feature;
39+
3740
/**
3841
* This function transforms the feature from reverse geocoding to AddressInfo object
3942
* @param {object} feature
4043
* @returns {object}
4144
*/
4245
function getAddressInfo(feature) {
46+
if(!isV5Feature(feature)) {
47+
return getV6AddressInfo(feature);
48+
}
4349
const houseNumber = feature.address || '';
4450
const street = feature.text || '';
4551
const placeName = feature.place_name || '';
@@ -60,6 +66,34 @@ function getAddressInfo(feature) {
6066
return addrInfo;
6167
}
6268

69+
/**
70+
* This function transforms the v6 feature from reverse geocoding to AddressInfo object
71+
* @param {object} feature
72+
* @returns {object}
73+
*/
74+
function getV6AddressInfo(feature) {
75+
const houseNumber = feature.address_number || "";
76+
const street = feature.street || "";
77+
const placeName = feature.name + (feature.place_formatted ? `, ${feature.place_formatted}` : "");
78+
const address = feature.name;
79+
80+
const addrInfo = {
81+
address: address,
82+
houseNumber: houseNumber,
83+
street: street,
84+
placeName: placeName,
85+
};
86+
87+
for (const contextProp in feature.context) {
88+
if (contextProp) {
89+
const layer = contextProp.id.split(".")[0];
90+
addrInfo[layer] = contextProp.name;
91+
}
92+
}
93+
94+
return addrInfo;
95+
}
96+
6397
const REVERSE_GEOCODE_COORD_RGX = /^[ ]*(-?\d{1,3}(\.\d{0,256})?)[, ]+(-?\d{1,3}(\.\d{0,256})?)[ ]*$/;
6498

6599
module.exports = {

0 commit comments

Comments
 (0)