Skip to content

Commit 2320845

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

File tree

6 files changed

+14982
-10505
lines changed

6 files changed

+14982
-10505
lines changed

debug/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ var geocoder = new MapboxGeocoder({
8585
.then(response => response.json())
8686
}
8787
},
88-
mapboxgl: mapboxgl
88+
mapboxgl: mapboxgl,
89+
reverseGeocode: true,
90+
version: 'v6',
8991
});
9092

9193
map.addControl(geocoder)

lib/events.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ MapboxEventManager.prototype = {
179179
routing: geocoder.options.routing,
180180
worldview: geocoder.options.worldview,
181181
mapZoom: zoom,
182-
keyboardLocale: this.locale
182+
keyboardLocale: this.locale,
183+
apiVersion: geocoder.options.version
183184
}
184185

185186
// get the text in the search bar

lib/index.js

Lines changed: 96 additions & 35 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,16 @@ MapboxGeocoder.prototype = {
532568

533569
_fly: function(selected) {
534570
var flyOptions;
535-
if (selected.properties && exceptions[selected.properties.short_code]) {
571+
const bbox = this.options.version === 'v6' ? selected.properties.bbox : selected.bbox;
572+
573+
let exceptionCode;
574+
if (this.options.version === 'v6' && selected.properties.feature_type === 'country') {
575+
exceptionCode = selected.properties.context.country.country_code.toLowerCase();
576+
} else if (this.options.version === 'v5') {
577+
exceptionCode = selected.properties && selected.properties.short_code;
578+
}
579+
580+
if (selected.properties && exceptions[exceptionCode]) {
536581
// Certain geocoder search results return (and therefore zoom to fit)
537582
// an unexpectedly large bounding box: for example, both Russia and the
538583
// USA span both sides of -180/180, or France includes the island of
@@ -541,10 +586,9 @@ MapboxGeocoder.prototype = {
541586
// short-term solution; this may be amended as necessary.
542587
flyOptions = extend({}, this.options.flyTo);
543588
if (this._map){
544-
this._map.fitBounds(exceptions[selected.properties.short_code].bbox, flyOptions);
589+
this._map.fitBounds(exceptions[exceptionCode].bbox, flyOptions);
545590
}
546-
} else if (selected.bbox) {
547-
var bbox = selected.bbox;
591+
} else if (bbox) {
548592
flyOptions = extend({}, this.options.flyTo);
549593
if (this._map){
550594
this._map.fitBounds([[bbox[0], bbox[1]], [bbox[2], bbox[3]]], flyOptions);
@@ -581,32 +625,36 @@ MapboxGeocoder.prototype = {
581625

582626
_setupConfig: function(requestType, search) {
583627
// Possible config properties to pass to client
628+
const v5Keys = [
629+
'fuzzyMatch',
630+
'routing',
631+
'reverseMode',
632+
];
584633
const keys = [
585634
'bbox',
586635
'limit',
587636
'proximity',
588637
'countries',
589638
'types',
590639
'language',
591-
'reverseMode',
592640
'mode',
593641
'autocomplete',
594-
'fuzzyMatch',
595-
'routing',
596642
'worldview'
597643
];
644+
const allKeys = [...(this.options.version === 'v6' ? [] : v5Keys), ...keys];
645+
// countries, types, and language need to be passed in as arrays to client
646+
// https://github.com/mapbox/mapbox-sdk-js/blob/master/services/geocoding.js#L38-L47
647+
const arrayParameters = ['countries', 'types', ...(this.options.version === 'v6' ? [] : ['language'])];
598648
const spacesOrCommaRgx = /[\s,]+/;
599649

600650
var self = this;
601-
var config = keys.reduce(function(config, key) {
651+
var config = allKeys.reduce(function(config, key) {
602652
// don't include undefined/null params, but allow boolean, among other, values
603653
if (self.options[key] === undefined || self.options[key] === null) {
604654
return config;
605655
}
606656

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
657+
arrayParameters.indexOf(key) > -1
610658
? (config[key] = self.options[key].split(spacesOrCommaRgx))
611659
: (config[key] = self.options[key]);
612660

@@ -626,24 +674,18 @@ MapboxGeocoder.prototype = {
626674

627675
switch (requestType) {
628676
case GEOCODE_REQUEST_TYPE.REVERSE: {
677+
// expected to be coordinates in the form `lat, lon`
629678
var coords = search.split(spacesOrCommaRgx).map(function(c) {
630679
return parseFloat(c, 10);
631680
})
632681
if (!self.options.flipCoordinates) {
633682
coords.reverse();
634683
}
635684

636-
// 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-
});
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,9 @@ 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' ?
903+
(result.properties.name + (result.properties.place_formatted ? `, ${result.properties.place_formatted}` : "")) :
904+
result.place_name;
844905
this._onChange();
845906
},
846907

@@ -1177,7 +1238,7 @@ MapboxGeocoder.prototype = {
11771238
*/
11781239
setOrigin: function(origin){
11791240
this.options.origin = origin;
1180-
this.geocoderService = mbxGeocoder(
1241+
this.geocoderService = this.geocoderFactory(
11811242
MapboxClient({
11821243
accessToken: this.options.accessToken,
11831244
origin: this.options.origin
@@ -1201,7 +1262,7 @@ MapboxGeocoder.prototype = {
12011262
*/
12021263
setAccessToken: function(accessToken){
12031264
this.options.accessToken = accessToken;
1204-
this.geocoderService = mbxGeocoder(
1265+
this.geocoderService = this.geocoderFactory(
12051266
MapboxClient({
12061267
accessToken: this.options.accessToken,
12071268
origin: this.options.origin

lib/utils.js

Lines changed: 33 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,33 @@ 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+
addrInfo[contextProp] = feature.context[contextProp].name;
90+
}
91+
}
92+
93+
return addrInfo;
94+
}
95+
6396
const REVERSE_GEOCODE_COORD_RGX = /^[ ]*(-?\d{1,3}(\.\d{0,256})?)[, ]+(-?\d{1,3}(\.\d{0,256})?)[ ]*$/;
6497

6598
module.exports = {

0 commit comments

Comments
 (0)