Skip to content

Commit

Permalink
[feat] Update custom map style updater to support managed map style (#…
Browse files Browse the repository at this point in the history
…2264)

Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>
Co-authored-by: Jacob Wasilkowski <4933392+jwasilgeo@users.noreply.github.com>
  • Loading branch information
igorDykhta and jwasilgeo committed Jun 20, 2023
1 parent 84c0736 commit a8599dc
Show file tree
Hide file tree
Showing 11 changed files with 444 additions and 38 deletions.
2 changes: 1 addition & 1 deletion src/actions/src/map-style-actions.ts
Expand Up @@ -225,7 +225,7 @@ export type LoadCustomMapStyleUpdaterAction = {
payload: {
icon?: string;
style?: object;
error?: Error;
error?: object;
};
};
/**
Expand Down
Expand Up @@ -28,13 +28,16 @@ import {
PanelHeaderContent,
PanelHeaderTitle,
PanelLabel,
StyledPanelHeader
StyledPanelHeader,
StyledPanelHeaderProps
} from '../../common/styled-components';
import {FormattedMessage} from '@kepler.gl/localization';
import {MapStyle} from '@kepler.gl/reducers';
import {BaseProps} from '../../common/icons';

const StyledMapDropdown = styled(StyledPanelHeader)`
type StyledMapDropdownProps = StyledPanelHeaderProps & {hasCallout: boolean};

const StyledMapDropdown = styled(StyledPanelHeader)<StyledMapDropdownProps>`
height: 48px;
margin-bottom: 5px;
opacity: 1;
Expand All @@ -61,6 +64,15 @@ const StyledMapDropdown = styled(StyledPanelHeader)`
height: 30px;
width: 40px;
}
/* show callout dot if props.hasCallout and theme provides calloutDot base styles */
:after {
${({theme}) => theme.calloutDot}
background-color: #00ACF5;
top: 12px;
left: 15px;
display: ${({theme, hasCallout}) => (theme.calloutDot && hasCallout ? 'block' : 'none')};
}
`;

export type MapStyleSelectorProps = {
Expand Down Expand Up @@ -95,6 +107,7 @@ function MapStyleSelectorFactory(PanelHeaderAction: ReturnType<typeof PanelHeade
})}
key={op}
onClick={isSelecting ? () => onChange(op) : toggleActive}
hasCallout={Boolean(mapStyle.mapStyles[op].custom)}
>
<PanelHeaderContent className="map-title-block">
<img className="map-preview" src={mapStyle.mapStyles[op].icon} />
Expand Down
57 changes: 39 additions & 18 deletions src/reducers/src/map-style-updaters.ts
Expand Up @@ -422,8 +422,16 @@ export const mapStyleChangeUpdater = (
state: MapStyle,
{payload: {styleType, onSuccess}}: MapStyleActions.MapStyleChangeUpdaterAction
): MapStyle => {
if (!state.mapStyles[styleType]) {
if (
// we might not have received the style yet
!state.mapStyles[styleType] ||
// or if it is a managed custom style asset
// and if it has not been hydrated with URL info yet (during app first initialization)
// and it does not have a style object (during adding a custom style)
(state.mapStyles[styleType]?.custom === 'MANAGED' &&
!state.mapStyles[styleType]?.url &&
!hasStyleObject(state.mapStyles[styleType]))
) {
return state;
}

Expand Down Expand Up @@ -673,18 +681,21 @@ export const loadCustomMapStyleUpdater = (
// style json and icon will load asynchronously
...(style
? {
// @ts-expect-error
id: style.id || generateHashId(),
id:
state.inputStyle.custom === 'MANAGED'
? state.inputStyle.id // custom MANAGED type
: // @ts-expect-error
style.id || generateHashId(), // custom LOCAL type
// make a copy of the style object
style: cloneDeep(style),
// @ts-expect-error
label: style.name,
label: state.inputStyle.label || style.name,
// gathering layer group info from style json
layerGroups: getLayerGroupsFromStyle(style)
}
: {}),
...(icon ? {icon} : {}),
...(error !== undefined ? {error} : {})
...(error ? {error} : {})
}
});

Expand All @@ -702,15 +713,21 @@ export const inputMapStyleUpdater = (
...inputStyle
};

const isValid = isValidStyleUrl(updated.url);
const icon = isValid
? getStyleImageIcon({
mapState,
styleUrl: updated.url || '',
mapboxApiAccessToken: updated.accessToken || state.mapboxApiAccessToken || '',
mapboxApiUrl: state.mapboxApiUrl || DEFAULT_MAPBOX_API_URL
})
: state.inputStyle.icon;
// differentiate between either a url to hosted style json that needs an icon url,
// or an icon already available client-side as a data uri
const isValidUrl = isValidStyleUrl(updated.url);
const isUpdatedIconDataUri = updated.icon?.startsWith('data:image');
const isValid = isValidUrl || Boolean(updated.uploadedFile);

const icon =
isValidUrl && !isUpdatedIconDataUri
? getStyleImageIcon({
mapState,
styleUrl: updated.url || '',
mapboxApiAccessToken: updated.accessToken || state.mapboxApiAccessToken || '',
mapboxApiUrl: state.mapboxApiUrl || DEFAULT_MAPBOX_API_URL
})
: updated.icon;

return {
...state,
Expand All @@ -729,10 +746,12 @@ export const inputMapStyleUpdater = (
* @memberof mapStyleUpdaters
*/
export const addCustomMapStyleUpdater = (state: MapStyle): MapStyle => {
// @ts-expect-error
const styleId = state.inputStyle.id;
const newState = {
if (!styleId) return state;

const newState: MapStyle = {
...state,
// @ts-expect-error Property 'layerGroups' is missing in type 'InputStyle' but required in type 'BaseMapStyle'. Legacy case?
mapStyles: {
...state.mapStyles,
[styleId]: state.inputStyle
Expand Down Expand Up @@ -773,15 +792,17 @@ export const setBackgroundColorUpdater = (
* Return the initial input style
* @return Object
*/
export function getInitialInputStyle() {
export function getInitialInputStyle(): InputStyle {
return {
id: null,
accessToken: null,
error: false,
isValid: false,
label: null,
style: null,
url: null,
icon: null,
custom: true
custom: 'LOCAL',
uploadedFile: null
};
}
3 changes: 2 additions & 1 deletion src/schemas/src/index.ts
Expand Up @@ -59,6 +59,7 @@ export {
propertiesV1 as datasetPropertiesV1
} from './dataset-schema';
export * from './vis-state-schema';
export {default as mapStyleSchema} from './map-style-schema';
/** NOTE: `MapStyleSchemaV1` is actually for `mapStyle.mapStyles` (original naming can be unclear) */
export {default as mapStyleSchema, MapStyleSchemaV1, CustomMapStyleSchema} from './map-style-schema';
export {default as mapStateSchema} from './map-state-schema';
export {default as Schema} from './schema';
4 changes: 2 additions & 2 deletions src/schemas/src/map-style-schema.ts
Expand Up @@ -30,13 +30,13 @@ export const customMapStylePropsV1 = {
url: null
};

const CustomMapStyleSchema = new Schema({
export const CustomMapStyleSchema = new Schema({
version: VERSIONS.v1,
key: 'customStyle',
properties: customMapStylePropsV1
});

class MapStyleSchemaV1 extends Schema {
export class MapStyleSchemaV1 extends Schema {
version = VERSIONS.v1;
key = 'mapStyles';
save(mapStyles) {
Expand Down
4 changes: 2 additions & 2 deletions src/schemas/src/schema-manager.ts
Expand Up @@ -30,7 +30,7 @@ import {visStateSchema} from './vis-state-schema';
import {CURRENT_VERSION, VERSIONS} from './versions';
import {isPlainObject} from '@kepler.gl/utils';

import {MapInfo, SavedVisState, SavedMapStyle, ParsedConfig} from '@kepler.gl/types';
import {MapInfo, SavedVisState, SavedMapStyle, ParsedConfig, BaseMapStyle} from '@kepler.gl/types';

export type SavedMapState = {
bearing: number;
Expand All @@ -52,7 +52,7 @@ export type SavedLayerGroups = {
export type SavedCustomMapStyle = {
[key: string]: {
accessToken: string;
custom: boolean;
custom: BaseMapStyle['custom'];
icon: string;
id: string;
label: string;
Expand Down
12 changes: 10 additions & 2 deletions src/types/reducers.d.ts
Expand Up @@ -291,6 +291,12 @@ export type LayerGroup = {
defaultVisibility: boolean;
};

export type CustomStyleType =
| 'LOCAL'
| 'MANAGED'
// boolean for backwards compatability with previous map configs
| boolean;

export type BaseMapStyle = {
id: string;
label: string;
Expand All @@ -299,7 +305,7 @@ export type BaseMapStyle = {
style?: Object;
layerGroups: LayerGroup[];
accessToken?: string;
custom?: boolean;
custom?: CustomStyleType;
colorMode?: BASE_MAP_COLOR_MODES;
};

Expand Down Expand Up @@ -437,14 +443,16 @@ export type VisibleLayerGroups = {
};

export type InputStyle = {
id?: string | null;
accessToken: string | null;
error: boolean;
isValid: boolean;
label: string | null;
style: any | null;
url: string | null;
icon: string | null;
custom: boolean;
custom: CustomStyleType;
uploadedFile: File | null;
};

export type FilterRecord = {
Expand Down
12 changes: 9 additions & 3 deletions test/helpers/mock-state.js
Expand Up @@ -429,10 +429,11 @@ export function mockStateWithLayerDimensions(state) {
return resultState;
}

function mockStateWithCustomMapStyle() {
function mockStateWithCustomMapStyle(customType = 'LOCAL') {
const initialState = cloneDeep(InitialState);
const testCustomMapStyle = {
...getInitialInputStyle(),
id: 'smoothie_the_cat',
accessToken: 'secret_token',
isValid: true,
label: 'Smoothie the Cat',
Expand All @@ -442,7 +443,10 @@ function mockStateWithCustomMapStyle() {
layers: [{id: 'background'}, {id: 'road'}, {id: 'label'}],
name: 'Smoothie the Cat'
},
url: 'mapbox://styles/shanhe/smoothie.the.cat'
url: 'mapbox://styles/shanhe/smoothie.the.cat',
icon:
'https://api.mapbox.com/styles/v1/shanhe/smoothie.the.cat/static/-122.3391,37.7922,9,0,0/400x300?access_token=secret_token&logo=false&attribution=false',
custom: customType
};

// add custom map style
Expand Down Expand Up @@ -879,7 +883,9 @@ export const StateWFilters = mockStateWithFilters();
export const StateWFilesFiltersLayerColor = mockStateWithLayerDimensions(StateWFilters);
export const StateWMultiFilters = mockStateWithMultiFilters();

export const StateWCustomMapStyle = mockStateWithCustomMapStyle();
export const StateWCustomMapStyleLegacy = mockStateWithCustomMapStyle(true);
export const StateWCustomMapStyleLocal = mockStateWithCustomMapStyle('LOCAL');
export const StateWCustomMapStyleManaged = mockStateWithCustomMapStyle('MANAGED');
export const StateWSplitMaps = mockStateWithSplitMaps();
export const StateWTrips = mockStateWithTripData();
export const StateWTripGeojson = mockStateWithTripGeojson();
Expand Down

0 comments on commit a8599dc

Please sign in to comment.