• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

keplergl / kepler.gl / 25196904117

01 May 2026 12:52AM UTC coverage: 59.169% (-0.005%) from 59.174%
25196904117

push

github

web-flow
chore: fix tests (#3403)

* chore: fix tests

Signed-off-by: Ihor Dykhta <ihordykhta@Ihors-MacBook-Pro.local>

* add RTLTextPlugin prop to kepler

Signed-off-by: Ihor Dykhta <ihordykhta@Ihors-MacBook-Pro.local>

---------

Signed-off-by: Ihor Dykhta <ihordykhta@Ihors-MacBook-Pro.local>
Co-authored-by: Ihor Dykhta <ihordykhta@Ihors-MacBook-Pro.local>

6923 of 14060 branches covered (49.24%)

Branch coverage included in aggregate %.

1 of 2 new or added lines in 1 file covered. (50.0%)

42 existing lines in 5 files now uncovered.

14276 of 21768 relevant lines covered (65.58%)

80.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

44.54
/src/utils/src/export-utils.ts
1
// SPDX-License-Identifier: MIT
2
// Copyright contributors to the kepler.gl project
3

4
import {Blob, URL, atob, Uint8Array, ArrayBuffer, document} from 'global/window';
5
import get from 'lodash/get';
6

7
import {
8
  EXPORT_IMG_RESOLUTION_OPTIONS,
9
  EXPORT_IMG_RATIO_OPTIONS,
10
  EXPORT_IMG_RATIOS,
11
  FourByThreeRatioOption,
12
  OneXResolutionOption,
13
  type ExportResolutionOption
14
} from '@kepler.gl/constants';
15
import {ExportImage} from '@kepler.gl/types';
16
import {generateHashId} from '@kepler.gl/common-utils';
17
import domtoimage from './dom-to-image';
18
import {set} from './utils';
19
import {exportMapToHTML} from './export-map-html';
20
import {getApplicationConfig} from './application-config';
21

22
const defaultResolution = OneXResolutionOption;
17✔
23

24
const defaultRatio = FourByThreeRatioOption;
17✔
25

26
export function isMSEdge(window: Window): boolean {
27
  // @ts-ignore msSaveOrOpenBlob was a proprietary addition to the Navigator object, added by Microsoft for Internet Explorer.
28
  return Boolean(window.navigator && window.navigator.msSaveOrOpenBlob);
3✔
29
}
30

31
export function getScaleFromImageSize(imageW = 0, imageH = 0, mapW = 0, mapH = 0) {
1!
32
  if ([imageW, imageH, mapW, mapH].some(d => d <= 0)) {
16✔
33
    return 1;
3✔
34
  }
35

36
  const base = imageW / imageH > 1 ? imageW : imageH;
1!
37
  const mapBase = imageW / imageH > 1 ? mapW : mapH;
1!
38
  return base / mapBase;
1✔
39
}
40

41
export function calculateExportImageSize({
42
  mapW,
43
  mapH,
44
  ratio,
45
  resolution
46
}: {
47
  mapW: number;
48
  mapH: number;
49
  ratio: keyof typeof EXPORT_IMG_RATIOS;
50
  resolution: ExportResolutionOption;
51
}) {
52
  if (mapW <= 0 || mapH <= 0) {
11✔
53
    return null;
4✔
54
  }
55

56
  const ratioItem = EXPORT_IMG_RATIO_OPTIONS.find(op => op.id === ratio) || defaultRatio;
13✔
57

58
  const resolutionItem =
59
    EXPORT_IMG_RESOLUTION_OPTIONS.find(op => op.id === resolution) || defaultResolution;
25✔
60

61
  const {width: scaledWidth, height: scaledHeight} = resolutionItem.getSize(mapW, mapH);
7✔
62

63
  const {width: imageW, height: imageH} = ratioItem.getSize(scaledWidth, scaledHeight);
7✔
64

65
  const {scale} = ratioItem.id === EXPORT_IMG_RATIOS.CUSTOM ? {scale: undefined} : resolutionItem;
7✔
66
  const resolvedScale = scale ?? 1;
7✔
67

68
  return {
7✔
69
    zoomOffset: Math.log2(resolvedScale) || 0,
14✔
70
    scale: resolvedScale,
71
    imageW,
72
    imageH
73
  };
74
}
75

76
export function convertToPng(sourceElem: HTMLElement, options) {
UNCOV
77
  return domtoimage.toPng(sourceElem, options);
×
78
}
79

80
export function dataURItoBlob(dataURI: string): Blob {
UNCOV
81
  const binary = atob(dataURI.split(',')[1]);
×
82

83
  // separate out the mime component
UNCOV
84
  const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
×
85

86
  // write the bytes of the string to an ArrayBuffer
UNCOV
87
  const ab = new ArrayBuffer(binary.length);
×
88

89
  // create a view into the buffer
90
  const ia = new Uint8Array(ab);
×
91

UNCOV
92
  for (let i = 0; i < binary.length; i++) {
×
UNCOV
93
    ia[i] = binary.charCodeAt(i);
×
94
  }
95

UNCOV
96
  return new Blob([ab], {type: mimeString});
×
97
}
98

99
export function downloadFile(fileBlob: Blob, fileName: string) {
UNCOV
100
  if (isMSEdge(window)) {
×
101
    (window.navigator as any).msSaveOrOpenBlob(fileBlob, fileName);
×
102
  } else {
103
    const url = URL.createObjectURL(fileBlob);
×
104

105
    const link = document.createElement('a');
×
UNCOV
106
    link.setAttribute('href', url);
×
107
    link.setAttribute('download', fileName);
×
108

UNCOV
109
    document.body.appendChild(link);
×
110
    // in some cases where maps are embedded, e.g. need to
111
    // create and dispatch an event so that the browser downloads
112
    // the file instead of navigating to the url
UNCOV
113
    const evt = new MouseEvent('click', {
×
114
      view: window,
115
      bubbles: false,
116
      cancelable: true
117
    });
118
    link.dispatchEvent(evt);
×
UNCOV
119
    document.body.removeChild(link);
×
UNCOV
120
    URL.revokeObjectURL(url);
×
121
  }
122
}
123

124
/**
125
 * Whether color is rgb
126
 * @returns
127
 */
128
export function exportImage(
129
  uiStateExportImage: ExportImage,
130
  filename = getApplicationConfig().defaultImageName
×
131
) {
132
  const {imageDataUri} = uiStateExportImage;
×
133
  if (imageDataUri) {
×
UNCOV
134
    const file = dataURItoBlob(imageDataUri);
×
UNCOV
135
    downloadFile(file, filename);
×
136
  }
137
}
138

139
export function exportToJsonString(data) {
140
  try {
2✔
141
    return JSON.stringify(data);
2✔
142
  } catch (e) {
143
    if (e instanceof TypeError) return e.message;
×
144
    // Non-Standard Error Object Property
UNCOV
145
    return (e as any).description;
×
146
  }
147
}
148

149
export function getMapJSON(state, options = getApplicationConfig().defaultExportJsonSettings) {
1✔
150
  const {hasData} = options;
1✔
151
  const schema = state.visState.schema;
1✔
152

153
  if (!hasData) {
1!
UNCOV
154
    return schema.getConfigToSave(state);
×
155
  }
156

157
  let mapToSave = schema.save(state);
1✔
158
  // add file name if title is not provided
159
  const title = get(mapToSave, ['info', 'title']);
1✔
160
  if (!title || !title.length) {
1!
161
    mapToSave = set(['info', 'title'], `keplergl_${generateHashId(6)}`, mapToSave);
1✔
162
  }
163
  return mapToSave;
1✔
164
}
165

166
export function exportJson(state, options: any = {}) {
×
167
  const map = getMapJSON(state, options);
×
168
  map.info.source = 'kepler.gl';
×
169
  const fileBlob = new Blob([exportToJsonString(map)], {type: 'application/json'});
×
UNCOV
170
  const fileName = state.appName ? `${state.appName}.json` : getApplicationConfig().defaultJsonName;
×
UNCOV
171
  downloadFile(fileBlob, fileName);
×
172
}
173

174
export function exportHtml(state, options) {
175
  const {userMapboxToken, exportMapboxAccessToken, mode} = options;
×
176

UNCOV
177
  const data = {
×
178
    ...getMapJSON(state),
179
    mapboxApiAccessToken:
180
      (userMapboxToken || '') !== '' ? userMapboxToken : exportMapboxAccessToken,
×
181
    mode
182
  };
183

UNCOV
184
  const fileBlob = new Blob([exportMapToHTML(data)], {type: 'text/html'});
×
UNCOV
185
  downloadFile(
×
186
    fileBlob,
187
    state.appName ? `${state.appName}.html` : getApplicationConfig().defaultHtmlName
×
188
  );
189
}
190

191
export function exportMap(state, options = getApplicationConfig().defaultExportJsonSettings) {
×
192
  const {imageDataUri} = state.uiState.exportImage;
×
UNCOV
193
  const thumbnail: Blob | null = imageDataUri ? dataURItoBlob(imageDataUri) : null;
×
194
  const mapToSave = getMapJSON(state, options);
×
195

UNCOV
196
  return {
×
197
    map: mapToSave,
198
    thumbnail
199
  };
200
}
201

202
const exporters = {
17✔
203
  exportImage,
204
  exportJson,
205
  exportHtml
206
};
207

208
export default exporters;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc