radar-sdk-js 4.2.1-beta.2 → 4.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/http.d.ts +3 -1
- package/dist/radar.js +1677 -1875
- package/dist/radar.js.map +1 -1
- package/dist/types.d.ts +11 -6
- package/dist/ui/RadarLogoControl.d.ts +6 -0
- package/dist/ui/RadarMap.d.ts +11 -0
- package/dist/ui/RadarMarker.d.ts +11 -0
- package/dist/ui/map.d.ts +4 -2
- package/dist/util/_.d.ts +2 -0
- package/dist/util/mapStyle.d.ts +2 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/api/track.ts +26 -26
- package/src/http.ts +35 -24
- package/src/types.ts +12 -6
- package/src/ui/RadarLogoControl.ts +24 -0
- package/src/ui/RadarMap.ts +155 -0
- package/src/ui/RadarMarker.ts +126 -0
- package/src/ui/map.ts +9 -143
- package/src/util/_.ts +30 -0
- package/src/util/mapStyle.ts +85 -0
- package/src/version.ts +1 -1
package/src/ui/map.ts
CHANGED
|
@@ -1,157 +1,23 @@
|
|
|
1
1
|
import maplibregl from 'maplibre-gl';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import RadarMap from './RadarMap';
|
|
4
|
+
import RadarMarker from './RadarMarker';
|
|
5
5
|
|
|
6
|
-
import type {
|
|
7
|
-
|
|
8
|
-
const DEFAULT_STYLE = 'radar-default-v1';
|
|
9
|
-
|
|
10
|
-
const RADAR_STYLES = [
|
|
11
|
-
'radar-default-v1',
|
|
12
|
-
'radar-light-v1',
|
|
13
|
-
'radar-dark-v1',
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
const RADAR_LOGO_URL = 'https://api.radar.io/maps/static/images/logo.svg';
|
|
17
|
-
|
|
18
|
-
const defaultMaplibreOptions: Partial<maplibregl.MapOptions> = {
|
|
19
|
-
minZoom: 1,
|
|
20
|
-
maxZoom: 20,
|
|
21
|
-
attributionControl: false,
|
|
22
|
-
dragRotate: false,
|
|
23
|
-
touchPitch: false,
|
|
24
|
-
maplibreLogo: false,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const defaultMarkerOptions: Partial<maplibregl.MarkerOptions> = {
|
|
28
|
-
color: '#000257',
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const createStyleURL = (options: RadarOptions, style: string = DEFAULT_STYLE) => (
|
|
32
|
-
`${options.host}/maps/styles/${style}?publishableKey=${options.publishableKey}`
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
// use formatted style URL if using one of Radar's out-of-the-box styles
|
|
36
|
-
const getStyle = (options: RadarOptions, mapOptions: RadarMapOptions) => {
|
|
37
|
-
const style = mapOptions.style;
|
|
38
|
-
|
|
39
|
-
if (!style || (typeof style === 'string' && RADAR_STYLES.includes(style))) {
|
|
40
|
-
return createStyleURL(options, mapOptions.style as string);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return mapOptions.style;
|
|
44
|
-
};
|
|
6
|
+
import type { RadarMapOptions, RadarMarkerOptions } from '../types';
|
|
45
7
|
|
|
46
8
|
class MapUI {
|
|
47
9
|
public static getMapLibre() {
|
|
48
10
|
return maplibregl;
|
|
49
11
|
}
|
|
50
12
|
|
|
51
|
-
public static createMap(mapOptions: RadarMapOptions):
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
if (!options.publishableKey) {
|
|
55
|
-
Logger.warn('publishableKey not set. Call Radar.initialize() with key before creating a new map.');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// configure maplibre options
|
|
59
|
-
const style = getStyle(options, mapOptions);
|
|
60
|
-
const maplibreOptions: maplibregl.MapOptions = Object.assign({},
|
|
61
|
-
defaultMaplibreOptions,
|
|
62
|
-
mapOptions,
|
|
63
|
-
{ style },
|
|
64
|
-
);
|
|
65
|
-
Logger.debug(`initialize map with options: ${JSON.stringify(maplibreOptions)}`);
|
|
66
|
-
|
|
67
|
-
// set container
|
|
68
|
-
maplibreOptions.container = mapOptions.container;
|
|
69
|
-
|
|
70
|
-
// custom request handler for Radar styles
|
|
71
|
-
maplibreOptions.transformRequest = (url, resourceType) => {
|
|
72
|
-
if (resourceType === 'Style' && RADAR_STYLES.includes(url)) {
|
|
73
|
-
const radarStyleURL = createStyleURL(options, url);
|
|
74
|
-
return { url: radarStyleURL };
|
|
75
|
-
} else {
|
|
76
|
-
return { url };
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// create map
|
|
81
|
-
const map = new maplibregl.Map(maplibreOptions);
|
|
82
|
-
const container = map.getContainer();
|
|
83
|
-
|
|
84
|
-
if (!container.style.width && !container.style.height) {
|
|
85
|
-
Logger.warn('map container does not have a set "width" or "height"');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// add radar logo
|
|
89
|
-
const img = document.createElement('img');
|
|
90
|
-
img.src = RADAR_LOGO_URL;
|
|
91
|
-
|
|
92
|
-
const link = document.createElement('a');
|
|
93
|
-
link.id = 'radar-map-logo';
|
|
94
|
-
link.href = 'https://radar.com?ref=powered_by_radar';
|
|
95
|
-
link.target = '_blank';
|
|
96
|
-
link.style.position = 'absolute';
|
|
97
|
-
link.style.bottom = '0';
|
|
98
|
-
link.style.left = '5px';
|
|
99
|
-
link.style.width = '80px';
|
|
100
|
-
link.style.height = '38px';
|
|
101
|
-
link.appendChild(img)
|
|
102
|
-
map.getContainer().appendChild(link);
|
|
103
|
-
|
|
104
|
-
// add attribution
|
|
105
|
-
const attribution = new maplibregl.AttributionControl({ compact: false });
|
|
106
|
-
map.addControl(attribution, 'bottom-right');
|
|
107
|
-
|
|
108
|
-
// add zoom controls
|
|
109
|
-
const nav = new maplibregl.NavigationControl({ showCompass: false });
|
|
110
|
-
map.addControl(nav, 'bottom-right');
|
|
111
|
-
|
|
112
|
-
// handle map resize actions
|
|
113
|
-
const onResize = () => {
|
|
114
|
-
const attrib = document.querySelector('.maplibregl-ctrl-attrib');
|
|
115
|
-
if (attrib) {
|
|
116
|
-
const width = map.getContainer().clientWidth;
|
|
117
|
-
if (width < 380) {
|
|
118
|
-
attrib.classList.add('hidden');
|
|
119
|
-
} else {
|
|
120
|
-
attrib.classList.remove('hidden');
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
map.on('resize', onResize);
|
|
125
|
-
map.on('load', onResize);
|
|
126
|
-
|
|
127
|
-
return map;
|
|
13
|
+
public static createMap(mapOptions: RadarMapOptions): RadarMap {
|
|
14
|
+
const radarMap = new RadarMap(mapOptions);
|
|
15
|
+
return radarMap;
|
|
128
16
|
}
|
|
129
17
|
|
|
130
|
-
public static createMarker(markerOptions: RadarMarkerOptions = {}):
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
if (markerOptions.color) {
|
|
134
|
-
maplibreOptions.color = markerOptions.color;
|
|
135
|
-
}
|
|
136
|
-
if (markerOptions.element) {
|
|
137
|
-
maplibreOptions.element = markerOptions.element;
|
|
138
|
-
}
|
|
139
|
-
if (markerOptions.scale) {
|
|
140
|
-
maplibreOptions.scale = markerOptions.scale;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const marker = new maplibregl.Marker(maplibreOptions);
|
|
144
|
-
|
|
145
|
-
// set popup text or HTML
|
|
146
|
-
if (markerOptions.text) {
|
|
147
|
-
const popup = new maplibregl.Popup({ offset: 35 }).setText(markerOptions.text);
|
|
148
|
-
marker.setPopup(popup);
|
|
149
|
-
} else if (markerOptions.html) {
|
|
150
|
-
const popup = new maplibregl.Popup({ offset: 35 }).setHTML(markerOptions.html);
|
|
151
|
-
marker.setPopup(popup);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return marker;
|
|
18
|
+
public static createMarker(markerOptions: RadarMarkerOptions = {}): RadarMarker {
|
|
19
|
+
const radarMarker = new RadarMarker(markerOptions);
|
|
20
|
+
return radarMarker;
|
|
155
21
|
}
|
|
156
22
|
}
|
|
157
23
|
|
package/src/util/_.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//check if value is primitive
|
|
2
|
+
const isPrimitive = (obj: any) => {
|
|
3
|
+
return (obj !== Object(obj));
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export const deepEqual = (obj1: any, obj2: any) => {
|
|
7
|
+
|
|
8
|
+
if (obj1 === obj2) // it's just the same object. No need to compare.
|
|
9
|
+
return true;
|
|
10
|
+
|
|
11
|
+
if (isPrimitive(obj1) && isPrimitive(obj2)) // compare primitives
|
|
12
|
+
return obj1 === obj2;
|
|
13
|
+
|
|
14
|
+
if (Object.keys(obj1).length !== Object.keys(obj2).length)
|
|
15
|
+
return false;
|
|
16
|
+
|
|
17
|
+
// compare objects with same number of keys
|
|
18
|
+
for (let key in obj1) {
|
|
19
|
+
if (!(key in obj2)) return false; //other object doesn't have this prop
|
|
20
|
+
if (!deepEqual(obj1[key], obj2[key])) return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const uniq = <T extends any>(arr: T[]): T[] => {
|
|
27
|
+
const outputArray: T[] = arr.filter((v, i, self) => i === self.indexOf(v));
|
|
28
|
+
|
|
29
|
+
return outputArray;
|
|
30
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as _ from './_';
|
|
2
|
+
|
|
3
|
+
import Logger from '../logger';
|
|
4
|
+
|
|
5
|
+
import { RadarMapOptions } from '../types';
|
|
6
|
+
|
|
7
|
+
// NOTE(jasonl): ENSURE THIS STAYS IN SYNC WITH THE SERVER IMPLEMENTATION
|
|
8
|
+
const RADAR_LANGUAGE_EXPR = ['get', 'radar:name_client_language'];
|
|
9
|
+
|
|
10
|
+
/** get an array of languages set in the browser in RFC 5646 format */
|
|
11
|
+
const getBrowserLanguages = (options = { fallback: 'en', format: 'RFC-5646' }) => {
|
|
12
|
+
let languagesRFC5646 = [options.fallback];
|
|
13
|
+
if (navigator.languages.length > 0) {
|
|
14
|
+
languagesRFC5646 = [...navigator.languages];
|
|
15
|
+
} else if (navigator.language) {
|
|
16
|
+
languagesRFC5646 = [navigator.language];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (options.format === 'ISO-639-1') {
|
|
20
|
+
// RFC 5646 format is always prefixed by the ISO 639-1 language code so we need to strip that out
|
|
21
|
+
const languagesISO6391 = _.uniq(languagesRFC5646.map(l => l.split('-')[0]));
|
|
22
|
+
return languagesISO6391;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return languagesRFC5646;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const replaceAllValuesInExpression = (expr: any, target: any[], value: any[]): any => {
|
|
29
|
+
if (target.length !== value.length) {
|
|
30
|
+
Logger.warn('replaceAllValueInExpression: Target and value arrays must be the same length');
|
|
31
|
+
return expr;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const findIndex = target.findIndex((t) => _.deepEqual(t, expr));
|
|
35
|
+
if (findIndex !== -1) {
|
|
36
|
+
return value[findIndex];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (Array.isArray(expr)) {
|
|
40
|
+
return expr.map((e) => replaceAllValuesInExpression(e, target, value));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return expr;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const transformMapStyle = (styleJSON: any, options: Pick<RadarMapOptions, 'languages' | 'navigatorFallbackLanguage'>): any => {
|
|
47
|
+
let languages: string[] = [];
|
|
48
|
+
|
|
49
|
+
// use browser language if style language is set to local
|
|
50
|
+
if (styleJSON?.metadata?.['radar:language'] === 'local') {
|
|
51
|
+
const fallbackLanguage = options.navigatorFallbackLanguage || 'en';
|
|
52
|
+
languages = getBrowserLanguages({ fallback: fallbackLanguage, format: 'ISO-639-1' });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// client set language takes precedence
|
|
56
|
+
if (options.languages) {
|
|
57
|
+
languages = options.languages;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (languages.length > 0) {
|
|
61
|
+
// construct expression ordered by language preference
|
|
62
|
+
const localLanguageExpression: any[] = ['coalesce'];
|
|
63
|
+
languages.forEach((l: any) => {
|
|
64
|
+
localLanguageExpression.push(['get', `name_${l}`], ['get', `name:${l}`]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const transformedLayers = styleJSON.layers.map((layer: any) => {
|
|
68
|
+
if (layer.type === 'symbol' && layer.layout['text-field']) {
|
|
69
|
+
layer.layout['text-field'] = replaceAllValuesInExpression(
|
|
70
|
+
layer.layout['text-field'],
|
|
71
|
+
[RADAR_LANGUAGE_EXPR],
|
|
72
|
+
[localLanguageExpression]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return layer;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return layer;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return { ...styleJSON, layers: transformedLayers };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return styleJSON;
|
|
85
|
+
};
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '4.
|
|
1
|
+
export default '4.3.0-beta.1';
|