-
Notifications
You must be signed in to change notification settings - Fork 940
/
GeocoderMapbox.js
165 lines (145 loc) · 4.52 KB
/
GeocoderMapbox.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import { types as sdkTypes } from '../../util/sdkLoader';
import { userLocation } from '../../util/maps';
import config from '../../config';
const { LatLng: SDKLatLng, LatLngBounds: SDKLatLngBounds } = sdkTypes;
export const CURRENT_LOCATION_ID = 'current-location';
const GENERATED_BOUNDS_DEFAULT_DISTANCE = 500; // meters
// Distances for generated bounding boxes for different Mapbox place types
const PLACE_TYPE_BOUNDS_DISTANCES = {
address: 500,
country: 2000,
region: 2000,
postcode: 2000,
district: 2000,
place: 2000,
locality: 2000,
neighborhood: 2000,
poi: 2000,
'poi.landmark': 2000,
};
const locationBounds = (latlng, distance) => {
if (!latlng) {
return null;
}
const bounds = new window.mapboxgl.LngLat(latlng.lng, latlng.lat).toBounds(distance);
return new SDKLatLngBounds(
new SDKLatLng(bounds.getNorth(), bounds.getEast()),
new SDKLatLng(bounds.getSouth(), bounds.getWest())
);
};
const placeOrigin = prediction => {
if (prediction && Array.isArray(prediction.center) && prediction.center.length === 2) {
// Coordinates in Mapbox features are represented as [longitude, latitude].
return new SDKLatLng(prediction.center[1], prediction.center[0]);
}
return null;
};
const placeBounds = prediction => {
if (prediction) {
if (Array.isArray(prediction.bbox) && prediction.bbox.length === 4) {
// Bounds in Mapbox features are represented as [minX, minY, maxX, maxY]
return new SDKLatLngBounds(
new SDKLatLng(prediction.bbox[3], prediction.bbox[2]),
new SDKLatLng(prediction.bbox[1], prediction.bbox[0])
);
} else {
// If bounds are not available, generate them around the origin
// Resolve bounds distance based on place type
const placeType = Array.isArray(prediction.place_type) && prediction.place_type[0];
const distance =
(placeType && PLACE_TYPE_BOUNDS_DISTANCES[placeType]) || GENERATED_BOUNDS_DEFAULT_DISTANCE;
return locationBounds(placeOrigin(prediction), distance);
}
}
return null;
};
export const GeocoderAttribution = () => null;
/**
* A forward geocoding (place name -> coordinates) implementation
* using the Mapbox Geocoding API.
*/
class GeocoderMapbox {
getClient() {
const libLoaded = typeof window !== 'undefined' && window.mapboxgl && window.mapboxSdk;
if (!libLoaded) {
throw new Error('Mapbox libraries are required for GeocoderMapbox');
}
if (!this._client) {
this._client = window.mapboxSdk({
accessToken: window.mapboxgl.accessToken,
});
}
return this._client;
}
// Public API
//
/**
* Search places with the given name.
*
* @param {String} search query for place names
*
* @return {Promise<{ search: String, predictions: Array<Object>}>}
* results of the geocoding, should have the original search query
* and an array of predictions. The format of the predictions is
* only relevant for the `getPlaceDetails` function below.
*/
getPlacePredictions(search) {
return this.getClient()
.geocoding.forwardGeocode({
query: search,
limit: 5,
language: [config.locale],
})
.send()
.then(response => {
return {
search,
predictions: response.body.features,
};
});
}
/**
* Get the ID of the given prediction.
*/
getPredictionId(prediction) {
return prediction.id;
}
/**
* Get the address text of the given prediction.
*/
getPredictionAddress(prediction) {
if (prediction.predictionPlace) {
// default prediction defined above
return prediction.predictionPlace.address;
}
// prediction from Mapbox geocoding API
return prediction.place_name;
}
/**
* Fetch or read place details from the selected prediction.
*
* @param {Object} prediction selected prediction object
*
* @return {Promise<util.propTypes.place>} a place object
*/
getPlaceDetails(prediction) {
if (this.getPredictionId(prediction) === CURRENT_LOCATION_ID) {
return userLocation().then(latlng => {
return {
address: '',
origin: latlng,
bounds: locationBounds(latlng, config.maps.search.currentLocationBoundsDistance),
};
});
}
if (prediction.predictionPlace) {
return Promise.resolve(prediction.predictionPlace);
}
return Promise.resolve({
address: this.getPredictionAddress(prediction),
origin: placeOrigin(prediction),
bounds: placeBounds(prediction),
});
}
}
export default GeocoderMapbox;