Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/api-reference/reducers/ui-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ Type: [Object][55]

#### Properties

- `ratio` **[string][57]** Default: `'SCREEN'`,
- `resolution` **[string][57]** Default: `'ONE_X'`,
- `resolution` **[string][57]** Default: `'SIZE_1024_768'`,
- `legend` **[boolean][58]** Default: `false`,
- `imageDataUri` **[string][57]** Default: `''`,
- `exporting` **[boolean][58]** Default: `false`
Expand Down
39 changes: 1 addition & 38 deletions src/components/src/modals/export-image-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
import ImagePreview from '../common/image-preview';
import {SetExportImageSettingUpdaterAction} from '@kepler.gl/actions';

import {EXPORT_IMG_RATIO_OPTIONS, EXPORT_IMG_RESOLUTION_OPTIONS} from '@kepler.gl/constants';
import {ExportImage} from '@kepler.gl/types';
import {StyledModalContent, SelectionButton, CheckMark} from '../common/styled-components';

Check failure on line 10 in src/components/src/modals/export-image-modal.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

'CheckMark' is declared but its value is never read.

Check failure on line 10 in src/components/src/modals/export-image-modal.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

'SelectionButton' is declared but its value is never read.

Check failure on line 10 in src/components/src/modals/export-image-modal.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

'CheckMark' is declared but its value is never read.

Check failure on line 10 in src/components/src/modals/export-image-modal.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

'SelectionButton' is declared but its value is never read.

Check failure on line 10 in src/components/src/modals/export-image-modal.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

'CheckMark' is declared but its value is never read.

Check failure on line 10 in src/components/src/modals/export-image-modal.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

'SelectionButton' is declared but its value is never read.
import Switch from '../common/switch';
Comment on lines 9 to 11
import {injectIntl, IntlShape} from 'react-intl';
import {FormattedMessage} from '@kepler.gl/localization';
Expand Down Expand Up @@ -55,7 +54,7 @@
cleanupExportImage,
intl
}) => {
const {legend, ratio, resolution} = exportImage;
const {legend} = exportImage;

useEffect(() => {
onUpdateImageSetting({
Expand All @@ -76,42 +75,6 @@
return (
<StyledModalContent className="export-image-modal">
<ImageOptionList>
<div className="image-option-section">
<div className="image-option-section-title">
<FormattedMessage id={'modal.exportImage.ratioTitle'} />
</div>
<FormattedMessage id={'modal.exportImage.ratioDescription'} />
<div className="button-list" id="export-image-modal__option_ratio">
{EXPORT_IMG_RATIO_OPTIONS.filter(op => !op.hidden).map(op => (
<SelectionButton
key={op.id}
selected={ratio === op.id}
onClick={() => onUpdateImageSetting({ratio: op.id})}
>
<FormattedMessage id={op.label} />
{ratio === op.id && <CheckMark />}
</SelectionButton>
))}
</div>
</div>
<div className="image-option-section">
<div className="image-option-section-title">
<FormattedMessage id={'modal.exportImage.resolutionTitle'} />
</div>
<FormattedMessage id={'modal.exportImage.resolutionDescription'} />
<div className="button-list" id="export-image-modal__option_resolution">
{EXPORT_IMG_RESOLUTION_OPTIONS.map(op => (
<SelectionButton
key={op.id}
selected={resolution === op.id}
onClick={() => op.available && onUpdateImageSetting({resolution: op.id})}
>
{op.label}
{resolution === op.id && <CheckMark />}
</SelectionButton>
))}
</div>
</div>
<div className="image-option-section">
Comment on lines 75 to 78
<div className="image-option-section-title">
<FormattedMessage id={'modal.exportImage.mapLegendTitle'} />
Expand Down
24 changes: 23 additions & 1 deletion src/components/src/plot-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ const CLASS_FILTER = [
const DOM_FILTER_FUNC = node => !CLASS_FILTER.includes(node.className);
const OUT_OF_SCREEN_POSITION = -9999;

/**
* Linearly remaps the export scale range [1..5] to a reduced legend scale [1..3].
* Prevents the legend from scaling as aggressively as the exported image.
* @param scale - scale factor (e.g., 1, 2, 3, 4, 5)
* @returns remapped scale factor
*/
function remapLegendScale(scale: number): number {
const max = 5;
const t = (scale - 1) / (max - 1);
Comment on lines +44 to +50
return 1 + t * 2;
}

PlotContainerFactory.deps = [MapContainerFactory, MapsLayoutFactory];

// Remove mapbox logo in exported map, because it contains non-ascii characters
Expand All @@ -57,6 +69,11 @@ const StyledPlotContainer = styled.div`
position: absolute;
top: ${OUT_OF_SCREEN_POSITION}px;
left: ${OUT_OF_SCREEN_POSITION}px;

/* Apply logarithmic zoom to legend panel */
.map-control-panel {
zoom: var(--legend-scale, 1) !important;
Comment on lines +73 to +75
}
`;

interface StyledMapContainerProps {
Expand Down Expand Up @@ -312,7 +329,12 @@ export default function PlotContainerFactory(
);

return (
<StyledPlotContainer className="export-map-instance">
<StyledPlotContainer
className="export-map-instance"
style={{
'--legend-scale': remapLegendScale(scale)
} as React.CSSProperties}
>
<StyledMapContainer ref={plottingAreaRef} width={size.width} height={size.height}>
<MapViewStateContextProvider mapState={newMapState}>
{mapContainers}
Expand Down
107 changes: 88 additions & 19 deletions src/constants/src/default-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -972,8 +972,14 @@ export const LAYER_BLENDINGS = {
export const MAX_DEFAULT_TOOLTIPS = 5;

export const RESOLUTIONS = keyMirror({
ONE_X: null,
TWO_X: null
SIZE_1024_768: null,
SIZE_1280_960: null,
SIZE_1600_1200: null,
SIZE_1920_1440: null,
SIZE_1280_720: null,
SIZE_1600_900: null,
SIZE_1920_1080: null,
SIZE_2560_1440: null
});
Comment on lines 974 to 983

export const EXPORT_IMG_RATIOS = keyMirror({
Expand Down Expand Up @@ -1029,35 +1035,98 @@ export type ImageResolutionOption = {
id: keyof typeof RESOLUTIONS;
label: string;
available: boolean;
scale: number;
getSize: (screenW: number, screenH: number) => {width: number; height: number};
};

export const OneXResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.ONE_X,
label: '1x',
export const Size1024x768ResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.SIZE_1024_768,
label: '1024x768 (4:3)',
available: true,
scale: 1,
getSize: (screenW, screenH) => ({
width: screenW,
height: screenH
getSize: () => ({
width: 1024,
height: 768
})
};

export const Size1280x960ResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.SIZE_1280_960,
label: '1280x960 (4:3)',
available: true,
getSize: () => ({
width: 1280,
height: 960
})
};

export const Size1600x1200ResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.SIZE_1600_1200,
label: '1600x1200 (4:3)',
available: true,
getSize: () => ({
width: 1600,
height: 1200
})
};

export const Size1920x1440ResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.SIZE_1920_1440,
label: '1920x1440 (4:3)',
available: true,
getSize: () => ({
width: 1920,
height: 1440
})
};

export const Size1280x720ResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.SIZE_1280_720,
label: '1280x720 (16:9)',
available: true,
getSize: () => ({
width: 1280,
height: 720
})
};

export const Size1600x900ResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.SIZE_1600_900,
label: '1600x900 (16:9)',
available: true,
getSize: () => ({
width: 1600,
height: 900
})
};

export const Size1920x1080ResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.SIZE_1920_1080,
label: '1920x1080 (16:9)',
available: true,
getSize: () => ({
width: 1920,
height: 1080
})
};

export const TwoXResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.TWO_X,
label: '2x',
export const Size2560x1440ResolutionOption: ImageResolutionOption = {
id: RESOLUTIONS.SIZE_2560_1440,
label: '2560x1440 (16:9)',
available: true,
scale: 2,
getSize: (screenW, screenH) => ({
width: screenW * 2,
height: screenH * 2
getSize: () => ({
width: 2560,
height: 1440
})
};

export const EXPORT_IMG_RESOLUTION_OPTIONS: ReadonlyArray<ImageResolutionOption> = [
OneXResolutionOption,
TwoXResolutionOption
Size1024x768ResolutionOption,
Size1280x960ResolutionOption,
Size1600x1200ResolutionOption,
Size1920x1440ResolutionOption,
Size1280x720ResolutionOption,
Size1600x900ResolutionOption,
Size1920x1080ResolutionOption,
Size2560x1440ResolutionOption
];

export const EXPORT_DATA_TYPE = keyMirror({
Expand Down
4 changes: 0 additions & 4 deletions src/reducers/src/ui-state-updaters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
DELETE_DATA_ID,
EXPORT_DATA_TYPE,
EXPORT_HTML_MAP_MODES,
EXPORT_IMG_RATIOS,

Check failure on line 11 in src/reducers/src/ui-state-updaters.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'EXPORT_IMG_RATIOS' is declared but its value is never read.

Check failure on line 11 in src/reducers/src/ui-state-updaters.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'EXPORT_IMG_RATIOS' is declared but its value is never read.

Check failure on line 11 in src/reducers/src/ui-state-updaters.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'EXPORT_IMG_RATIOS' is declared but its value is never read.
EXPORT_MAP_FORMATS,
RESOLUTIONS,

Check failure on line 13 in src/reducers/src/ui-state-updaters.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'RESOLUTIONS' is declared but its value is never read.

Check failure on line 13 in src/reducers/src/ui-state-updaters.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'RESOLUTIONS' is declared but its value is never read.

Check failure on line 13 in src/reducers/src/ui-state-updaters.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'RESOLUTIONS' is declared but its value is never read.
MAP_CONTROLS
} from '@kepler.gl/constants';
import {LOCALE_CODES} from '@kepler.gl/localization';
Expand Down Expand Up @@ -124,8 +124,6 @@
* Default image export config
* @memberof uiStateUpdaters
* @constant
* @property ratio Default: `'SCREEN'`,
* @property resolution Default: `'ONE_X'`,
* @property legend Default: `false`,
* @property mapH Default: 0,
* @property mapW Default: 0,
Expand All @@ -138,8 +136,6 @@
*/
export const DEFAULT_EXPORT_IMAGE: ExportImage = {
// user options
ratio: EXPORT_IMG_RATIOS.SCREEN,
resolution: RESOLUTIONS.ONE_X,
legend: false,
mapH: 0,
mapW: 0,
Expand Down
2 changes: 0 additions & 2 deletions src/types/reducers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,6 @@ export type BaseMapStyle = {
};

export declare type ExportImage = {
ratio: 'SCREEN' | 'FOUR_BY_THREE' | 'SIXTEEN_BY_NINE' | 'CUSTOM';
resolution: 'ONE_X' | 'TWO_X';
legend: boolean;
mapH: number;
mapW: number;
Expand Down
45 changes: 4 additions & 41 deletions src/utils/src/export-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,13 @@
import {Blob, URL, atob, Uint8Array, ArrayBuffer, document} from 'global/window';
import get from 'lodash/get';

import {
EXPORT_IMG_RESOLUTION_OPTIONS,
EXPORT_IMG_RATIO_OPTIONS,
RESOLUTIONS,
EXPORT_IMG_RATIOS,
FourByThreeRatioOption,
OneXResolutionOption
} from '@kepler.gl/constants';
import {ExportImage} from '@kepler.gl/types';
import {generateHashId} from '@kepler.gl/common-utils';
import domtoimage from './dom-to-image';
import {set} from './utils';
import {exportMapToHTML} from './export-map-html';
import {getApplicationConfig} from './application-config';

const defaultResolution = OneXResolutionOption;

const defaultRatio = FourByThreeRatioOption;

export function isMSEdge(window: Window): boolean {
// @ts-ignore msSaveOrOpenBlob was a proprietary addition to the Navigator object, added by Microsoft for Internet Explorer.
return Boolean(window.navigator && window.navigator.msSaveOrOpenBlob);
Expand All @@ -38,36 +26,11 @@ export function getScaleFromImageSize(imageW = 0, imageH = 0, mapW = 0, mapH = 0
return base / mapBase;
}

export function calculateExportImageSize({
mapW,
mapH,
ratio,
resolution
}: {
mapW: number;
mapH: number;
ratio: keyof typeof EXPORT_IMG_RATIOS;
resolution: keyof typeof RESOLUTIONS;
}) {
if (mapW <= 0 || mapH <= 0) {
return null;
}

const ratioItem = EXPORT_IMG_RATIO_OPTIONS.find(op => op.id === ratio) || defaultRatio;

const resolutionItem =
EXPORT_IMG_RESOLUTION_OPTIONS.find(op => op.id === resolution) || defaultResolution;

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

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

const {scale} = ratioItem.id === EXPORT_IMG_RATIOS.CUSTOM ? {scale: undefined} : resolutionItem;

export function calculateExportImageSize({}: {}) {
return {
scale,
imageW,
imageH
scale: 1,
imageW: 0,
imageH: 0
};
}
Comment on lines +29 to 35

Expand Down
Loading
Loading