From 0481c428f664897ae566fe9fa338b473f6d736c1 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Fri, 27 Nov 2020 22:24:19 +0900 Subject: [PATCH 001/138] =?UTF-8?q?[refactor]=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B6=84=EB=A5=98,=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기능유형/세부유형/스타일 관련 상태는 계속해서 사용하므로 store/common/type 으로 이동하였음 - 기존 상태 구조는 세부 유형의 타입에 따라서 예외처리가 빈번하게 발생해서 불필요한 복잡도가 증가하였음. 따라서 Label이 text, icon으로 한 depth 더 들어가지 않고 동일하게 처리될 수 있도록 속성 수정함 - 이를 반영해서 초기 속성 불러오는 함수도 수정하고, 함수명도 조금 더 직관적이도록 수정하였음 --- src/store/common/properties.ts | 52 ++++++++++------------------------ src/store/common/type.ts | 43 ++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/store/common/properties.ts b/src/store/common/properties.ts index 6b784e0..a3f2d16 100644 --- a/src/store/common/properties.ts +++ b/src/store/common/properties.ts @@ -1,23 +1,6 @@ -export interface StyleType { - isChanged: boolean; - visibility: string; - color: string; - weight: number; - saturation: number; - lightness: number; -} +import { StyleType, ElementType, FeatureType } from './type'; -export interface CommonType { - fill: StyleType; - stroke: StyleType; -} - -export interface LabelType { - text: CommonType; - icon: StyleType; -} - -const style: StyleType = { +export const style: StyleType = { isChanged: false, visibility: 'inherit', color: '#000000', @@ -26,27 +9,22 @@ const style: StyleType = { lightness: 0, }; -const getStyle = (): StyleType => { +const getDefaultStyle = (): StyleType => { return JSON.parse(JSON.stringify(style)); }; -const label = { - text: { - fill: getStyle(), - stroke: getStyle(), - }, - icon: getStyle(), -}; - -const section = { - fill: getStyle(), - stroke: getStyle(), -}; - -export const getLabel = (): LabelType => { - return JSON.parse(JSON.stringify(label)); +const getDefaultElement = (): ElementType => { + return { + fill: getDefaultStyle(), + stroke: getDefaultStyle(), + }; }; -export const getSection = (): CommonType => { - return JSON.parse(JSON.stringify(section)); +export const getDefaultFeature = (): FeatureType => { + return { + isChanged: false, + section: getDefaultElement(), + labelText: getDefaultElement(), + labelIcon: getDefaultStyle(), + }; }; diff --git a/src/store/common/type.ts b/src/store/common/type.ts index ab7a6e0..3882ddb 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,9 +1,40 @@ -import { LabelType, CommonType } from './properties'; +export type ElementNameType = 'section' | 'labelText' | 'labelIcon'; +export type SubElementNameType = 'fill' | 'stroke'; +export type FeatureNameOneType = 'water' | 'marker'; +export type FeatureNameMultiType = + | 'poi' + | 'administrative' + | 'landscape' + | 'road' + | 'transit'; +export type FeatureNameType = FeatureNameMultiType | FeatureNameOneType; + +export interface StyleType { + isChanged: boolean; + visibility: string; + color: string; + weight: number; + saturation: number; + lightness: number; +} +export interface ElementType { + fill: StyleType; + stroke: StyleType; +} +export interface FeatureType { + isChanged: boolean; + section: ElementType; + labelText: ElementType; + labelIcon: StyleType; +} export interface FeatureState { - [name: string]: { - isChanged: boolean; - section: CommonType; - label: LabelType; - }; + [name: string]: FeatureType; } + +export type ActionPayload = { + feature: string; + element: ElementNameType; + subElement?: SubElementNameType; + style: StyleType; +}; From 71516e80135bb1f7b40957226a99c9163c91fe89 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Fri, 27 Nov 2020 22:28:53 +0900 Subject: [PATCH 002/138] =?UTF-8?q?[refactor]=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EA=B3=B5=ED=86=B5=20Action,=20Reducer=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 요소 유형에 따라서 다른 Action으로 분류할 필요성이 없어졌고, 코드 중복을 줄이기 위해서 SET을 하나의 액션으로 분류하였음 - 리듀서 또한 코드 중복이 많아서 함께 SET으로 통합해서 처리하였음 --- src/store/common/action.ts | 47 ++++++++++--------------------------- src/store/common/reducer.ts | 43 ++++++++------------------------- 2 files changed, 23 insertions(+), 67 deletions(-) diff --git a/src/store/common/action.ts b/src/store/common/action.ts index 8ed6d3a..26ac171 100644 --- a/src/store/common/action.ts +++ b/src/store/common/action.ts @@ -1,49 +1,28 @@ -import { StyleType } from './properties'; +import { ActionPayload } from './type'; export const INIT = 'INIT' as const; +export const SET = 'SET' as const; export const SET_SECTION = 'SET_SECTION' as const; export const SET_LABEL_TEXT = 'SET_LABEL_TEXT' as const; export const SET_LABEL_ICON = 'SET_LABEL_ICON' as const; -export type ElementType = 'fill' | 'stroke'; export interface SetType { - type: typeof SET_SECTION | typeof SET_LABEL_ICON | typeof SET_LABEL_TEXT; - payload: { - feature: string; - element?: ElementType; - style: StyleType; - }; + type: typeof SET; + payload: ActionPayload; } export const init = (): { type: typeof INIT } => ({ type: INIT, }); -export const setSection = ( - feature: string, - element: ElementType, - style: StyleType -): SetType => ({ - type: SET_SECTION, - payload: { feature, style, element }, +export const setStyle = ({ + feature, + element, + subElement, + style, +}: ActionPayload): SetType => ({ + type: SET, + payload: { feature, element, subElement, style }, }); -export const setLabelText = ( - feature: string, - element: ElementType, - style: StyleType -): SetType => ({ - type: SET_LABEL_TEXT, - payload: { feature, style, element }, -}); - -export const setLabelIcon = (feature: string, style: StyleType): SetType => ({ - type: SET_LABEL_ICON, - payload: { feature, style }, -}); - -export type ActionType = - | ReturnType - | ReturnType - | ReturnType - | ReturnType; +export type ActionType = ReturnType | ReturnType; diff --git a/src/store/common/reducer.ts b/src/store/common/reducer.ts index 1f8ead7..61ceddc 100644 --- a/src/store/common/reducer.ts +++ b/src/store/common/reducer.ts @@ -1,14 +1,8 @@ import renderingData from '../../utils/rendering-data/featureTypeData'; import { FeatureState } from './type'; -import { getLabel, getSection } from './properties'; -import { - INIT, - SET_SECTION, - SET_LABEL_TEXT, - SET_LABEL_ICON, - ActionType, -} from './action'; +import { getDefaultFeature } from './properties'; +import { INIT, SET, ActionType } from './action'; interface ReducerType { (state: FeatureState, action: ActionType): FeatureState; @@ -21,11 +15,7 @@ export default function getReducer(IDX: number): ReducerType { ]; const initialState = subFeatures.reduce((acc: FeatureState, cur: string) => { - acc[cur] = { - isChanged: false, - section: getSection(), - label: getLabel(), - }; + acc[cur] = getDefaultFeature(); return acc; }, {}); @@ -38,31 +28,18 @@ export default function getReducer(IDX: number): ReducerType { switch (action.type) { case INIT: return initialState; - case SET_SECTION: { - const { feature, element, style } = action.payload; + case SET: { + const { feature, element, subElement, style } = action.payload; if (!featureTypes.includes(feature)) return state; const newState = JSON.parse(JSON.stringify(state)); - newState[feature].section[element as string] = style; - return newState; - } - case SET_LABEL_TEXT: { - const { feature, element, style } = action.payload; - if (!featureTypes.includes(feature)) return state; - - const newState = JSON.parse(JSON.stringify(state)); - newState[feature].label.text[element as string] = style; - - return newState; - } - case SET_LABEL_ICON: { - const { feature, style } = action.payload; - if (!featureTypes.includes(feature)) return state; - - const newState = JSON.parse(JSON.stringify(state)); - newState[feature].icon = style; + if (element === 'labelIcon') { + newState[feature][element] = style; + return newState; + } + newState[feature][element][subElement as string] = style; return newState; } default: From a1c3576ad7a10bd2df6e2db6e652ef987fc328d1 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Fri, 27 Nov 2020 22:33:44 +0900 Subject: [PATCH 003/138] =?UTF-8?q?[refactor]=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EB=AC=BC,=20=EB=A7=88=EC=BB=A4=20Aciton,=20Reducer?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 공통 Reducer와 동일하게 중복되는 함수를 제거하도록 조정하였음 - 그런데 공통 Action과 물, 마커 Action의 처리에 있어서 feature를 다른 단위로 취급하는 버그가 아직 남아있음 - 위 이슈를 해결하기 위해서 Aciton을 Reducer 별로 다르게 표기하던지, payload에 항목을 하나 상위 기능 유형을 추가할 필요가 있을 것 같음 --- src/store/style/markerReducer.ts | 60 +++++++------------------------- src/store/style/waterReducer.ts | 60 +++++++------------------------- 2 files changed, 26 insertions(+), 94 deletions(-) diff --git a/src/store/style/markerReducer.ts b/src/store/style/markerReducer.ts index a77d19e..95553f0 100644 --- a/src/store/style/markerReducer.ts +++ b/src/store/style/markerReducer.ts @@ -1,62 +1,28 @@ -import { - getLabel, - getSection, - LabelType, - CommonType, -} from '../common/properties'; +import { getDefaultFeature } from '../common/properties'; +import { FeatureType } from '../common/type'; +import { INIT, SET, ActionType } from '../common/action'; -import { - INIT, - SET_SECTION, - SET_LABEL_TEXT, - SET_LABEL_ICON, - ActionType, -} from '../common/action'; - -export interface MarkerType { - isChanged: boolean; - section: CommonType; - label: LabelType; -} - -const initialState = { - isChanged: false, - section: getSection(), - label: getLabel(), -}; +const initialState = getDefaultFeature(); export default function markerReducer( - state: MarkerType = initialState, + state: FeatureType = initialState, action: ActionType -): MarkerType { +): FeatureType { switch (action.type) { case INIT: return initialState; - case SET_SECTION: { - const { feature, element, style } = action.payload; - if (feature !== 'marker') return state; - - const newState = JSON.parse(JSON.stringify(state)); - newState.section[element as string] = style; - - return newState; - } - case SET_LABEL_TEXT: { - const { feature, element, style } = action.payload; + case SET: { + const { feature, element, subElement, style } = action.payload; if (feature !== 'marker') return state; const newState = JSON.parse(JSON.stringify(state)); - newState.label.text[element as string] = style; - return newState; - } - case SET_LABEL_ICON: { - const { feature, style } = action.payload; - if (feature !== 'marker') return state; - - const newState = JSON.parse(JSON.stringify(state)); - newState.icon = style; + if (element === 'labelIcon') { + newState[element] = style; + return newState; + } + newState[element][subElement as string] = style; return newState; } default: diff --git a/src/store/style/waterReducer.ts b/src/store/style/waterReducer.ts index 6c84d50..0ae8b94 100644 --- a/src/store/style/waterReducer.ts +++ b/src/store/style/waterReducer.ts @@ -1,62 +1,28 @@ -import { - getLabel, - getSection, - LabelType, - CommonType, -} from '../common/properties'; +import { getDefaultFeature } from '../common/properties'; +import { FeatureType } from '../common/type'; +import { INIT, SET, ActionType } from '../common/action'; -import { - INIT, - SET_SECTION, - SET_LABEL_TEXT, - SET_LABEL_ICON, - ActionType, -} from '../common/action'; - -export interface WaterType { - isChanged: boolean; - section: CommonType; - label: LabelType; -} - -const initialState = { - isChanged: false, - section: getSection(), - label: getLabel(), -}; +const initialState = getDefaultFeature(); export default function waterReducer( - state: WaterType = initialState, + state: FeatureType = initialState, action: ActionType -): WaterType { +): FeatureType { switch (action.type) { case INIT: return initialState; - case SET_SECTION: { - const { feature, element, style } = action.payload; - if (feature !== 'water') return state; - - const newState = JSON.parse(JSON.stringify(state)); - newState.section[element as string] = style; - - return newState; - } - case SET_LABEL_TEXT: { - const { feature, element, style } = action.payload; + case SET: { + const { feature, element, subElement, style } = action.payload; if (feature !== 'water') return state; const newState = JSON.parse(JSON.stringify(state)); - newState.label.text[element as string] = style; - return newState; - } - case SET_LABEL_ICON: { - const { feature, style } = action.payload; - if (feature !== 'water') return state; - - const newState = JSON.parse(JSON.stringify(state)); - newState.icon = style; + if (element === 'labelIcon') { + newState[element] = style; + return newState; + } + newState[element][subElement as string] = style; return newState; } default: From 9bdaa3dc1b27cd003a1a7cf2abb9d36a92d73fea Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Fri, 27 Nov 2020 22:55:11 +0900 Subject: [PATCH 004/138] =?UTF-8?q?[refactor]=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B6=84=EB=A5=98=20=EB=8B=A4=EB=A5=B8?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=8F=84=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - store/common/type으로 분류된 타입들 import 경로 수정함 - 기능 유형 인터페이스 2개로 분리되어 있었는데, 둘다 사용하는 경우가 많아서 하나로 통합된 interface도 하나더 정의해 import하도록 하였음 --- .../Sidebar/SidebarContentMore/DetailType.tsx | 6 +++-- .../SidebarContentMore/FeatureType.tsx | 4 +++- .../SidebarContentMore/FeatureTypeItem.tsx | 9 +++----- src/hooks/sidebar/useDetailType.ts | 22 ++++++++----------- src/hooks/sidebar/useFeatureTypeItem.ts | 12 +++------- src/utils/rendering-data/featureTypeData.ts | 10 ++------- 6 files changed, 24 insertions(+), 39 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 82a93db..c347351 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -6,7 +6,9 @@ import useSidebarType, { import ListItem, { paddingStepType, paddingStep } from './DetailTypeItem'; import useDetailType from '../../../hooks/sidebar/useDetailType'; import Styler from './Styler'; -import { FeatureNameType } from '../../../utils/rendering-data/featureTypeData'; +import { + FeatureNameType, +} from '../../../store/common/type'; interface PaddingProp { padding: paddingStepType; @@ -58,7 +60,7 @@ const CheckRight = styled.div` `; interface DetailTypeProps { - featureName: string; + featureName: FeatureNameType; subFeatureName: string; } diff --git a/src/components/Sidebar/SidebarContentMore/FeatureType.tsx b/src/components/Sidebar/SidebarContentMore/FeatureType.tsx index ed38902..da87cae 100644 --- a/src/components/Sidebar/SidebarContentMore/FeatureType.tsx +++ b/src/components/Sidebar/SidebarContentMore/FeatureType.tsx @@ -6,6 +6,8 @@ import DetailType from './DetailType'; import useSidebarType, { SidebarHookType, } from '../../../hooks/sidebar/useSidebarType'; +import { FeatureNameType } from '../../../store/common/type'; + import FeatureTypeItem from './FeatureTypeItem'; interface WrapperProps { @@ -51,7 +53,7 @@ function FeatureType(): React.ReactElement { ))} diff --git a/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx b/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx index bd16082..cf44206 100644 --- a/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx +++ b/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx @@ -1,10 +1,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; -import { - FeaturesType, - FeatureNameType, - FeatureNameOneType, -} from '../../../utils/rendering-data/featureTypeData'; +import { FeaturesType } from '../../../utils/rendering-data/featureTypeData'; +import { FeatureNameType } from '../../../store/common/type'; import useFeatureTypeItemHook from '../../../hooks/sidebar/useFeatureTypeItem'; interface ListProps { @@ -61,7 +58,7 @@ const Pointer = styled.span` `; interface FeatureTypeItemProps { - typeKey: FeatureNameType | FeatureNameOneType; + typeKey: FeatureNameType; typeName: string; features: FeaturesType[]; sidebarTypeName: string; diff --git a/src/hooks/sidebar/useDetailType.ts b/src/hooks/sidebar/useDetailType.ts index e0e9a8f..7ba7ecc 100644 --- a/src/hooks/sidebar/useDetailType.ts +++ b/src/hooks/sidebar/useDetailType.ts @@ -3,21 +3,17 @@ import { RootState } from '../../store'; import { FeatureNameType, FeatureNameOneType, -} from '../../utils/rendering-data/featureTypeData'; -import { CommonType, LabelType } from '../../store/common/properties'; - -interface DetailType { - section: CommonType; - label: LabelType; -} + FeatureNameMultiType, + FeatureType, +} from '../../store/common/type'; interface UseDetailTypeProps { - featureName: string; + featureName: FeatureNameType; subFeatureName: string; } -interface UseDetailTypeType { - detail: DetailType; +export interface UseDetailHookType { + detail: FeatureType; } const dummyDetail = { @@ -28,7 +24,7 @@ const dummyDetail = { function useDetailType({ featureName, subFeatureName, -}: UseDetailTypeProps): UseDetailTypeType { +}: UseDetailTypeProps): UseDetailHookType { const detail = useSelector((state) => { if (!featureName) { return dummyDetail; @@ -37,8 +33,8 @@ function useDetailType({ return state[featureName as FeatureNameOneType]; } - return state[featureName as FeatureNameType][subFeatureName]; - }) as DetailType; + return state[featureName as FeatureNameMultiType][subFeatureName]; + }) as FeatureType; return { detail, diff --git a/src/hooks/sidebar/useFeatureTypeItem.ts b/src/hooks/sidebar/useFeatureTypeItem.ts index b4b1fbe..994c51a 100644 --- a/src/hooks/sidebar/useFeatureTypeItem.ts +++ b/src/hooks/sidebar/useFeatureTypeItem.ts @@ -1,19 +1,13 @@ import { useSelector } from 'react-redux'; import { RootState } from '../../store'; -import { FeatureState } from '../../store/common/type'; - -import { - FeatureNameType, - FeatureNameOneType, -} from '../../utils/rendering-data/featureTypeData'; +import { FeatureState, FeatureNameType } from '../../store/common/type'; interface useFeatureTypeItemType { - // 나중에 | 연산으로 다양한 타입으로 수정 필요 featureList: FeatureState; } export interface useFeatureTypeItemProps { - featureName: FeatureNameType | FeatureNameOneType; + featureName: FeatureNameType; } function useFeatureTypeItem({ @@ -21,7 +15,7 @@ function useFeatureTypeItem({ }: useFeatureTypeItemProps): useFeatureTypeItemType { const featureList = useSelector( (state) => state[featureName] - ) as FeatureState; // 나중에 다양한 타입으로 수정 필요 + ) as FeatureState; return { featureList, diff --git a/src/utils/rendering-data/featureTypeData.ts b/src/utils/rendering-data/featureTypeData.ts index 5492066..51893c5 100644 --- a/src/utils/rendering-data/featureTypeData.ts +++ b/src/utils/rendering-data/featureTypeData.ts @@ -1,17 +1,11 @@ -export type FeatureNameOneType = 'water' | 'marker'; -export type FeatureNameType = - | 'poi' - | 'administrative' - | 'landscape' - | 'road' - | 'transit'; +import { FeatureNameType } from '../../store/common/type'; export interface FeaturesType { key: string; name: string; } export interface DataType { - typeKey: FeatureNameType | FeatureNameOneType; + typeKey: FeatureNameType; typeName: string; features: FeaturesType[]; } From 4d823805e9b0e3125cb6d696aa7cc1bc35899a6a Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Fri, 27 Nov 2020 23:04:41 +0900 Subject: [PATCH 005/138] =?UTF-8?q?[fix]=20=EC=84=B8=EB=B6=80=20=EC=9C=A0?= =?UTF-8?q?=ED=98=95=20=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 세부 유형 선택 중인 상태을 상위, 하위 요소를 구분해서 상태를 저장하도록 수정함 - 세부 유형을 클릭하면 상위/하위 요소 상태 업데이트하도록 수정함 - 리팩토링 후 ListItem 컴포넌트 내부에서 사용하지 않는 속성은 제거하고, UI로 표시될 텍스트만 남겼음 - 수정한 함수를 Hook으로 분리, 하드코딩된 유형을 분리 등의 리팩토링이 요구됨 --- .../Sidebar/SidebarContentMore/DetailType.tsx | 75 +++++++++++-------- .../SidebarContentMore/DetailTypeItem.tsx | 16 +--- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index c347351..f9b5131 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -4,10 +4,14 @@ import useSidebarType, { SidebarHookType, } from '../../../hooks/sidebar/useSidebarType'; import ListItem, { paddingStepType, paddingStep } from './DetailTypeItem'; -import useDetailType from '../../../hooks/sidebar/useDetailType'; +import useDetailType, { + UseDetailHookType, +} from '../../../hooks/sidebar/useDetailType'; import Styler from './Styler'; import { FeatureNameType, + ElementNameType, + SubElementNameType, } from '../../../store/common/type'; interface PaddingProp { @@ -70,12 +74,35 @@ function DetailType({ }: DetailTypeProps): React.ReactElement { const { sidebarTypeName, + sidebarSubTypeName, sidebarTypeClickHandler, + sidebarSubTypeClickHandler, }: SidebarHookType = useSidebarType(); const { - detail: { section, label }, - } = useDetailType({ featureName, subFeatureName }); + detail: { section, labelText, labelIcon }, + }: UseDetailHookType = useDetailType({ + featureName, + subFeatureName, + }); + + const styleClickHandler = ( + elementName: ElementNameType, + subElementName?: SubElementNameType + ) => { + sidebarTypeClickHandler(elementName); + if (subElementName) sidebarSubTypeClickHandler(subElementName); + }; + + const getIsSelected = ( + elementName: ElementNameType, + subElementName?: SubElementNameType + ) => { + if (!subElementName) return sidebarTypeName === elementName; + return ( + sidebarTypeName === elementName && sidebarSubTypeName === subElementName + ); + }; if (!featureName) { return <>; @@ -89,57 +116,45 @@ function DetailType({ 구역 {section?.fill.isChanged ? : <>} { - return sidebarTypeClickHandler(name as FeatureNameType); - }} + clickHandler={() => styleClickHandler('section', 'fill')} name="채우기" - parent="구역" /> {section?.stroke.isChanged ? : <>} { - sidebarTypeClickHandler(name as FeatureNameType); - }} + clickHandler={() => styleClickHandler('section', 'stroke')} name="윤곽선" - parent="구역" /> 라벨 텍스트 - {label?.text.fill.isChanged ? : <>} + {labelText.fill.isChanged ? : <>} { - sidebarTypeClickHandler(name as FeatureNameType); - }} + clickHandler={() => styleClickHandler('labelText', 'fill')} name="채우기" - parent="텍스트" /> - {label?.text.stroke.isChanged ? : <>} + {labelText.stroke.isChanged ? : <>} { - sidebarTypeClickHandler(name as FeatureNameType); - }} + clickHandler={() => styleClickHandler('labelText', 'stroke')} name="윤곽선" - parent="텍스트" /> - {label?.icon.isChanged ? : <>} + {labelIcon.isChanged ? : <>} { - sidebarTypeClickHandler(name as FeatureNameType); - }} + clickHandler={() => styleClickHandler('labelIcon')} name="아이콘" /> diff --git a/src/components/Sidebar/SidebarContentMore/DetailTypeItem.tsx b/src/components/Sidebar/SidebarContentMore/DetailTypeItem.tsx index cd03501..b7a16a1 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailTypeItem.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailTypeItem.tsx @@ -4,11 +4,10 @@ import styled from '../../../utils/styles/styled'; export type paddingStepType = 'first' | 'second' | 'third'; interface ListItemProps { + isSelected: boolean; padding: paddingStepType; - clickHandler: (name: string) => void; + clickHandler: () => void; name: string; - parent?: string; - detailName: string; } interface PaddingProp { @@ -46,20 +45,13 @@ const Item = styled.li` const Pointer = styled.span``; function DetailTypeItem({ + isSelected, padding, clickHandler, name, - parent, - detailName, }: ListItemProps): React.ReactElement { return ( - { - clickHandler(`${parent} ${name}`); - }} - > + {name} {'>'} From 096b76b670859338e4eac57ea51a8dabcfe6f336 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Fri, 27 Nov 2020 23:08:24 +0900 Subject: [PATCH 006/138] =?UTF-8?q?[feat]=20Style=20=EC=A0=84=EC=97=AD=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=A1=B0=ED=9A=8C=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20Hook=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useDetailType과 유사한 방법으로 useStyleType Hook을 구현함 - useStyle의 경우에는 현재 선택중인 기능/세부 유형 정보도 모두 필요해 Styler 컴포넌트가 상위 컴포넌트에서 전달받도록 수정함 --- .../Sidebar/SidebarContentMore/DetailType.tsx | 7 ++- .../Sidebar/SidebarContentMore/Styler.tsx | 28 ++++++++++- src/hooks/sidebar/useStyleType.ts | 50 +++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 src/hooks/sidebar/useStyleType.ts diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index f9b5131..7cdf04f 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -159,7 +159,12 @@ function DetailType({ /> - + ); } diff --git a/src/components/Sidebar/SidebarContentMore/Styler.tsx b/src/components/Sidebar/SidebarContentMore/Styler.tsx index 20a54fa..3104cdf 100644 --- a/src/components/Sidebar/SidebarContentMore/Styler.tsx +++ b/src/components/Sidebar/SidebarContentMore/Styler.tsx @@ -1,5 +1,14 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; +import { + FeatureNameType, + ElementNameType, + SubElementNameType, +} from '../../../store/common/type'; +import useStyleType, { + UseStyleHookType, +} from '../../../hooks/sidebar/useStyleType'; + import ColorStyle from './ColorStyle'; import WeightStyle from './WeightStyle'; import SaturationStyle from './SaturationStyle'; @@ -31,10 +40,25 @@ const Hr = styled.hr` `; interface StylerProps { - detailName: string; + featureName: FeatureNameType; + subFeatureName: string; + detailName: ElementNameType; + subDetailName?: SubElementNameType; } -function Styler({ detailName }: StylerProps): React.ReactElement { +function Styler({ + featureName, + subFeatureName, + detailName, + subDetailName, +}: StylerProps): React.ReactElement { + const { style }: UseStyleHookType = useStyleType({ + featureName, + subFeatureName, + detailName, + subDetailName, + }); + if (!detailName) { return <>; } diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts new file mode 100644 index 0000000..8193591 --- /dev/null +++ b/src/hooks/sidebar/useStyleType.ts @@ -0,0 +1,50 @@ +import { useSelector } from 'react-redux'; +import { RootState } from '../../store'; +import { + FeatureNameType, + FeatureNameOneType, + FeatureNameMultiType, + StyleType, + ElementNameType, + SubElementNameType, +} from '../../store/common/type'; +import { style as dummyStyle } from '../../store/common/properties'; + +interface UseStyleTypeProps { + featureName: FeatureNameType; + subFeatureName: string; + detailName: ElementNameType; + subDetailName?: SubElementNameType; +} + +export interface UseStyleHookType { + style: StyleType; +} + +function useStyleType({ + featureName, + subFeatureName, + detailName, + subDetailName, +}: UseStyleTypeProps): UseStyleHookType { + const style = useSelector((state) => { + if (!detailName) { + return dummyStyle; + } + let feature; + if (featureName === 'water' || featureName === 'marker') { + feature = state[featureName as FeatureNameOneType]; + } else { + feature = state[featureName as FeatureNameMultiType][subFeatureName]; + } + + if (detailName === 'labelIcon') return feature.labelIcon; + return feature[detailName][subDetailName as SubElementNameType]; + }) as StyleType; + + return { + style, + }; +} + +export default useStyleType; From 7ac0e00f0cfe8de577440bbd51c9facc3300d4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Sat, 28 Nov 2020 12:07:42 +0900 Subject: [PATCH 007/138] =?UTF-8?q?[feat]=20redux-devtool-extension?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - redux를 크롬에서 확인하기 위한 redux-devtool-extension을 추가함 --- package-lock.json | 5 +++++ package.json | 3 ++- src/store/index.ts | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 06b1252..e34ecd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13270,6 +13270,11 @@ "symbol-observable": "^1.2.0" } }, + "redux-devtools-extension": { + "version": "2.13.8", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz", + "integrity": "sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==" + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", diff --git a/package.json b/package.json index 1b7db12..7f40996 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@types/node": "^12.19.4", "@types/react": "^16.9.56", "@types/react-dom": "^16.9.9", - "dotenv": "^8.2.0", + "dotenv": "^8.2.0", "emotion-reset": "^2.0.7", "emotion-theming": "^10.0.27", "mapbox-gl": "^1.12.0", @@ -21,6 +21,7 @@ "react-redux": "^7.2.2", "react-scripts": "4.0.0", "redux": "^4.0.5", + "redux-devtools-extension": "^2.13.8", "typescript": "^4.0.5", "web-vitals": "^0.2.4" }, diff --git a/src/store/index.ts b/src/store/index.ts index bef4662..58142cc 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,4 +1,5 @@ import { combineReducers, createStore } from 'redux'; +import { composeWithDevTools } from 'redux-devtools-extension'; import map from './map/reducer'; import poi from './style/poiReducer'; import transit from './style/transitReducer'; @@ -18,7 +19,8 @@ const rootReducer = combineReducers({ water, marker, }); -const store = createStore(rootReducer); + +const store = createStore(rootReducer, composeWithDevTools()); export type RootState = ReturnType; From 8271c4441d4c40fe1817cf46789ce4095af0c23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Sat, 28 Nov 2020 15:03:00 +0900 Subject: [PATCH 008/138] =?UTF-8?q?[feat]=20typeCheck=20utils=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 여러 타입들을 체크할 수 잇는 파일 생성 - 다른 타입체크 상황이 생기면 추가 예정 --- src/utils/typeCheck.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/utils/typeCheck.ts diff --git a/src/utils/typeCheck.ts b/src/utils/typeCheck.ts new file mode 100644 index 0000000..469e681 --- /dev/null +++ b/src/utils/typeCheck.ts @@ -0,0 +1,8 @@ +import { FeatureNameType, FeatureNameSingleType } from '../store/common/type'; + +export function featureNameTypeCheck( + name: FeatureNameType +): name is FeatureNameSingleType { + if (name === 'water' || name === 'marker') return true; + return false; +} From b756e80e608f4a2c36468dfdf5aea32b777a9420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Sat, 28 Nov 2020 15:04:53 +0900 Subject: [PATCH 009/138] =?UTF-8?q?[refactor]=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C=20=EA=B0=80=EC=9E=A5?= =?UTF-8?q?=20=EC=83=81=EC=9C=84=20feature=EB=8F=84=20=EA=B0=99=EC=9D=B4?= =?UTF-8?q?=20=EC=9E=85=EB=A0=A5=EB=B0=9B=EB=8F=84=EB=A1=9D=20=ED=95=A8=20?= =?UTF-8?q?|=20action=20Type=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 스타일 변경 할 때 가장 상위 feature도 payload값으로 같이 입력받음(구별 위함) - action Type의 위치를 다른 type들이 같이 있는 파일 안으로 이동 --- src/store/common/action.ts | 8 ++------ src/store/common/reducer.ts | 24 +++++++++++++++--------- src/store/common/type.ts | 10 +++++++--- src/store/style/markerReducer.ts | 6 +++--- src/store/style/waterReducer.ts | 6 +++--- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/store/common/action.ts b/src/store/common/action.ts index 26ac171..3ba56c9 100644 --- a/src/store/common/action.ts +++ b/src/store/common/action.ts @@ -2,9 +2,6 @@ import { ActionPayload } from './type'; export const INIT = 'INIT' as const; export const SET = 'SET' as const; -export const SET_SECTION = 'SET_SECTION' as const; -export const SET_LABEL_TEXT = 'SET_LABEL_TEXT' as const; -export const SET_LABEL_ICON = 'SET_LABEL_ICON' as const; export interface SetType { type: typeof SET; @@ -17,12 +14,11 @@ export const init = (): { type: typeof INIT } => ({ export const setStyle = ({ feature, + subFeature, element, subElement, style, }: ActionPayload): SetType => ({ type: SET, - payload: { feature, element, subElement, style }, + payload: { feature, subFeature, element, subElement, style }, }); - -export type ActionType = ReturnType | ReturnType; diff --git a/src/store/common/reducer.ts b/src/store/common/reducer.ts index 61ceddc..a5a353f 100644 --- a/src/store/common/reducer.ts +++ b/src/store/common/reducer.ts @@ -1,8 +1,8 @@ import renderingData from '../../utils/rendering-data/featureTypeData'; -import { FeatureState } from './type'; +import { FeatureState, ActionType, SubElementNameType } from './type'; import { getDefaultFeature } from './properties'; -import { INIT, SET, ActionType } from './action'; +import { INIT, SET } from './action'; interface ReducerType { (state: FeatureState, action: ActionType): FeatureState; @@ -19,8 +19,6 @@ export default function getReducer(IDX: number): ReducerType { return acc; }, {}); - const featureTypes = renderingData[IDX].features.map((v) => v.key); - return function reducer( state: FeatureState = initialState, action: ActionType @@ -29,17 +27,25 @@ export default function getReducer(IDX: number): ReducerType { case INIT: return initialState; case SET: { - const { feature, element, subElement, style } = action.payload; - if (!featureTypes.includes(feature)) return state; + const { + feature, + subFeature, + element, + subElement, + style, + } = action.payload; + if (feature === renderingData[IDX].typeKey) return state; - const newState = JSON.parse(JSON.stringify(state)); + const newState: FeatureState = JSON.parse(JSON.stringify(state)); if (element === 'labelIcon') { - newState[feature][element] = style; + newState[subFeature as string][element] = style; return newState; } - newState[feature][element][subElement as string] = style; + newState[subFeature as string][element][ + subElement as SubElementNameType + ] = style; return newState; } default: diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 3882ddb..30a4ab6 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,14 +1,17 @@ +import { init, setStyle } from './action'; + +export type ActionType = ReturnType | ReturnType; export type ElementNameType = 'section' | 'labelText' | 'labelIcon'; export type SubElementNameType = 'fill' | 'stroke'; -export type FeatureNameOneType = 'water' | 'marker'; +export type FeatureNameSingleType = 'water' | 'marker'; export type FeatureNameMultiType = | 'poi' | 'administrative' | 'landscape' | 'road' | 'transit'; -export type FeatureNameType = FeatureNameMultiType | FeatureNameOneType; +export type FeatureNameType = FeatureNameMultiType | FeatureNameSingleType; export interface StyleType { isChanged: boolean; @@ -33,7 +36,8 @@ export interface FeatureState { } export type ActionPayload = { - feature: string; + feature: FeatureNameType; + subFeature?: string; element: ElementNameType; subElement?: SubElementNameType; style: StyleType; diff --git a/src/store/style/markerReducer.ts b/src/store/style/markerReducer.ts index 95553f0..061e7ad 100644 --- a/src/store/style/markerReducer.ts +++ b/src/store/style/markerReducer.ts @@ -1,6 +1,6 @@ import { getDefaultFeature } from '../common/properties'; -import { FeatureType } from '../common/type'; -import { INIT, SET, ActionType } from '../common/action'; +import { FeatureType, ActionType, SubElementNameType } from '../common/type'; +import { INIT, SET } from '../common/action'; const initialState = getDefaultFeature(); @@ -22,7 +22,7 @@ export default function markerReducer( return newState; } - newState[element][subElement as string] = style; + newState[element][subElement as SubElementNameType] = style; return newState; } default: diff --git a/src/store/style/waterReducer.ts b/src/store/style/waterReducer.ts index 0ae8b94..b0eef3f 100644 --- a/src/store/style/waterReducer.ts +++ b/src/store/style/waterReducer.ts @@ -1,6 +1,6 @@ import { getDefaultFeature } from '../common/properties'; -import { FeatureType } from '../common/type'; -import { INIT, SET, ActionType } from '../common/action'; +import { FeatureType, ActionType, SubElementNameType } from '../common/type'; +import { INIT, SET } from '../common/action'; const initialState = getDefaultFeature(); @@ -22,7 +22,7 @@ export default function waterReducer( return newState; } - newState[element][subElement as string] = style; + newState[element][subElement as SubElementNameType] = style; return newState; } default: From ce64a6ffbf583005cd42e23287c78e87aaaca325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Sat, 28 Nov 2020 15:07:42 +0900 Subject: [PATCH 010/138] =?UTF-8?q?[refactor]=20hook=EC=97=90=EC=84=9C=20f?= =?UTF-8?q?eaturename=EC=B2=98=EB=A6=AC=ED=95=A0=20=EB=95=8C=20util?= =?UTF-8?q?=EC=97=90=20=EC=9E=88=EB=8A=94=20=EB=AA=A8=EB=93=88=EB=A1=9C=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=B2=B4=ED=81=AC=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - hook에서 featureName이 single인지 multi인지 체크할 때 utils에 있는 typecheck함수로 확인함 --- src/hooks/sidebar/useDetailType.ts | 14 +++++--------- src/hooks/sidebar/useStyleType.ts | 9 ++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/hooks/sidebar/useDetailType.ts b/src/hooks/sidebar/useDetailType.ts index 7ba7ecc..8706250 100644 --- a/src/hooks/sidebar/useDetailType.ts +++ b/src/hooks/sidebar/useDetailType.ts @@ -1,11 +1,7 @@ import { useSelector } from 'react-redux'; import { RootState } from '../../store'; -import { - FeatureNameType, - FeatureNameOneType, - FeatureNameMultiType, - FeatureType, -} from '../../store/common/type'; +import { featureNameTypeCheck } from '../../utils/typeCheck'; +import { FeatureNameType, FeatureType } from '../../store/common/type'; interface UseDetailTypeProps { featureName: FeatureNameType; @@ -29,11 +25,11 @@ function useDetailType({ if (!featureName) { return dummyDetail; } - if (featureName === 'water' || featureName === 'marker') { - return state[featureName as FeatureNameOneType]; + if (featureNameTypeCheck(featureName)) { + return state[featureName]; } - return state[featureName as FeatureNameMultiType][subFeatureName]; + return state[featureName][subFeatureName]; }) as FeatureType; return { diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index 8193591..2739f0d 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -1,9 +1,8 @@ import { useSelector } from 'react-redux'; import { RootState } from '../../store'; +import { featureNameTypeCheck } from '../../utils/typeCheck'; import { FeatureNameType, - FeatureNameOneType, - FeatureNameMultiType, StyleType, ElementNameType, SubElementNameType, @@ -32,10 +31,10 @@ function useStyleType({ return dummyStyle; } let feature; - if (featureName === 'water' || featureName === 'marker') { - feature = state[featureName as FeatureNameOneType]; + if (featureNameTypeCheck(featureName)) { + feature = state[featureName]; } else { - feature = state[featureName as FeatureNameMultiType][subFeatureName]; + feature = state[featureName][subFeatureName]; } if (detailName === 'labelIcon') return feature.labelIcon; From 02e57862e3f91d756448e998c96657f2a2b26f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Sat, 28 Nov 2020 17:35:00 +0900 Subject: [PATCH 011/138] =?UTF-8?q?[feat]=20=EC=A7=80=EB=8F=84=EC=9D=98=20?= =?UTF-8?q?=EB=9D=BC=EB=B2=A8=EB=93=A4=20=ED=95=9C=EA=B5=AD=EC=96=B4?= =?UTF-8?q?=EB=A1=9C=20=EB=B2=88=EC=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 지도의 라벨들을 한국어로 번역하였다 --- src/store/map/initializeMap.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/store/map/initializeMap.ts b/src/store/map/initializeMap.ts index 28c3414..71aaa73 100644 --- a/src/store/map/initializeMap.ts +++ b/src/store/map/initializeMap.ts @@ -6,6 +6,20 @@ import dotenv from 'dotenv'; const LNG = 128; const LAT = 36.5; const ZOOM = 7; +const LABELS = [ + 'country-label', + 'settlement-label', + 'road-label', + 'state-label', + 'settlement-subdivision-label', + 'airport-label', + 'transit-label', + 'water-point-label', + 'water-line-label', + 'waterway-label', + 'natural-point-label', + 'natural-line-label', +]; dotenv.config(); mapboxgl.accessToken = process.env.REACT_APP_ACCESS_TOKEN as string; @@ -14,6 +28,12 @@ interface InitializeMapProps { mapRef: RefObject; } +function translate(map: mapboxgl.Map) { + LABELS.forEach((label) => { + map.setLayoutProperty(label, 'text-field', ['get', 'name_ko']); + }); +} + function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { const map = new mapboxgl.Map({ container: mapRef.current as HTMLDivElement, @@ -32,6 +52,10 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { 'bottom-right' ); + map.on('load', () => { + translate(map); + }); + return map; } From bd51316a46f77f62151a47c74a5ff78a295d5845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Sat, 28 Nov 2020 17:36:25 +0900 Subject: [PATCH 012/138] =?UTF-8?q?[fix]=20=EC=9E=90=EC=9E=98=ED=95=9C=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=EB=93=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LightnessStyleWrapper의 이름 변경 (wrapper제거) - properties에서 weight값 변경 - reducer에서 자신에게맞는 action이 왔는지 필터링 하는 부분 수정 --- .../LightnessStyleWrapper.tsx | 40 ------------------- src/store/common/properties.ts | 2 +- src/store/common/reducer.ts | 4 +- 3 files changed, 4 insertions(+), 42 deletions(-) delete mode 100644 src/components/Sidebar/SidebarContentMore/LightnessStyleWrapper.tsx diff --git a/src/components/Sidebar/SidebarContentMore/LightnessStyleWrapper.tsx b/src/components/Sidebar/SidebarContentMore/LightnessStyleWrapper.tsx deleted file mode 100644 index e922c9a..0000000 --- a/src/components/Sidebar/SidebarContentMore/LightnessStyleWrapper.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import styled from '../../../utils/styles/styled'; -import { Range } from '../SidebarContentFewer/DepthItem'; - -const LightnessWrapper = styled.div` - display: flex; - flex-direction: column; - margin: 15px 0; -`; - -const LightnessTitle = styled.label` - margin-bottom: 10px; - font-size: 1.7rem; - font-weight: 600; - color: ${(props) => props.theme.GREY}; -`; - -const LightnessControlBar = styled(Range)` - width: 100%; - height: 2px; -`; - -function LightnessStyleWrapper(): React.ReactElement { - return ( -
- - 밝기 - - -
- ); -} - -export default LightnessStyleWrapper; diff --git a/src/store/common/properties.ts b/src/store/common/properties.ts index a3f2d16..f4956d9 100644 --- a/src/store/common/properties.ts +++ b/src/store/common/properties.ts @@ -4,7 +4,7 @@ export const style: StyleType = { isChanged: false, visibility: 'inherit', color: '#000000', - weight: 50, + weight: 4, saturation: 0, lightness: 0, }; diff --git a/src/store/common/reducer.ts b/src/store/common/reducer.ts index a5a353f..47df899 100644 --- a/src/store/common/reducer.ts +++ b/src/store/common/reducer.ts @@ -34,7 +34,8 @@ export default function getReducer(IDX: number): ReducerType { subElement, style, } = action.payload; - if (feature === renderingData[IDX].typeKey) return state; + + if (feature !== renderingData[IDX].typeKey) return state; const newState: FeatureState = JSON.parse(JSON.stringify(state)); @@ -46,6 +47,7 @@ export default function getReducer(IDX: number): ReducerType { newState[subFeature as string][element][ subElement as SubElementNameType ] = style; + return newState; } default: From a7532ba9d87ee807e7bcff8588027f2e01bfdbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Sat, 28 Nov 2020 17:38:25 +0900 Subject: [PATCH 013/138] =?UTF-8?q?[refactor]=20DetailType=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=99=80=20=ED=9B=85=EC=9D=84=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DetailType에 있는 로직관련 코드를 hook으로 옮기었다. --- .../Sidebar/SidebarContentMore/DetailType.tsx | 33 +++++--------- src/hooks/sidebar/useDetailType.ts | 43 ++++++++++++++++++- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 7cdf04f..b34ae0d 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -24,7 +24,6 @@ const DetailWrapper = styled.div` padding: 20px; overflow-y: scroll; - background-color: ${(props) => props.theme.WHITE}; border-left: 1px solid ${(props) => props.theme.LIGHTGREY}; `; @@ -81,29 +80,17 @@ function DetailType({ const { detail: { section, labelText, labelIcon }, + styleClickHandler, + checkIsSelected, }: UseDetailHookType = useDetailType({ + sidebarTypeClickHandler, + sidebarSubTypeClickHandler, + sidebarTypeName, + sidebarSubTypeName, featureName, subFeatureName, }); - const styleClickHandler = ( - elementName: ElementNameType, - subElementName?: SubElementNameType - ) => { - sidebarTypeClickHandler(elementName); - if (subElementName) sidebarSubTypeClickHandler(subElementName); - }; - - const getIsSelected = ( - elementName: ElementNameType, - subElementName?: SubElementNameType - ) => { - if (!subElementName) return sidebarTypeName === elementName; - return ( - sidebarTypeName === elementName && sidebarSubTypeName === subElementName - ); - }; - if (!featureName) { return <>; } @@ -116,7 +103,7 @@ function DetailType({ 구역 {section?.fill.isChanged ? : <>} styleClickHandler('section', 'fill')} name="채우기" @@ -137,14 +124,14 @@ function DetailType({ 텍스트 {labelText.fill.isChanged ? : <>} styleClickHandler('labelText', 'fill')} name="채우기" /> {labelText.stroke.isChanged ? : <>} styleClickHandler('labelText', 'stroke')} name="윤곽선" @@ -152,7 +139,7 @@ function DetailType({ {labelIcon.isChanged ? : <>} styleClickHandler('labelIcon')} name="아이콘" diff --git a/src/hooks/sidebar/useDetailType.ts b/src/hooks/sidebar/useDetailType.ts index 8706250..99bda37 100644 --- a/src/hooks/sidebar/useDetailType.ts +++ b/src/hooks/sidebar/useDetailType.ts @@ -1,15 +1,32 @@ import { useSelector } from 'react-redux'; import { RootState } from '../../store'; import { featureNameTypeCheck } from '../../utils/typeCheck'; -import { FeatureNameType, FeatureType } from '../../store/common/type'; +import { + FeatureNameType, + ElementNameType, + SubElementNameType, + FeatureType, +} from '../../store/common/type'; interface UseDetailTypeProps { featureName: FeatureNameType; subFeatureName: string; + sidebarTypeName: string; + sidebarSubTypeName: string; + sidebarTypeClickHandler: (name: string) => void; + sidebarSubTypeClickHandler: (name: string) => void; } export interface UseDetailHookType { detail: FeatureType; + styleClickHandler: ( + elementName: ElementNameType, + subElementName?: SubElementNameType + ) => void; + checkIsSelected: ( + elementName: ElementNameType, + subElementName?: SubElementNameType + ) => boolean; } const dummyDetail = { @@ -20,6 +37,10 @@ const dummyDetail = { function useDetailType({ featureName, subFeatureName, + sidebarTypeName, + sidebarSubTypeName, + sidebarTypeClickHandler, + sidebarSubTypeClickHandler, }: UseDetailTypeProps): UseDetailHookType { const detail = useSelector((state) => { if (!featureName) { @@ -32,8 +53,28 @@ function useDetailType({ return state[featureName][subFeatureName]; }) as FeatureType; + const styleClickHandler = ( + elementName: ElementNameType, + subElementName?: SubElementNameType + ) => { + sidebarTypeClickHandler(elementName); + if (subElementName) sidebarSubTypeClickHandler(subElementName); + }; + + const checkIsSelected = ( + elementName: ElementNameType, + subElementName?: SubElementNameType + ) => { + if (!subElementName) return sidebarTypeName === elementName; + return ( + sidebarTypeName === elementName && sidebarSubTypeName === subElementName + ); + }; + return { detail, + styleClickHandler, + checkIsSelected, }; } From 2e60a823d86579839e8614c9d25c5d0bb927de22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Sat, 28 Nov 2020 17:39:55 +0900 Subject: [PATCH 014/138] =?UTF-8?q?[feat]=20Style=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=EC=84=9C=20=ED=95=98=EC=9C=84?= =?UTF-8?q?=EB=A1=9C=20style=EC=9D=84=20prop=EC=9C=BC=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=EB=8B=AC=20/=20hook=EC=97=90=20dispatch=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - style컴포넌트에서 각 하위 컴포넌트들로 필요한 style속성들을 props로 전달하였다 - hook에 dispatch를 이용해서 상태를 변경시키는 로직을 추가하였다 --- .../Sidebar/SidebarContentMore/Styler.tsx | 17 ++++++---- src/hooks/sidebar/useStyleType.ts | 32 ++++++++++++++++--- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/Styler.tsx b/src/components/Sidebar/SidebarContentMore/Styler.tsx index 3104cdf..e6b1391 100644 --- a/src/components/Sidebar/SidebarContentMore/Styler.tsx +++ b/src/components/Sidebar/SidebarContentMore/Styler.tsx @@ -12,7 +12,7 @@ import useStyleType, { import ColorStyle from './ColorStyle'; import WeightStyle from './WeightStyle'; import SaturationStyle from './SaturationStyle'; -import LightnessStyleWrapper from './LightnessStyleWrapper'; +import LightnessStyle from './LightnessStyle'; import VisibilityStyle from './VisibilityStyle'; const StylerWrapper = styled.div` @@ -52,7 +52,10 @@ function Styler({ detailName, subDetailName, }: StylerProps): React.ReactElement { - const { style }: UseStyleHookType = useStyleType({ + const { + styleElement: { visibility, color, weight, saturation, lightness }, + onStyleChange, + }: UseStyleHookType = useStyleType({ featureName, subFeatureName, detailName, @@ -66,13 +69,13 @@ function Styler({ return ( 스타일 - +
- +
- - - + + +
); } diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index 2739f0d..d485785 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -1,4 +1,5 @@ -import { useSelector } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; +import { useCallback } from 'react'; import { RootState } from '../../store'; import { featureNameTypeCheck } from '../../utils/typeCheck'; import { @@ -7,6 +8,7 @@ import { ElementNameType, SubElementNameType, } from '../../store/common/type'; +import { setStyle } from '../../store/common/action'; import { style as dummyStyle } from '../../store/common/properties'; interface UseStyleTypeProps { @@ -17,7 +19,8 @@ interface UseStyleTypeProps { } export interface UseStyleHookType { - style: StyleType; + styleElement: StyleType; + onStyleChange: (key: string, value: string | number) => void; } function useStyleType({ @@ -26,7 +29,9 @@ function useStyleType({ detailName, subDetailName, }: UseStyleTypeProps): UseStyleHookType { - const style = useSelector((state) => { + const dispatch = useDispatch(); + + const styleElement = useSelector((state) => { if (!detailName) { return dummyStyle; } @@ -41,8 +46,27 @@ function useStyleType({ return feature[detailName][subDetailName as SubElementNameType]; }) as StyleType; + const onStyleChange = useCallback( + (key: string, value: string | number) => { + dispatch( + setStyle({ + feature: featureName, + subFeature: subFeatureName, + element: detailName, + subElement: subDetailName, + style: { + ...styleElement, + [key]: value, + }, + }) + ); + }, + [featureName, subFeatureName, detailName, subDetailName, styleElement] + ); + return { - style, + styleElement, + onStyleChange, }; } From bd686326fd3563bed074bcdc55a9b4a5a5814e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Sat, 28 Nov 2020 17:41:29 +0900 Subject: [PATCH 015/138] =?UTF-8?q?[feat]=20=EA=B0=81=20Style=EC=84=B8?= =?UTF-8?q?=EB=B6=80=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=93=A4?= =?UTF-8?q?=EC=97=90=EA=B2=8C=20=EC=83=81=EC=9C=84=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=80=20props=EB=A1=9C=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=99=80=20=EC=83=81=ED=83=9C=EB=B3=80=EA=B2=BD=EC=9D=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각 Style세부 컴포넌트들이 상위에서 props로 store의 상태와 상태를 변경시키기 위한 함수를 받음 - Visibility컴포넌트를 제외하고는 useInputRange hook을 이용해서 상태를 변경시킴 - Visibility컴포넌트는 onClick으로 동작하기 때문에 바로 상태를 변경시키는 로직을 붙여줌 - useInputRange는 기존 input range/color변경 마다 dispatch발생을 방지하기 위해서 mouseUp/blur 와 onchange이벤트를 이용해서 사용자의 동작이 끝났을 때 dispatch를 보내도록 해준다 --- .../Sidebar/SidebarContentMore/ColorStyle.tsx | 42 ++++++++++--- .../SidebarContentMore/LightnessStyle.tsx | 57 ++++++++++++++++++ .../SidebarContentMore/SaturationStyle.tsx | 19 +++++- .../SidebarContentMore/VisibilityStyle.tsx | 59 +++++++++++++++++-- .../SidebarContentMore/WeightStyle.tsx | 19 +++++- src/hooks/common/useInputRange.ts | 39 ++++++++++++ 6 files changed, 220 insertions(+), 15 deletions(-) create mode 100644 src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx create mode 100644 src/hooks/common/useInputRange.ts diff --git a/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx b/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx index 66aae9b..c791208 100644 --- a/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx @@ -1,29 +1,57 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; +import useInputRange from '../../../hooks/common/useInputRange'; -const ColorWrapper = styled.div``; +const ColorWrapper = styled.div` + display: flex; + flex-wrap: wrap; +`; const ColorTitle = styled.label` - margin-bottom: 10px; + margin: auto 0; font-size: 1.7rem; font-weight: 600; color: ${(props) => props.theme.GREY}; `; -const ColorCode = styled.div``; +const ColorCode = styled.div` + margin: auto 0 auto 5px; + font-size: 1.3rem; + font-weight: 600; + color: ${(props) => props.theme.LIGHTGREY}; +`; const ColorPalette = styled.input` - margin: 10px 20px; + margin: 20px 20px 10px 20px; width: 100px; height: 40px; `; -function ColorStyle(): React.ReactElement { +interface ColorStyleProps { + color: string; + onStyleChange: (key: string, value: string | number) => void; +} + +function ColorStyle({ + color, + onStyleChange, +}: ColorStyleProps): React.ReactElement { + const { curRange, rangeChangeHandler, rangeMouseUpHandler } = useInputRange({ + range: color, + onStyleChange, + }); + return ( 색상 - - + {curRange} + rangeMouseUpHandler('color')} + value={curRange} + /> ); } diff --git a/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx b/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx new file mode 100644 index 0000000..5e23985 --- /dev/null +++ b/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import styled from '../../../utils/styles/styled'; +import { Range } from '../SidebarContentFewer/DepthItem'; +import useInputRange from '../../../hooks/common/useInputRange'; + +const LightnessWrapper = styled.div` + display: flex; + flex-direction: column; + margin: 15px 0; +`; + +const LightnessTitle = styled.label` + margin-bottom: 10px; + font-size: 1.7rem; + font-weight: 600; + color: ${(props) => props.theme.GREY}; +`; + +const LightnessControlBar = styled(Range)` + width: 100%; + height: 2px; +`; + +interface LightnessPropsInterface { + lightness: number; + onStyleChange: (key: string, value: string | number) => void; +} + +function LightnessStyle({ + lightness, + onStyleChange, +}: LightnessPropsInterface): React.ReactElement { + const { curRange, rangeChangeHandler, rangeMouseUpHandler } = useInputRange({ + range: lightness, + onStyleChange, + }); + + return ( +
+ + 밝기 + rangeChangeHandler(e)} + onMouseUp={() => rangeMouseUpHandler('lightness')} + /> + +
+ ); +} + +export default LightnessStyle; diff --git a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx index c3e389b..f4f541a 100644 --- a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx @@ -1,6 +1,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; +import useInputRange from '../../../hooks/common/useInputRange'; const SaturationWrapper = styled.div` display: flex; @@ -20,7 +21,20 @@ const SaturationControlBar = styled(Range)` height: 2px; `; -function SaturationStyle(): React.ReactElement { +interface SaturationStyleProps { + saturation: number; + onStyleChange: (key: string, value: string | number) => void; +} + +function SaturationStyle({ + saturation, + onStyleChange, +}: SaturationStyleProps): React.ReactElement { + const { curRange, rangeChangeHandler, rangeMouseUpHandler } = useInputRange({ + range: saturation, + onStyleChange, + }); + return ( 채도 @@ -30,6 +44,9 @@ function SaturationStyle(): React.ReactElement { max="100" step="5" id="styler__saturation" + value={curRange} + onChange={(e) => rangeChangeHandler(e)} + onMouseUp={() => rangeMouseUpHandler('saturation')} /> ); diff --git a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx index 760f4cc..0a99cbf 100644 --- a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx @@ -1,6 +1,10 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; +interface CheckedProp { + checked: boolean; +} + const VisibilityWrapper = styled.div` display: flex; flex-direction: column; @@ -13,19 +17,62 @@ const VisibilityTitle = styled.h2` margin: 10px 0; `; -const VisilityItem = styled.div` - font-size: 1.6rem; +const VisibilityItem = styled.div` + display: flex; + align-items: center; + font-size: 1.5rem; margin: 5px 10px; cursor: pointer; `; -function VisibilityStyle(): React.ReactElement { +const Checkbox = styled.div` + display: flex; + justify-content: center; + align-items: center; + margin-right: 10px; + width: 18px; + height: 18px; + border-radius: 9px; + background-color: ${(props) => + props.checked ? props.theme.GREEN : 'lightgray'}; +`; + +const Circle = styled.div` + width: 10px; + height: 10px; + border-radius: 5px; + background-color: ${(props) => (props.checked ? 'white' : 'lightgray')}; +`; + +interface VisibilityStyleProps { + visibility: string; + onStyleChange: (key: string, value: string | number) => void; +} + +function VisibilityStyle({ + visibility, + onStyleChange, +}: VisibilityStyleProps): React.ReactElement { + const list = [ + { title: '상위요소 상속', value: 'inherit' }, + { title: '보임', value: 'visibility' }, + { title: '숨김', value: 'invisibility' }, + ]; + return ( 가시성 - 상위요소 상속 - 보임 - 숨김 + {list.map((item) => ( + onStyleChange('visibility', item.value)} + > + + + + {item.title} + + ))} ); } diff --git a/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx b/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx index 3e97412..dc66cf8 100644 --- a/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx @@ -1,6 +1,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; +import useInputRange from '../../../hooks/common/useInputRange'; const WeightWrapper = styled.div` display: flex; @@ -20,7 +21,20 @@ const WeightControlBar = styled(Range)` height: 2px; `; -function WeightStyle(): React.ReactElement { +interface WeightStyleProps { + weight: number; + onStyleChange: (key: string, value: string | number) => void; +} + +function WeightStyle({ + weight, + onStyleChange, +}: WeightStyleProps): React.ReactElement { + const { curRange, rangeChangeHandler, rangeMouseUpHandler } = useInputRange({ + range: weight, + onStyleChange, + }); + return ( 굵기 @@ -30,6 +44,9 @@ function WeightStyle(): React.ReactElement { max="8" step="0.5" id="styler__weight" + value={curRange} + onChange={rangeChangeHandler} + onMouseUp={() => rangeMouseUpHandler('weight')} /> ); diff --git a/src/hooks/common/useInputRange.ts b/src/hooks/common/useInputRange.ts new file mode 100644 index 0000000..d82a72c --- /dev/null +++ b/src/hooks/common/useInputRange.ts @@ -0,0 +1,39 @@ +import React, { useState, useEffect } from 'react'; + +interface UseInputRangeProps { + range: string | number; + onStyleChange: (key: string, value: string | number) => void; +} + +interface InputRangeHookType { + curRange: string | number; + rangeChangeHandler: (e: React.ChangeEvent) => void; + rangeMouseUpHandler: (key: string) => void; +} + +function useInputRange({ + range, + onStyleChange, +}: UseInputRangeProps): InputRangeHookType { + const [curRange, setCurRange] = useState(range); + + useEffect(() => { + setCurRange(range); + }, [range]); + + const rangeChangeHandler = (e: React.ChangeEvent) => { + setCurRange(e.target.value); + }; + + const rangeMouseUpHandler = (key: string) => { + onStyleChange(key, curRange); + }; + + return { + curRange, + rangeChangeHandler, + rangeMouseUpHandler, + }; +} + +export default useInputRange; From 5e06ccea3ae68a8feef833dd5200cf7aa16b8221 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 14:54:34 +0900 Subject: [PATCH 016/138] =?UTF-8?q?[refactor]=20=EC=9D=BC=EB=B6=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 객체 직접 참조하지 않고 함수를 이용하도록 하였음 - 불필요한 내용 삭제 --- src/hooks/sidebar/useStyleType.ts | 4 ++-- src/store/common/properties.ts | 4 ++-- src/store/index.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index d485785..aa108ec 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -9,7 +9,7 @@ import { SubElementNameType, } from '../../store/common/type'; import { setStyle } from '../../store/common/action'; -import { style as dummyStyle } from '../../store/common/properties'; +import { getDefaultStyle } from '../../store/common/properties'; interface UseStyleTypeProps { featureName: FeatureNameType; @@ -33,7 +33,7 @@ function useStyleType({ const styleElement = useSelector((state) => { if (!detailName) { - return dummyStyle; + return getDefaultStyle(); } let feature; if (featureNameTypeCheck(featureName)) { diff --git a/src/store/common/properties.ts b/src/store/common/properties.ts index f4956d9..af69f33 100644 --- a/src/store/common/properties.ts +++ b/src/store/common/properties.ts @@ -1,6 +1,6 @@ import { StyleType, ElementType, FeatureType } from './type'; -export const style: StyleType = { +const style: StyleType = { isChanged: false, visibility: 'inherit', color: '#000000', @@ -9,7 +9,7 @@ export const style: StyleType = { lightness: 0, }; -const getDefaultStyle = (): StyleType => { +export const getDefaultStyle = (): StyleType => { return JSON.parse(JSON.stringify(style)); }; diff --git a/src/store/index.ts b/src/store/index.ts index 58142cc..5f90824 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,5 +1,5 @@ import { combineReducers, createStore } from 'redux'; -import { composeWithDevTools } from 'redux-devtools-extension'; + import map from './map/reducer'; import poi from './style/poiReducer'; import transit from './style/transitReducer'; @@ -20,7 +20,7 @@ const rootReducer = combineReducers({ marker, }); -const store = createStore(rootReducer, composeWithDevTools()); +const store = createStore(rootReducer); export type RootState = ReturnType; From f0fd854d3f302893446fb207955032ea59bd8ffb Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 14:57:03 +0900 Subject: [PATCH 017/138] =?UTF-8?q?[feat]=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - isChanged 관련 공통 함수 구현 - 기능 유형, 스타일 변화 상태 reducer에서 체크하도록 하였음 - 위 함수에 요구되는 공통 타입 분리 --- src/store/common/compareStyle.ts | 27 +++++++++++++++++++++++++++ src/store/common/reducer.ts | 28 ++++++++++++++++++++++------ src/store/common/type.ts | 12 ++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 src/store/common/compareStyle.ts diff --git a/src/store/common/compareStyle.ts b/src/store/common/compareStyle.ts new file mode 100644 index 0000000..12748ff --- /dev/null +++ b/src/store/common/compareStyle.ts @@ -0,0 +1,27 @@ +import { getDefaultStyle } from './properties'; +import { StyleType, StyleKeyType, objType } from './type'; + +export function checkStyleIsChanged(targetStyle: StyleType): boolean { + const defaultStyle: StyleType = getDefaultStyle(); + const keys = Object.keys(defaultStyle) as StyleKeyType[]; + + const filteredKeys = keys.filter( + (key) => key === 'isChanged' || defaultStyle[key] === targetStyle[key] + ); + return keys.length !== filteredKeys.length; +} + +export function checkFeatureIsChanged(targetFeature: objType): boolean { + const keys = Object.keys(targetFeature); + for (let i = 0; i < keys.length; i += 1) { + if ( + typeof targetFeature[keys[i]] === 'object' && + checkFeatureIsChanged(targetFeature[keys[i]]) === true + ) { + return true; + } + + if (Object.values(targetFeature).includes(true)) return true; + } + return false; +} diff --git a/src/store/common/reducer.ts b/src/store/common/reducer.ts index 47df899..f6abe48 100644 --- a/src/store/common/reducer.ts +++ b/src/store/common/reducer.ts @@ -1,8 +1,15 @@ import renderingData from '../../utils/rendering-data/featureTypeData'; -import { FeatureState, ActionType, SubElementNameType } from './type'; +import { + FeatureState, + FeatureType, + ActionType, + SubElementNameType, + objType, +} from './type'; import { getDefaultFeature } from './properties'; import { INIT, SET } from './action'; +import { checkStyleIsChanged, checkFeatureIsChanged } from './compareStyle'; interface ReducerType { (state: FeatureState, action: ActionType): FeatureState; @@ -37,16 +44,25 @@ export default function getReducer(IDX: number): ReducerType { if (feature !== renderingData[IDX].typeKey) return state; + style.isChanged = checkStyleIsChanged(style); const newState: FeatureState = JSON.parse(JSON.stringify(state)); + const newFeature: FeatureType = newState[subFeature as string]; + let prevIsChanged; if (element === 'labelIcon') { - newState[subFeature as string][element] = style; - return newState; + prevIsChanged = newFeature[element].isChanged; + newFeature[element] = style; + } else { + prevIsChanged = + newFeature[element][subElement as SubElementNameType].isChanged; + newFeature[element][subElement as SubElementNameType] = style; } - newState[subFeature as string][element][ - subElement as SubElementNameType - ] = style; + if (prevIsChanged !== style.isChanged) { + delete (newFeature as objType).isChanged; + const featureIsChanged = checkFeatureIsChanged(newFeature); + newFeature.isChanged = featureIsChanged; + } return newState; } diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 30a4ab6..2367bbf 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -13,6 +13,18 @@ export type FeatureNameMultiType = | 'transit'; export type FeatureNameType = FeatureNameMultiType | FeatureNameSingleType; +export type StyleKeyType = + | 'visibility' + | 'color' + | 'weight' + | 'saturation' + | 'lightness' + | 'isChanged'; + +export interface objType { + [name: string]: any; +} + export interface StyleType { isChanged: boolean; visibility: string; From 7a5a02175e6acbf02dd518733b83f17a32e1622f Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 15:20:41 +0900 Subject: [PATCH 018/138] =?UTF-8?q?[refactor]=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=9C=A0=ED=98=95=20=EA=B5=AC=EC=A1=B0=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - View에서 marker, all을 예외처리 복잡도가 증가됨 - 이를 해결하기 위해서 marker, water도 다른 기능 유형과 구조 통일시키고, 관련 타입을 사용하던 함수를 수정하였음 --- .../Sidebar/SidebarContentMore/DetailType.tsx | 6 +++--- src/hooks/sidebar/useDetailType.ts | 4 ---- src/hooks/sidebar/useStyleType.ts | 10 ++-------- src/store/common/type.ts | 8 ++++---- src/utils/typeCheck.ts | 8 -------- 5 files changed, 9 insertions(+), 27 deletions(-) delete mode 100644 src/utils/typeCheck.ts diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index b34ae0d..1d8cc67 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -122,14 +122,14 @@ function DetailType({ 라벨 텍스트 - {labelText.fill.isChanged ? : <>} + {labelText?.fill.isChanged ? : <>} styleClickHandler('labelText', 'fill')} name="채우기" /> - {labelText.stroke.isChanged ? : <>} + {labelText?.stroke.isChanged ? : <>} - {labelIcon.isChanged ? : <>} + {labelIcon?.isChanged ? : <>} | ReturnType; export type ElementNameType = 'section' | 'labelText' | 'labelIcon'; export type SubElementNameType = 'fill' | 'stroke'; -export type FeatureNameSingleType = 'water' | 'marker'; -export type FeatureNameMultiType = +export type FeatureNameType = | 'poi' | 'administrative' | 'landscape' | 'road' - | 'transit'; -export type FeatureNameType = FeatureNameMultiType | FeatureNameSingleType; + | 'transit' + | 'water' + | 'marker'; export type StyleKeyType = | 'visibility' diff --git a/src/utils/typeCheck.ts b/src/utils/typeCheck.ts deleted file mode 100644 index 469e681..0000000 --- a/src/utils/typeCheck.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { FeatureNameType, FeatureNameSingleType } from '../store/common/type'; - -export function featureNameTypeCheck( - name: FeatureNameType -): name is FeatureNameSingleType { - if (name === 'water' || name === 'marker') return true; - return false; -} From 2fe145c817b9544d570517b788efc717dd39795f Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 15:21:35 +0900 Subject: [PATCH 019/138] =?UTF-8?q?[refactor]=20=EB=A7=88=EC=BB=A4,=20?= =?UTF-8?q?=EB=AC=BC=20=EA=B3=B5=ED=86=B5=20=EB=A6=AC=EB=93=80=EC=84=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기능 유형 구조 동일하게 변화됨에 따라 동일한 리듀서를 사용할 수 있게 되었음 --- src/store/style/markerReducer.ts | 33 ++++---------------------------- src/store/style/waterReducer.ts | 33 ++++---------------------------- 2 files changed, 8 insertions(+), 58 deletions(-) diff --git a/src/store/style/markerReducer.ts b/src/store/style/markerReducer.ts index 061e7ad..df4afda 100644 --- a/src/store/style/markerReducer.ts +++ b/src/store/style/markerReducer.ts @@ -1,31 +1,6 @@ -import { getDefaultFeature } from '../common/properties'; -import { FeatureType, ActionType, SubElementNameType } from '../common/type'; -import { INIT, SET } from '../common/action'; +import getReducer from '../common/reducer'; -const initialState = getDefaultFeature(); +const MARKER_IDX = 6; +const markerReducer = getReducer(MARKER_IDX); -export default function markerReducer( - state: FeatureType = initialState, - action: ActionType -): FeatureType { - switch (action.type) { - case INIT: - return initialState; - case SET: { - const { feature, element, subElement, style } = action.payload; - if (feature !== 'marker') return state; - - const newState = JSON.parse(JSON.stringify(state)); - - if (element === 'labelIcon') { - newState[element] = style; - return newState; - } - - newState[element][subElement as SubElementNameType] = style; - return newState; - } - default: - return state; - } -} +export default markerReducer; diff --git a/src/store/style/waterReducer.ts b/src/store/style/waterReducer.ts index b0eef3f..527d57b 100644 --- a/src/store/style/waterReducer.ts +++ b/src/store/style/waterReducer.ts @@ -1,31 +1,6 @@ -import { getDefaultFeature } from '../common/properties'; -import { FeatureType, ActionType, SubElementNameType } from '../common/type'; -import { INIT, SET } from '../common/action'; +import getReducer from '../common/reducer'; -const initialState = getDefaultFeature(); +const WATER_IDX = 5; +const waterReducer = getReducer(WATER_IDX); -export default function waterReducer( - state: FeatureType = initialState, - action: ActionType -): FeatureType { - switch (action.type) { - case INIT: - return initialState; - case SET: { - const { feature, element, subElement, style } = action.payload; - if (feature !== 'water') return state; - - const newState = JSON.parse(JSON.stringify(state)); - - if (element === 'labelIcon') { - newState[element] = style; - return newState; - } - - newState[element][subElement as SubElementNameType] = style; - return newState; - } - default: - return state; - } -} +export default waterReducer; From c2bbc66b821744992224e2a040412e7895ec32d2 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 15:42:57 +0900 Subject: [PATCH 020/138] =?UTF-8?q?[refactor]=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=83=81=ED=83=9C=20=EB=A6=AC=EB=93=80=EC=84=9C=20?= =?UTF-8?q?=ED=95=98=EB=82=98=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/common/reducer.ts | 73 ------------------------ src/store/style/administrativeReducer.ts | 6 -- src/store/style/landscapeReducer.ts | 6 -- src/store/style/markerReducer.ts | 6 -- src/store/style/poiReducer.ts | 6 -- src/store/style/reducer.ts | 25 ++++++++ src/store/style/roadReducer.ts | 6 -- src/store/style/transitReducer.ts | 6 -- src/store/style/waterReducer.ts | 6 -- 9 files changed, 25 insertions(+), 115 deletions(-) delete mode 100644 src/store/common/reducer.ts delete mode 100644 src/store/style/administrativeReducer.ts delete mode 100644 src/store/style/landscapeReducer.ts delete mode 100644 src/store/style/markerReducer.ts delete mode 100644 src/store/style/poiReducer.ts create mode 100644 src/store/style/reducer.ts delete mode 100644 src/store/style/roadReducer.ts delete mode 100644 src/store/style/transitReducer.ts delete mode 100644 src/store/style/waterReducer.ts diff --git a/src/store/common/reducer.ts b/src/store/common/reducer.ts deleted file mode 100644 index f6abe48..0000000 --- a/src/store/common/reducer.ts +++ /dev/null @@ -1,73 +0,0 @@ -import renderingData from '../../utils/rendering-data/featureTypeData'; - -import { - FeatureState, - FeatureType, - ActionType, - SubElementNameType, - objType, -} from './type'; -import { getDefaultFeature } from './properties'; -import { INIT, SET } from './action'; -import { checkStyleIsChanged, checkFeatureIsChanged } from './compareStyle'; - -interface ReducerType { - (state: FeatureState, action: ActionType): FeatureState; -} - -export default function getReducer(IDX: number): ReducerType { - const subFeatures = [ - 'all', - ...(renderingData[IDX].features?.map((v) => v.key) as string[]), - ]; - - const initialState = subFeatures.reduce((acc: FeatureState, cur: string) => { - acc[cur] = getDefaultFeature(); - return acc; - }, {}); - - return function reducer( - state: FeatureState = initialState, - action: ActionType - ): FeatureState { - switch (action.type) { - case INIT: - return initialState; - case SET: { - const { - feature, - subFeature, - element, - subElement, - style, - } = action.payload; - - if (feature !== renderingData[IDX].typeKey) return state; - - style.isChanged = checkStyleIsChanged(style); - const newState: FeatureState = JSON.parse(JSON.stringify(state)); - const newFeature: FeatureType = newState[subFeature as string]; - - let prevIsChanged; - if (element === 'labelIcon') { - prevIsChanged = newFeature[element].isChanged; - newFeature[element] = style; - } else { - prevIsChanged = - newFeature[element][subElement as SubElementNameType].isChanged; - newFeature[element][subElement as SubElementNameType] = style; - } - - if (prevIsChanged !== style.isChanged) { - delete (newFeature as objType).isChanged; - const featureIsChanged = checkFeatureIsChanged(newFeature); - newFeature.isChanged = featureIsChanged; - } - - return newState; - } - default: - return state; - } - }; -} diff --git a/src/store/style/administrativeReducer.ts b/src/store/style/administrativeReducer.ts deleted file mode 100644 index 9062941..0000000 --- a/src/store/style/administrativeReducer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import getReducer from '../common/reducer'; - -const ADMINISTRATIVE_IDX = 2; -const administrativeReducer = getReducer(ADMINISTRATIVE_IDX); - -export default administrativeReducer; diff --git a/src/store/style/landscapeReducer.ts b/src/store/style/landscapeReducer.ts deleted file mode 100644 index c2b3d99..0000000 --- a/src/store/style/landscapeReducer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import getReducer from '../common/reducer'; - -const LANDSCAPE_IDX = 3; -const landscapeReducer = getReducer(LANDSCAPE_IDX); - -export default landscapeReducer; diff --git a/src/store/style/markerReducer.ts b/src/store/style/markerReducer.ts deleted file mode 100644 index df4afda..0000000 --- a/src/store/style/markerReducer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import getReducer from '../common/reducer'; - -const MARKER_IDX = 6; -const markerReducer = getReducer(MARKER_IDX); - -export default markerReducer; diff --git a/src/store/style/poiReducer.ts b/src/store/style/poiReducer.ts deleted file mode 100644 index df61e68..0000000 --- a/src/store/style/poiReducer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import getReducer from '../common/reducer'; - -const POI_IDX = 0; -const poiReducer = getReducer(POI_IDX); - -export default poiReducer; diff --git a/src/store/style/reducer.ts b/src/store/style/reducer.ts new file mode 100644 index 0000000..4305bd1 --- /dev/null +++ b/src/store/style/reducer.ts @@ -0,0 +1,25 @@ +import getReducer from './getReducer'; + +const INDEX = { + POI: 0, + ROAD: 1, + ADMINISTRATIVE: 2, + LANDSCAPE: 3, + TRANSIT: 4, + WATER: 5, + MARKER: 6, +}; + +export const poiReducer = getReducer(INDEX.POI); + +export const roadReducer = getReducer(INDEX.ROAD); + +export const administrativeReducer = getReducer(INDEX.ADMINISTRATIVE); + +export const landscapeReducer = getReducer(INDEX.LANDSCAPE); + +export const transitReducer = getReducer(INDEX.TRANSIT); + +export const waterReducer = getReducer(INDEX.WATER); + +export const markerReducer = getReducer(INDEX.MARKER); diff --git a/src/store/style/roadReducer.ts b/src/store/style/roadReducer.ts deleted file mode 100644 index c8813ac..0000000 --- a/src/store/style/roadReducer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import getReducer from '../common/reducer'; - -const ROAD_IDX = 1; -const roadReducer = getReducer(ROAD_IDX); - -export default roadReducer; diff --git a/src/store/style/transitReducer.ts b/src/store/style/transitReducer.ts deleted file mode 100644 index 961500d..0000000 --- a/src/store/style/transitReducer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import getReducer from '../common/reducer'; - -const TRANSIT_IDX = 4; -const transitReducer = getReducer(TRANSIT_IDX); - -export default transitReducer; diff --git a/src/store/style/waterReducer.ts b/src/store/style/waterReducer.ts deleted file mode 100644 index 527d57b..0000000 --- a/src/store/style/waterReducer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import getReducer from '../common/reducer'; - -const WATER_IDX = 5; -const waterReducer = getReducer(WATER_IDX); - -export default waterReducer; From d95450788861a136a6a25e1774395ec16f152e1f Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 15:45:03 +0900 Subject: [PATCH 021/138] =?UTF-8?q?[chore]=20style=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=95=A8=EC=88=98=20store/common=EC=97=90?= =?UTF-8?q?=EC=84=9C=20store/style=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - common이 style 상태에만 해당되어서, store/map과 일관되도록 수정하였음. --- src/hooks/sidebar/useStyleType.ts | 4 +- src/store/common/type.ts | 2 +- src/store/index.ts | 16 +++-- src/store/{common => style}/action.ts | 2 +- src/store/{common => style}/compareStyle.ts | 2 +- src/store/style/getReducer.ts | 73 +++++++++++++++++++++ src/store/{common => style}/properties.ts | 2 +- 7 files changed, 88 insertions(+), 13 deletions(-) rename src/store/{common => style}/action.ts (89%) rename src/store/{common => style}/compareStyle.ts (92%) create mode 100644 src/store/style/getReducer.ts rename src/store/{common => style}/properties.ts (89%) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index c0491dc..b6bf5d7 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -7,8 +7,8 @@ import { ElementNameType, SubElementNameType, } from '../../store/common/type'; -import { setStyle } from '../../store/common/action'; -import { getDefaultStyle } from '../../store/common/properties'; +import { setStyle } from '../../store/style/action'; +import { getDefaultStyle } from '../../store/style/properties'; interface UseStyleTypeProps { featureName: FeatureNameType; diff --git a/src/store/common/type.ts b/src/store/common/type.ts index a7296d0..3e65f44 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,4 +1,4 @@ -import { init, setStyle } from './action'; +import { init, setStyle } from '../style/action'; export type ActionType = ReturnType | ReturnType; export type ElementNameType = 'section' | 'labelText' | 'labelIcon'; diff --git a/src/store/index.ts b/src/store/index.ts index 5f90824..439bf20 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,13 +1,15 @@ import { combineReducers, createStore } from 'redux'; import map from './map/reducer'; -import poi from './style/poiReducer'; -import transit from './style/transitReducer'; -import water from './style/waterReducer'; -import marker from './style/markerReducer'; -import road from './style/roadReducer'; -import landscape from './style/landscapeReducer'; -import administrative from './style/administrativeReducer'; +import { + poiReducer as poi, + transitReducer as transit, + landscapeReducer as landscape, + administrativeReducer as administrative, + roadReducer as road, + waterReducer as water, + markerReducer as marker, +} from './style/reducer'; const rootReducer = combineReducers({ map, diff --git a/src/store/common/action.ts b/src/store/style/action.ts similarity index 89% rename from src/store/common/action.ts rename to src/store/style/action.ts index 3ba56c9..441ec31 100644 --- a/src/store/common/action.ts +++ b/src/store/style/action.ts @@ -1,4 +1,4 @@ -import { ActionPayload } from './type'; +import { ActionPayload } from '../common/type'; export const INIT = 'INIT' as const; export const SET = 'SET' as const; diff --git a/src/store/common/compareStyle.ts b/src/store/style/compareStyle.ts similarity index 92% rename from src/store/common/compareStyle.ts rename to src/store/style/compareStyle.ts index 12748ff..1fee4fa 100644 --- a/src/store/common/compareStyle.ts +++ b/src/store/style/compareStyle.ts @@ -1,5 +1,5 @@ import { getDefaultStyle } from './properties'; -import { StyleType, StyleKeyType, objType } from './type'; +import { StyleType, StyleKeyType, objType } from '../common/type'; export function checkStyleIsChanged(targetStyle: StyleType): boolean { const defaultStyle: StyleType = getDefaultStyle(); diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts new file mode 100644 index 0000000..7fcde3e --- /dev/null +++ b/src/store/style/getReducer.ts @@ -0,0 +1,73 @@ +import renderingData from '../../utils/rendering-data/featureTypeData'; + +import { + FeatureState, + FeatureType, + ActionType, + SubElementNameType, + objType, +} from '../common/type'; +import { getDefaultFeature } from './properties'; +import { INIT, SET } from './action'; +import { checkStyleIsChanged, checkFeatureIsChanged } from './compareStyle'; + +interface ReducerType { + (state: FeatureState, action: ActionType): FeatureState; +} + +export default function getReducer(IDX: number): ReducerType { + const subFeatures = [ + 'all', + ...(renderingData[IDX].features?.map((v) => v.key) as string[]), + ]; + + const initialState = subFeatures.reduce((acc: FeatureState, cur: string) => { + acc[cur] = getDefaultFeature(); + return acc; + }, {}); + + return function reducer( + state: FeatureState = initialState, + action: ActionType + ): FeatureState { + switch (action.type) { + case INIT: + return initialState; + case SET: { + const { + feature, + subFeature, + element, + subElement, + style, + } = action.payload; + + if (feature !== renderingData[IDX].typeKey) return state; + + style.isChanged = checkStyleIsChanged(style); + const newState: FeatureState = JSON.parse(JSON.stringify(state)); + const newFeature: FeatureType = newState[subFeature as string]; + + let prevIsChanged; + if (element === 'labelIcon') { + prevIsChanged = newFeature[element].isChanged; + newFeature[element] = style; + } else { + prevIsChanged = + newFeature[element][subElement as SubElementNameType].isChanged; + newFeature[element][subElement as SubElementNameType] = style; + } + + if (prevIsChanged !== style.isChanged) { + delete (newFeature as objType).isChanged; + const featureIsChanged = checkFeatureIsChanged(newFeature); + newFeature.isChanged = featureIsChanged; + } + + return newState; + } + default: + return state; + } + }; +} diff --git a/src/store/common/properties.ts b/src/store/style/properties.ts similarity index 89% rename from src/store/common/properties.ts rename to src/store/style/properties.ts index af69f33..1452926 100644 --- a/src/store/common/properties.ts +++ b/src/store/style/properties.ts @@ -1,4 +1,4 @@ -import { StyleType, ElementType, FeatureType } from './type'; +import { StyleType, ElementType, FeatureType } from '../common/type'; const style: StyleType = { isChanged: false, From dd8066f7812427b371f9f40fdf919b9c39551efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Mon, 30 Nov 2020 18:50:29 +0900 Subject: [PATCH 022/138] =?UTF-8?q?[fix]=20visibility=EC=9D=98=20value?= =?UTF-8?q?=EA=B0=92=EB=93=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 좀 더 편하게 처리를 하기 위해서 value값들을 알맞게 수정 --- src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx | 4 ++-- src/store/style/properties.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx index 0a99cbf..3ae1adb 100644 --- a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx @@ -55,8 +55,8 @@ function VisibilityStyle({ }: VisibilityStyleProps): React.ReactElement { const list = [ { title: '상위요소 상속', value: 'inherit' }, - { title: '보임', value: 'visibility' }, - { title: '숨김', value: 'invisibility' }, + { title: '보임', value: 'visible' }, + { title: '숨김', value: 'none' }, ]; return ( diff --git a/src/store/style/properties.ts b/src/store/style/properties.ts index 1452926..83b0051 100644 --- a/src/store/style/properties.ts +++ b/src/store/style/properties.ts @@ -3,8 +3,8 @@ import { StyleType, ElementType, FeatureType } from '../common/type'; const style: StyleType = { isChanged: false, visibility: 'inherit', - color: '#000000', - weight: 4, + color: '#55bf40', + weight: 0, saturation: 0, lightness: 0, }; From c0e4e53d77e6c912716c2a09a7756a262a069585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Mon, 30 Nov 2020 18:51:24 +0900 Subject: [PATCH 023/138] =?UTF-8?q?[fix]=20=EC=9D=B4=EC=A0=84,=20=ED=98=84?= =?UTF-8?q?=EC=9E=AC=20=EC=83=81=ED=83=9C=20=EB=B9=84=EA=B5=90=ED=95=A0=20?= =?UTF-8?q?=EB=95=8C=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이전과 현재 스타일의 상태를 비교할 때 자료형이 맞지않아 원하는대로 작동하지 않음 - string으로 처리를 해주어서 비교함 --- src/store/style/compareStyle.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/store/style/compareStyle.ts b/src/store/style/compareStyle.ts index 1fee4fa..b1587cc 100644 --- a/src/store/style/compareStyle.ts +++ b/src/store/style/compareStyle.ts @@ -6,7 +6,9 @@ export function checkStyleIsChanged(targetStyle: StyleType): boolean { const keys = Object.keys(defaultStyle) as StyleKeyType[]; const filteredKeys = keys.filter( - (key) => key === 'isChanged' || defaultStyle[key] === targetStyle[key] + (key) => + key === 'isChanged' || + String(defaultStyle[key]) === String(targetStyle[key]) ); return keys.length !== filteredKeys.length; } From f983e7d7902cd10c7acec2889b47660df6c082b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Mon, 30 Nov 2020 18:52:28 +0900 Subject: [PATCH 024/138] =?UTF-8?q?[feat]=20=EC=A7=80=EB=8F=84=EC=97=90=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=EC=9D=84=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=EC=9D=84=20=EB=A7=8C=EB=93=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 지도에 각각의 스타일들을 적용할 수 있게 여러 함수들을 모듈로 만듦 - 각각의 타입(symbol line polygon)마다 서로 다른 값들을 인자로 주어야 함 --- src/utils/applyStyle.ts | 62 ++++++++++++++++++++++++++++++++++++++++ src/utils/colorFormat.ts | 42 +++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/utils/applyStyle.ts create mode 100644 src/utils/colorFormat.ts diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts new file mode 100644 index 0000000..3e301f0 --- /dev/null +++ b/src/utils/applyStyle.ts @@ -0,0 +1,62 @@ +import mapboxgl from 'mapbox-gl'; +import { hexToHSL } from './colorFormat'; + +type colorType = 'fill-color' | 'line-color' | 'text-color' | 'text-halo-color'; +type weightType = 'line-width' | 'text-halo-width' | 'text-size'; + +export function applyVisibility( + map: mapboxgl.Map, + layerName: string, + visibility: string +): void { + map.setLayoutProperty(layerName, 'visibility', visibility); +} + +export function applyColor( + map: mapboxgl.Map, + color: string, + type: colorType, + layerName: string +): void { + const { h, s, l } = hexToHSL(color); + map.setPaintProperty(layerName, type, `hsl(${h}, ${s}, ${l})`); +} + +export function applyWeight( + map: mapboxgl.Map, + weight: number, + layerName: string, + type: weightType +): void { + map.setPaintProperty(layerName, type, weight * 2 + 1); +} + +export function applySaturation( + map: mapboxgl.Map, + color: string, + saturation: number, + type: colorType, + layerName: string +): void { + const { h, l } = hexToHSL(color); + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${50 + saturation / 2}, ${l})` + ); +} + +export function applyLightness( + map: mapboxgl.Map, + color: string, + layerName: string, + type: colorType, + lightness: number +): void { + const { h, s } = hexToHSL(color); + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${s}, ${50 + lightness / 2})` + ); +} diff --git a/src/utils/colorFormat.ts b/src/utils/colorFormat.ts new file mode 100644 index 0000000..3333c05 --- /dev/null +++ b/src/utils/colorFormat.ts @@ -0,0 +1,42 @@ +/** https://css-tricks.com/converting-color-spaces-in-javascript/ */ +interface HexToHSLType { + h: number; + s: number; + l: number; +} + +export function hexToHSL(color: string): HexToHSLType { + let r = parseInt(color.slice(1, 3), 10); + let g = parseInt(color.slice(3, 5), 10); + let b = parseInt(color.slice(5), 10); + + r /= 255; + g /= 255; + b /= 255; + const cmin = Math.min(r, g, b); + const cmax = Math.max(r, g, b); + const delta = cmax - cmin; + let h = 0; + let s = 0; + let l = 0; + + if (delta === 0) h = 0; + else if (cmax === r) h = ((g - b) / delta) % 6; + else if (cmax === g) h = (b - r) / delta + 2; + else h = (r - g) / delta + 4; + + h = Math.round(h * 60); + + if (h < 0) h += 360; + + l = (cmax + cmin) / 2; + s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); + s = +(s * 100).toFixed(1); + l = +(l * 100).toFixed(1); + + return { + h, + s, + l, + }; +} From d2aa5d626a52f85f5bb6f1f9e4bf405f8fbf5161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Mon, 30 Nov 2020 18:54:00 +0900 Subject: [PATCH 025/138] =?UTF-8?q?[feat]=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=ED=9B=84=20=EA=B0=81=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=93=A4=EC=9D=84=20=EB=B0=94=EA=BF=80=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EA=B2=8C=20utils=ED=8C=8C=EC=9D=BC=EC=97=90=20feature=EB=B3=84?= =?UTF-8?q?=EB=A1=9C=20=EB=82=98=EB=88=84=EC=96=B4=20=EB=86=93=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 스타일 변경 후 dispatch를 보내기 이전에 지도의 상태를 변경가능하게 feature단위로 파일을 만들어 놓고 useStyle훅에서 불러서 가져옴 - 각 파일들의 내부에서 레이어 변경 및 기타 다른 작업들을 진행하면 좋을듯 --- src/hooks/sidebar/useStyleType.ts | 11 ++++++++++- src/utils/map-styling/administrative.ts | 12 ++++++++++++ src/utils/map-styling/index.ts | 17 +++++++++++++++++ src/utils/map-styling/landscape.ts | 12 ++++++++++++ src/utils/map-styling/marker.ts | 12 ++++++++++++ src/utils/map-styling/poi.ts | 12 ++++++++++++ src/utils/map-styling/road.ts | 12 ++++++++++++ src/utils/map-styling/transit.ts | 12 ++++++++++++ src/utils/map-styling/water.ts | 12 ++++++++++++ 9 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/utils/map-styling/administrative.ts create mode 100644 src/utils/map-styling/index.ts create mode 100644 src/utils/map-styling/landscape.ts create mode 100644 src/utils/map-styling/marker.ts create mode 100644 src/utils/map-styling/poi.ts create mode 100644 src/utils/map-styling/road.ts create mode 100644 src/utils/map-styling/transit.ts create mode 100644 src/utils/map-styling/water.ts diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index b6bf5d7..530d538 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -1,3 +1,4 @@ +import mapboxgl from 'mapbox-gl'; import { useSelector, useDispatch } from 'react-redux'; import { useCallback } from 'react'; import { RootState } from '../../store'; @@ -9,6 +10,7 @@ import { } from '../../store/common/type'; import { setStyle } from '../../store/style/action'; import { getDefaultStyle } from '../../store/style/properties'; +import * as mapStyling from '../../utils/map-styling'; interface UseStyleTypeProps { featureName: FeatureNameType; @@ -30,18 +32,25 @@ function useStyleType({ }: UseStyleTypeProps): UseStyleHookType { const dispatch = useDispatch(); + const map = useSelector((state) => state.map.map) as mapboxgl.Map; const styleElement = useSelector((state) => { if (!detailName) { return getDefaultStyle(); } const feature = state[featureName][subFeatureName]; - if (detailName === 'labelIcon') return feature[detailName]; return feature[detailName][subDetailName as SubElementNameType]; }) as StyleType; const onStyleChange = useCallback( (key: string, value: string | number) => { + mapStyling[featureName]({ + map, + subFeatureName, + detailName, + subDetailName: subDetailName as SubElementNameType, + }); + dispatch( setStyle({ feature: featureName, diff --git a/src/utils/map-styling/administrative.ts b/src/utils/map-styling/administrative.ts new file mode 100644 index 0000000..a95b30d --- /dev/null +++ b/src/utils/map-styling/administrative.ts @@ -0,0 +1,12 @@ +import { stylingProps } from './index'; + +function administrativeStyling({ + map, + subFeatureName, + detailName, + subDetailName, +}: stylingProps): void { + console.log(1); +} + +export default administrativeStyling; diff --git a/src/utils/map-styling/index.ts b/src/utils/map-styling/index.ts new file mode 100644 index 0000000..cf7b040 --- /dev/null +++ b/src/utils/map-styling/index.ts @@ -0,0 +1,17 @@ +import mapboxgl from 'mapbox-gl'; +import { ElementNameType, SubElementNameType } from '../../store/common/type'; + +export interface stylingProps { + map: mapboxgl.Map; + subFeatureName: string; + detailName: ElementNameType; + subDetailName: SubElementNameType; +} + +export { default as poi } from './poi'; +export { default as road } from './road'; +export { default as water } from './water'; +export { default as marker } from './marker'; +export { default as administrative } from './administrative'; +export { default as transit } from './transit'; +export { default as landscape } from './landscape'; diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts new file mode 100644 index 0000000..5ad0dd7 --- /dev/null +++ b/src/utils/map-styling/landscape.ts @@ -0,0 +1,12 @@ +import { stylingProps } from './index'; + +function landscapeStyling({ + map, + subFeatureName, + detailName, + subDetailName, +}: stylingProps): void { + console.log(1); +} + +export default landscapeStyling; diff --git a/src/utils/map-styling/marker.ts b/src/utils/map-styling/marker.ts new file mode 100644 index 0000000..99951c8 --- /dev/null +++ b/src/utils/map-styling/marker.ts @@ -0,0 +1,12 @@ +import { stylingProps } from './index'; + +function markerStyling({ + map, + subFeatureName, + detailName, + subDetailName, +}: stylingProps): void { + console.log(1); +} + +export default markerStyling; diff --git a/src/utils/map-styling/poi.ts b/src/utils/map-styling/poi.ts new file mode 100644 index 0000000..8b5f970 --- /dev/null +++ b/src/utils/map-styling/poi.ts @@ -0,0 +1,12 @@ +import { stylingProps } from './index'; + +function poiStyling({ + map, + subFeatureName, + detailName, + subDetailName, +}: stylingProps): void { + console.log(1); +} + +export default poiStyling; diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts new file mode 100644 index 0000000..43b172d --- /dev/null +++ b/src/utils/map-styling/road.ts @@ -0,0 +1,12 @@ +import { stylingProps } from '.'; + +function roadStyling({ + map, + subFeatureName, + detailName, + subDetailName, +}: stylingProps): void { + console.log(1); +} + +export default roadStyling; diff --git a/src/utils/map-styling/transit.ts b/src/utils/map-styling/transit.ts new file mode 100644 index 0000000..2bd583a --- /dev/null +++ b/src/utils/map-styling/transit.ts @@ -0,0 +1,12 @@ +import { stylingProps } from '.'; + +function transitStyling({ + map, + subFeatureName, + detailName, + subDetailName, +}: stylingProps): void { + console.log(1); +} + +export default transitStyling; diff --git a/src/utils/map-styling/water.ts b/src/utils/map-styling/water.ts new file mode 100644 index 0000000..70a9d72 --- /dev/null +++ b/src/utils/map-styling/water.ts @@ -0,0 +1,12 @@ +import { stylingProps } from '.'; + +function waterStyling({ + map, + subFeatureName, + detailName, + subDetailName, +}: stylingProps): void { + console.log(1); +} + +export default waterStyling; From a93427dd0cf8828ff359333b41b4c0a1af6aa6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Mon, 30 Nov 2020 19:07:40 +0900 Subject: [PATCH 026/138] =?UTF-8?q?[refactor]=20=EB=84=98=EA=B2=A8?= =?UTF-8?q?=EC=A4=84=20=EB=95=8C=20=EC=9D=B8=EC=9E=90=EB=A1=9C=20style?= =?UTF-8?q?=EA=B0=92=EC=9D=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 넘겨줄 때 스타일 적용이 가능하도록 style을 추가함( 사실 빼먹었었음 ) --- src/hooks/sidebar/useStyleType.ts | 4 ++++ src/utils/map-styling/administrative.ts | 1 + src/utils/map-styling/index.ts | 7 ++++++- src/utils/map-styling/landscape.ts | 1 + src/utils/map-styling/marker.ts | 1 + src/utils/map-styling/poi.ts | 1 + src/utils/map-styling/road.ts | 1 + src/utils/map-styling/transit.ts | 1 + src/utils/map-styling/water.ts | 1 + 9 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index 530d538..04ef08e 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -49,6 +49,10 @@ function useStyleType({ subFeatureName, detailName, subDetailName: subDetailName as SubElementNameType, + style: { + ...styleElement, + [key]: value, + }, }); dispatch( diff --git a/src/utils/map-styling/administrative.ts b/src/utils/map-styling/administrative.ts index a95b30d..6032339 100644 --- a/src/utils/map-styling/administrative.ts +++ b/src/utils/map-styling/administrative.ts @@ -5,6 +5,7 @@ function administrativeStyling({ subFeatureName, detailName, subDetailName, + style, }: stylingProps): void { console.log(1); } diff --git a/src/utils/map-styling/index.ts b/src/utils/map-styling/index.ts index cf7b040..59c7af9 100644 --- a/src/utils/map-styling/index.ts +++ b/src/utils/map-styling/index.ts @@ -1,11 +1,16 @@ import mapboxgl from 'mapbox-gl'; -import { ElementNameType, SubElementNameType } from '../../store/common/type'; +import { + ElementNameType, + SubElementNameType, + StyleType, +} from '../../store/common/type'; export interface stylingProps { map: mapboxgl.Map; subFeatureName: string; detailName: ElementNameType; subDetailName: SubElementNameType; + style: StyleType; } export { default as poi } from './poi'; diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index 5ad0dd7..9282499 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -5,6 +5,7 @@ function landscapeStyling({ subFeatureName, detailName, subDetailName, + style, }: stylingProps): void { console.log(1); } diff --git a/src/utils/map-styling/marker.ts b/src/utils/map-styling/marker.ts index 99951c8..051a573 100644 --- a/src/utils/map-styling/marker.ts +++ b/src/utils/map-styling/marker.ts @@ -5,6 +5,7 @@ function markerStyling({ subFeatureName, detailName, subDetailName, + style, }: stylingProps): void { console.log(1); } diff --git a/src/utils/map-styling/poi.ts b/src/utils/map-styling/poi.ts index 8b5f970..e16d251 100644 --- a/src/utils/map-styling/poi.ts +++ b/src/utils/map-styling/poi.ts @@ -5,6 +5,7 @@ function poiStyling({ subFeatureName, detailName, subDetailName, + style, }: stylingProps): void { console.log(1); } diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index 43b172d..85c72af 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -5,6 +5,7 @@ function roadStyling({ subFeatureName, detailName, subDetailName, + style, }: stylingProps): void { console.log(1); } diff --git a/src/utils/map-styling/transit.ts b/src/utils/map-styling/transit.ts index 2bd583a..e66698b 100644 --- a/src/utils/map-styling/transit.ts +++ b/src/utils/map-styling/transit.ts @@ -5,6 +5,7 @@ function transitStyling({ subFeatureName, detailName, subDetailName, + style, }: stylingProps): void { console.log(1); } diff --git a/src/utils/map-styling/water.ts b/src/utils/map-styling/water.ts index 70a9d72..e13fa2d 100644 --- a/src/utils/map-styling/water.ts +++ b/src/utils/map-styling/water.ts @@ -5,6 +5,7 @@ function waterStyling({ subFeatureName, detailName, subDetailName, + style, }: stylingProps): void { console.log(1); } From 305713cfcf30ae7bf00ea4d800fc653bd5b7b1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Mon, 30 Nov 2020 19:08:23 +0900 Subject: [PATCH 027/138] =?UTF-8?q?[fix]=20hsl=EC=83=89=EC=83=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=8B=9C=EC=97=90=20=EB=B6=80=EC=A1=B1?= =?UTF-8?q?=ED=95=9C=20=EB=B6=80=EB=B6=84=EC=9D=B4=20=EC=9E=88=EC=96=B4?= =?UTF-8?q?=EC=84=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - hsl색상 변경시 부족한 부분이 많아서 수정을 함 - 1. s와 l엔 %를 붙여주어야 함 - 2. hex를 rgb로 변경 시 16진수에서 바꾸어 주어야함 --- src/utils/applyStyle.ts | 6 +++--- src/utils/colorFormat.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index 3e301f0..a60c423 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -19,7 +19,7 @@ export function applyColor( layerName: string ): void { const { h, s, l } = hexToHSL(color); - map.setPaintProperty(layerName, type, `hsl(${h}, ${s}, ${l})`); + map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); } export function applyWeight( @@ -42,7 +42,7 @@ export function applySaturation( map.setPaintProperty( layerName, type, - `hsl(${h}, ${50 + saturation / 2}, ${l})` + `hsl(${h}, ${50 + saturation / 2}%, ${l}%)` ); } @@ -57,6 +57,6 @@ export function applyLightness( map.setPaintProperty( layerName, type, - `hsl(${h}, ${s}, ${50 + lightness / 2})` + `hsl(${h}, ${s}%, ${50 + lightness / 2}%)` ); } diff --git a/src/utils/colorFormat.ts b/src/utils/colorFormat.ts index 3333c05..9c4166f 100644 --- a/src/utils/colorFormat.ts +++ b/src/utils/colorFormat.ts @@ -6,9 +6,9 @@ interface HexToHSLType { } export function hexToHSL(color: string): HexToHSLType { - let r = parseInt(color.slice(1, 3), 10); - let g = parseInt(color.slice(3, 5), 10); - let b = parseInt(color.slice(5), 10); + let r = parseInt(color.slice(1, 3), 16); + let g = parseInt(color.slice(3, 5), 16); + let b = parseInt(color.slice(5), 16); r /= 255; g /= 255; From 6d9e3f58841da4d3877346def5ca28bbd198ed7e Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 19:38:51 +0900 Subject: [PATCH 028/138] =?UTF-8?q?[feat]=20[#84]=20=EA=B2=BD=EA=B4=80=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EB=B8=94=20=EB=B6=84=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/map/layers/landscape.ts | 144 ++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/store/map/layers/landscape.ts diff --git a/src/store/map/layers/landscape.ts b/src/store/map/layers/landscape.ts new file mode 100644 index 0000000..376db32 --- /dev/null +++ b/src/store/map/layers/landscape.ts @@ -0,0 +1,144 @@ +export default [ + { + id: 'landscape-humanmade', + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'grey', + 'fill-opacity': 0.7, + }, + filter: [ + 'in', + 'type', + 'civic', + 'construction', + 'gate', + 'palace', + 'parking', + 'riverbank', + 'station', + 'steps', + 'train_station', + 'shrine', + 'retaining_wall', + 'smoking_area', + 'city_gate', + 'terrace', + 'shelter', + 'wall', + 'fence', + 'toll_booth', + 'kerb', + 'bench', + 'footway', + 'pedestrian', + 'parking', + ], + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'black', + 'fill-opacity': 0.5, + }, + filter: ['in', 'type', 'retail', 'commercial'], + id: 'landscape-commercial', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'black', + 'fill-opacity': 0.5, + }, + filter: [ + 'in', + 'type', + 'apartments', + 'bunker', + 'cathedral', + 'chapel', + 'childcare', + 'chungmoo art hall', + 'church', + 'courthouse', + 'government', + 'hospital', + 'hotel', + 'house', + 'kindergarten', + 'public', + 'ranger_station', + 'residential', + 'roof', + 'school', + 'shed', + 'shelter', + 'university', + 'warehouse', + 'company', + 'conference_centre', + 'pavilion', + 'swimming_pool', + 'office', + 'dormitory', + 'public_bath', + 'community_centre', + 'theatre', + ], + id: 'landscape-building', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'darkgreen', + 'fill-opacity': 0.5, + }, + filter: [ + 'match', + ['get', 'type'], + ['wood', 'scrub', 'bare_rock'], + true, + false, + ], + id: 'landscape-natural', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'yellow', + 'fill-opacity': 0.5, + }, + filter: [ + 'match', + ['get', 'type'], + ['sand', 'heath', 'grassland'], + true, + false, + ], + id: 'landscape-landcover', + }, +]; From 1f48c1a809e702282a80c81b31f95abea5408cbb Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 19:39:16 +0900 Subject: [PATCH 029/138] =?UTF-8?q?[feat]=20[#84]=20poi=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EB=B6=84=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/map/layers/poi.ts | 368 ++++++++++++++++++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 src/store/map/layers/poi.ts diff --git a/src/store/map/layers/poi.ts b/src/store/map/layers/poi.ts new file mode 100644 index 0000000..5c5b6d2 --- /dev/null +++ b/src/store/map/layers/poi.ts @@ -0,0 +1,368 @@ +export default [ + // POI - polygon start + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'skyblue', + 'fill-opacity': 0.5, + }, + filter: [ + 'match', + ['get', 'type'], + ['arts_centre', 'fountain', 'gate', 'palace', 'chungmoo art hall'], + true, + false, + ], + id: 'poi-attraction-polygon', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'skyblue', + 'fill-opacity': 0.5, + }, + filter: [ + 'match', + ['get', 'type'], + [ + 'restaurant', + 'cafe', + 'marketplace', + 'fast_food', + 'bar', + 'pub', + 'casino', + 'cinema', + '', + ], + true, + false, + ], + id: 'poi-business-polygon', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'skyblue', + 'fill-opacity': 0.5, + }, + filter: [ + 'match', + ['get', 'type'], + [ + 'fire_station', + 'post_office', + 'police', + 'townhall', + 'public', + 'ranger_station', + 'government', + 'bunker', + ], + true, + false, + ], + id: 'poi-government-polygon', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'lightgrey', + 'fill-opacity': 0.5, + }, + filter: ['match', ['get', 'type'], ['hospital', 'pharmacy'], true, false], + id: 'poi-medical-polygon', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'red', + 'fill-opacity': 0.5, + }, + filter: [ + 'match', + ['get', 'type'], + ['place_of_worship', 'cathedral', 'chapel', 'church'], + true, + false, + ], + id: 'poi-worship-polygon', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'brown', + 'fill-opacity': 0.5, + }, + filter: [ + 'in', + 'type', + 'college', + 'university', + 'language_school', + 'school', + 'kindergarten', + ], + id: 'poi-school-polygon', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'lightgrey', + 'fill-opacity': 0.5, + }, + filter: [ + 'match', + ['get', 'type'], + ['toilets', 'social_facility', 'conference_centre'], + true, + false, + ], + id: 'poi-etc-polygon', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'skyblue', + 'fill-opacity': 0.5, + }, + filter: ['match', ['get', 'type'], 'swimming_pool', true, false], + id: 'poi-sports-polygon', + }, + // POI - polygon end + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'brown', + }, + filter: [ + 'match', + ['get', 'type'], + ['milestone', 'arts_centre', 'fountain'], + true, + false, + ], + id: 'poi-attraction', + }, + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'blue', + }, + filter: [ + 'match', + ['get', 'type'], + [ + 'cinema', + 'bar', + 'post_box', + 'restaurant', + 'theatre', + 'studio', + 'bureau_de_change', + 'marketplace', + 'fast_food', + 'toll_booth', + 'charging_station', + 'social_facility', + 'nightclub', + 'food_court', + 'car_rental', + 'bicycle_rental', + 'pub', + 'bank', + 'ice_cream', + 'fuel', + ], + true, + false, + ], + id: 'poi-business', + }, + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'green', + }, + filter: [ + 'match', + ['get', 'type'], + [ + 'townhall', + 'social_centre', + 'post_office', + 'shelter', + 'embassy', + 'police', + ], + true, + false, + ], + id: 'poi-government', + }, + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'green', + }, + filter: [ + 'match', + ['get', 'type'], + ['hospital', 'doctors', 'clinic', 'dentist', 'pharmacy'], + true, + false, + ], + id: 'poi-medical', + }, + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'green', + }, + filter: [ + 'match', + ['get', 'type'], + ['waste_basket', 'tree', 'rest_area'], + true, + false, + ], + id: 'poi-park', + }, + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'green', + }, + filter: ['match', ['get', 'type'], 'place_of_worship'], + id: 'poi-worship', + }, + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'green', + }, + filter: [ + 'match', + ['get', 'type'], + ['school', 'kindergarten', 'college'], + true, + false, + ], + id: 'poi-school', + }, + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'green', + }, + filter: [ + 'in', + 'type', + 'parking', + 'crossing', + 'traffic_signal', + 'mini_roundabout', + 'bench', + 'sally port', + 'block', + 'street_lamp', + 'bollard', + 'lift_gate', + 'gate', + 'elevator', + 'toilets', + 'water_point', + 'spring', + 'atm', + ], + id: 'poi-etc', + }, +]; From bc4987be3c5b031a2f50ae48ee26435c9ef983d7 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 19:39:37 +0900 Subject: [PATCH 030/138] =?UTF-8?q?[feat]=20[#84]=20=EB=8F=84=EB=A1=9C=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EB=B8=94=20=EB=B6=84=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/map/layers/road.ts | 67 ++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/store/map/layers/road.ts diff --git a/src/store/map/layers/road.ts b/src/store/map/layers/road.ts new file mode 100644 index 0000000..9219d2d --- /dev/null +++ b/src/store/map/layers/road.ts @@ -0,0 +1,67 @@ +export default [ + { + type: 'line', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'line-color': 'brown', + 'line-width': 4, + }, + filter: ['match', ['get', 'type'], 'city_wall', true, false], + id: 'city-wall', + }, + { + type: 'line', + source: 'line_source', + 'source-layer': 'line', + layout: { + visibility: 'visible', + }, + paint: { + 'line-color': '#ff00ff', + }, + filter: [ + 'match', + ['get', 'type'], + ['road', 'primary', 'secondary', 'teritiary'], + true, + false, + ], + id: 'road-arterial', + }, + { + type: 'line', + source: 'line_source', + 'source-layer': 'line', + layout: { + visibility: 'visible', + }, + paint: { + 'line-color': 'blue', + }, + filter: [ + 'match', + ['get', 'type'], + ['cycleway', 'path', 'living_street', 'service'], + true, + false, + ], + id: 'road-local', + }, + { + type: 'line', + source: 'line_source', + 'source-layer': 'line', + layout: { + visibility: 'visible', + }, + paint: { + 'line-color': 'red', + }, + filter: ['match', ['get', 'type'], ['pedestrian', 'footway'], true, false], + id: 'road-footway', + }, +]; From a31d6cf2b1d6735b272e68d7f329230100d33a96 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 19:39:55 +0900 Subject: [PATCH 031/138] =?UTF-8?q?[feat]=20[#84]=20=EA=B5=90=ED=86=B5=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EB=B8=94=20=EB=B6=84=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/map/layers/transit.ts | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/store/map/layers/transit.ts diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts new file mode 100644 index 0000000..72b9cd8 --- /dev/null +++ b/src/store/map/layers/transit.ts @@ -0,0 +1,56 @@ +export default [ + { + type: 'line', + source: 'line_source', + 'source-layer': 'line', + layout: { + visibility: 'visible', + }, + paint: { + 'line-color': 'yellow', + }, + filter: ['==', ['get', 'type'], 'subway'], + id: 'transit-subway', + }, + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'green', + }, + filter: ['==', ['get', 'type'], 'taxi'], + id: 'transit-bus', + }, + { + type: 'line', + source: 'line_source', + 'source-layer': 'line', + layout: { + visibility: 'visible', + }, + paint: { + 'line-color': 'black', + }, + filter: ['==', ['get', 'type'], 'rail'], + id: 'transit-rail', + }, + { + type: 'symbol', + source: 'poi_source', + 'source-layer': 'poi', + layout: { + 'text-field': ['get', 'name'], + visibility: 'visible', + }, + paint: { + 'text-color': 'pink', + }, + filter: ['match', ['get', 'type'], ['taxi'], true, false], + id: 'transit-bus', + }, +]; From 6483b2a17e03b5e1cbd0d6d1bb65bbc9304860ac Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 19:40:08 +0900 Subject: [PATCH 032/138] =?UTF-8?q?[feat]=20[#84]=20=EB=AC=BC=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EB=B6=84=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/map/layers/water.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/store/map/layers/water.ts diff --git a/src/store/map/layers/water.ts b/src/store/map/layers/water.ts new file mode 100644 index 0000000..3e8ad46 --- /dev/null +++ b/src/store/map/layers/water.ts @@ -0,0 +1,29 @@ +export default [ + { + type: 'line', + source: 'line_source', + 'source-layer': 'line', + layout: { + visibility: 'visible', + }, + paint: { + 'line-color': 'blue', + }, + filter: ['match', ['get', 'type'], ['river', 'stream'], true, false], + id: 'water-section', + }, + { + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': '#000000', + 'fill-opacity': 0.5, + }, + filter: ['==', ['get', 'type'], 'water'], + id: 'water-line', + }, +]; From 883834a06523b4d76200986935803d62ab99f0d4 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Mon, 30 Nov 2020 19:41:44 +0900 Subject: [PATCH 033/138] =?UTF-8?q?[feat]=20[#84]=20=EB=B6=84=EB=A5=98?= =?UTF-8?q?=EB=90=9C=20=EB=A0=88=EC=9D=B4=EB=B8=94=20=EB=A7=B5=EC=97=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 경도, 위도, 줌 서울 데이터를 기준으로 조정하였음 - 굵기, 색상, zoom 등 레이블 기본값의 수정이 필요함 --- src/store/map/initializeMap.ts | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/store/map/initializeMap.ts b/src/store/map/initializeMap.ts index 71aaa73..0aa0e35 100644 --- a/src/store/map/initializeMap.ts +++ b/src/store/map/initializeMap.ts @@ -2,10 +2,15 @@ import { RefObject } from 'react'; import mapboxgl from 'mapbox-gl'; import 'mapbox-gl/dist/mapbox-gl.css'; import dotenv from 'dotenv'; +import road from './layers/road'; +import transit from './layers/transit'; +import poi from './layers/poi'; +import landscape from './layers/landscape'; +import water from './layers/water'; -const LNG = 128; -const LAT = 36.5; -const ZOOM = 7; +const LNG = 126.978; +const LAT = 37.5656; +const ZOOM = 15.5; const LABELS = [ 'country-label', 'settlement-label', @@ -54,6 +59,27 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { map.on('load', () => { translate(map); + map.addSource('polygon_source', { + type: 'vector', + tiles: ['http://110.93.147.18:8080/boostcamp/polygon/{x}/{y}/{z}'], + }); + map.addSource('line_source', { + type: 'vector', + tiles: ['http://110.93.147.18:8080/boostcamp/line/{x}/{y}/{z}'], + }); + map.addSource('poi_source', { + type: 'vector', + tiles: ['http:/110.93.147.18:8080/boostcamp/poi/{x}/{y}/{z}'], + }); + + const layers = [ + ...road, + ...transit, + ...poi, + ...water, + ...landscape, + ] as mapboxgl.Layer[]; + layers.forEach((layer: mapboxgl.Layer) => map.addLayer(layer)); }); return map; From 69814ae7ea3c937370b0d2a7af9f4759fa969531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Mon, 30 Nov 2020 19:52:23 +0900 Subject: [PATCH 034/138] =?UTF-8?q?[feat]=20applyStyle=EC=9D=B8=EC=9E=90?= =?UTF-8?q?=EB=93=A4=20=EC=84=9C=EC=88=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - applyStyle의 매개변수들의 순서를 변경하였다. - 나중에 object로 바꿀 필요 있을 듯 --- src/utils/applyStyle.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index a60c423..940154e 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -14,9 +14,9 @@ export function applyVisibility( export function applyColor( map: mapboxgl.Map, - color: string, + layerName: string, type: colorType, - layerName: string + color: string ): void { const { h, s, l } = hexToHSL(color); map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); @@ -24,19 +24,19 @@ export function applyColor( export function applyWeight( map: mapboxgl.Map, - weight: number, layerName: string, - type: weightType + type: weightType, + weight: number ): void { map.setPaintProperty(layerName, type, weight * 2 + 1); } export function applySaturation( map: mapboxgl.Map, - color: string, - saturation: number, + layerName: string, type: colorType, - layerName: string + color: string, + saturation: number ): void { const { h, l } = hexToHSL(color); map.setPaintProperty( @@ -48,9 +48,9 @@ export function applySaturation( export function applyLightness( map: mapboxgl.Map, - color: string, layerName: string, type: colorType, + color: string, lightness: number ): void { const { h, s } = hexToHSL(color); From 6e7c44c88a82593e97ae53fc880fe758d8f421d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Mon, 30 Nov 2020 22:52:43 +0900 Subject: [PATCH 035/138] =?UTF-8?q?[feat]=20=EB=A9=94=EC=9D=B8=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=20=EC=82=AC=EC=9A=A9=ED=95=A0=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20=EB=B0=8F=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 메인화면에 사용할 컴포넌트를 canvas를 이용해서 구상해보았다 - 필요한 이미지 파일들 추가 - 기존과 동일하게 Canvas컴포넌트 생성 후 useCanvas hook으로 로직을 가져왔다 --- public/images/logo.png | Bin 0 -> 67065 bytes public/images/map.png | Bin 0 -> 110518 bytes src/components/Canvas.tsx | 41 +++++++ src/hooks/useCanvas.ts | 214 +++++++++++++++++++++++++++++++++++++ src/pages/Entry.tsx | 53 +++++++++ src/utils/styles/styled.ts | 1 + 6 files changed, 309 insertions(+) create mode 100644 public/images/logo.png create mode 100644 public/images/map.png create mode 100644 src/components/Canvas.tsx create mode 100644 src/hooks/useCanvas.ts create mode 100644 src/pages/Entry.tsx diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..cf548302449ff09717c84538748fd5805bd73336 GIT binary patch literal 67065 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 z

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 SNnGFtki4|ARIP+j(EkGS0I%c# literal 0 HcmV?d00001 diff --git a/public/images/map.png b/public/images/map.png new file mode 100644 index 0000000000000000000000000000000000000000..03e4b734fb851c5d9231376c69e1666e7cfa7941 GIT binary patch literal 110518 zcmY&<1yCH(wk?F<8r*fz;O_43!CeEv-GjTkySqEVA-F?u*Wj+NbKm_{{}n|Q)y#BH zpFVroa(ne`z2nR?$cnhQ`H76LN_@UqjtVZ9r`4 z(})&IT?f^DcU^9NWkkiYqB|Puw6p%x##49O&Ea_3N8X;X zTwbNUq+rzmj()+a%P0FTwOAZ9j95o=HgxG=9ulq0KK8m_tJ<)TkU;b;1RD6m*d zuWnvwuyyrPa|~Dq;+S)?(DfqWFmy0Jve1CfM%^eopwJKubXoP?HWVHqPn-cBVl#8^ z!L%EW_evojegSh7tLqaihg4CLu|tIq0hybgB4T~6t$uL!bPd9!+)1LvOYx083$|jR zHS&q@D3vAL9aq%6;&ZTE)jDPxP2-q!cyt{NfyW$(dEF7@h~Q=X9T-a=>qzySB)YII zA%t)6+;YkzGP9GqRLX&r-?^5?C@BXJx2(BR3bM3^QyyN_R$FLp;<>cm@r=IM`h7Up zaZ+O#(STt$NyekX*U=A%hj$KGL?N1~&>jpSEsrF@o;>~~Hj5~X%ft1dCDSI1%WX=* zpn|#{lpaLYtHk_<8J>{Ode`d$_xN^|A*jO;?+_2|CK_(f1knZ~X1JZcR>vO4s+qyy zqM(kQo}0BwHx{$>XV*l@r%=llqAo3;N{I)HNDT(7B$y~B4r7SYa%X_W@u^^!?P^Pm z7|Kov+W?&0TGN1e1Twz4I+*@Zh6NS;Q4q<&52n?U4T44aMWXOB4JN#o2qnzV$WG7V zqZ9^R=&eMk>fjiv{zW3<*?jNkFbbBRMJ%%K*B5gs=&m9^2}QD}5LfU>HX`=BWdUdW z(y*EDi4dK%UEnfCoJ^6}Vv)q$yJl`Egnq-n%wenvIq@FwLhmE^wwdy>F8%D1a!J=R zLDX@+Xk*y9{;yyDL^gC!V!QffZn#kq!mN<6Ob$%=;hjUi*Arc`_sD6*6=Qu z9f0Gd=2%5AnB}h=tSC$+zd1SYJab2AqV(;mh+q)6?q=DLjZjA<=6Pw_uO;x^?V31dkLdkTE9XR-X3c7yXI^*O7&+IDFebDQq$q8+Dx(+nSpRxI0oWaf*iE{};Ms>d@pyy2)6KRX zQsVvH9l>*c#5>q8AFI|0HGQY(l&w>+?id9IEUJAmT!LyJl&w`kL&V5i5&^jf)&cryAO8_161I`%JPV@34O)>+F&PYnI8g{#D^=uh7g z@WxRw)Lr(>3}vyyJcnAcL(h(czodS&_G0Qr+Wfh+TlHxA%D)liOEC?5@cEm+z)uNm zi4hW6@>9;O_Gs(p^$?baYg7&ZGuG*rZOD zbQT|XiSzksX9>=%j)jgEl$?~d@T$C3@p@rb{@gLrEs6(sS8R8no``^akA!c@*fc@) z*c`^mCzITce&IaT^azKS31J~80^ z28BYILO*+r0)1d&EeJ%Y}Ot z=@9ih%7^_s%LzL>>pinPQ?{9osn@c`;6N&pEFnuyTVh*^d(@L*=pf1@%1o@7$Vspc z?)9QcsZ?m#Na46i%cxocaxv|k-5l@SelcRP>x3N(Rzsbc$^?6Q7UL=IcUD$|QUi;X z=`Io5w1NZr1K0zW@s`vaj*Gh5#?Z!C&B`WDn;Fy5MufWYCLL4vf%>^{Lktu0IxSP@ z`4gP6)Ir1j1{%$(N*gQ3Q!1;XJF`>vQ$0M143iAhjCFR`A7#zftJbS(&2-J{F6u7E zXJ&sE{;b!REI0feytg`2Sm|-LbCPs^+{o+QE-MHlo2px`TfB-!Tc=*O3|DEI!!XZh zS>>8?54aJ-njc&hJ5%0w=*4ipvrlTZvHL+Jj(xx(%U$WQ?97plH(WJRwfOhl?%*iCVc7F$Um%BRn%nVptQ5BUdB& zpcvtiBCKFu2kwrK?w{>+!Z;%*g%HAckKG+k?y!d#8pau#8vfW0-X7gP?Yj_L7Tpt_ z7k85IkeCd$jwTfqlc0-fpKREIbz|#1pXn|zkFtp>7NZoeiSR+$MJ0=|#0`kX;WOJ$ z<*B=`n_Uj(L>Z3I#&PA?VDgcEtuYQW-W^tt^3G&rHZG1KQcAb~ z;lCSbi8d+Anvp7SbqVT{dX;)%hD|F=<6#lF-hEtn&8F2`e6F+s|5hu0se?_bLw<%1 z8Ll3hZ31mpGvbqumfFBvqJmFbjp8cR8t)vR(x%jw;jnoyxc_77bWCAlQ&*6FgqD+v zE%87~n^Idoo4rl@A>C%NAUYa)RFL@PrH|fm;j6rR4^?St`O*4( zV%bYEj%udKF5vmmj=!T_9<~Q^9h?YV;=L~%;>f_ zN7wO!$+3(F?J&>L@c5-0l|v4?S`9|m-u?^Ho(j4Jx}+4n5B{weIryuf_@EyVyjWGb zw{5Z-F3oFQo%yf3M!h=?0+FwI*CE~HD>8qQpJ~@yBU@$L&;DF*?enN(S5oO++3lw$ z-BoB*{8sPMXxr z(zy7zIDf{v+17|zrMBpD{(IT^>aaEk?{w{i{le^=eiP!J>Vfx#{3gZ9v*4lm%=n^W z)waj?4SeWly+@&shqr4$tI$H~;Rb(+NKL53l!S1TaWnAaeN^tYow?h7J0^nit@Pc#HTuXQ@y)&k2jpG(y7yXk?09>~ z;|=NU&D@>2>5Kl$K%}7o{LzXB?3NV_8*BT?u=8MYi6QhL`%la--@bHD!=6VlMQENo z`>a)jUxdr}2f{YX^AdN3D1IIuNbFTq^?ZD+I;?KHB%P3 z2C=cEvK$zg2L%|IUmzIRGjPc72n@`b5e)3a01S*f6$}i^F0);U7x)L1k&J{W*vG%0 z+^&)Y;0Uapq^2Vn7$*6@UvMd9&?RsX+DS@I40;cq0Qw7=feNf2a7HjGQ6W{gKWAO0 zsrXB2?{8l%aLov#SaWAW(@ZrD%!a{vthmv~-|j=^27HS7;Y#ACPq z_s0;1AMYZ>|2fhGu?k-Ie_j&s*YE#-({6aLi{O9W{om8r4;t_g|L^(!_eiTzEdk1Z zukfEEI!wz*q5toi|Bh5+TSE{0_wE0=);EL`4dh?{j!Z)KGsc2o`x$?Mfhv2*K?p_{ zr@@c5Ao{9~a!&ES3&~jxg@FG(*H!V)e;&KW`~ zId%`cKS!oF{1_4dk`kE|i3g3%FlNz!?26iBF>rL}jyX4k4hCfLrI7l% z8Ov3KlubaI`3=oz`XNM`aoea4#KGXWo361y%Ttq8*5U7e=KHYdq`!si*+G<3a*R7a8XCHkP0fFSb$%uen;(tP@ z;on#*4k8{ZKjof8r<@T?goq&ooxvAaala64OeLnPB8roC;9p<@geg+l+>HQ7m zuf%E``l!R79eHUt%&QNAhx8+Pzs@5T|mgpnGd6Ss&Q4FcyZTFzr(r zP-u;8)!mpLHb$wHSm_oi>O0Fu5c%@~aa~Z|L<_0W?%<9axOdEGWyS)(>(j9?sDhF5 z@h~ha`=Ykx`-s1{91q&VtM6k3V6r-TLn~bqfnD1J$4kNXaH#nF#v@h%1Yw~9efRq3 zzc!CcjMTV(o`Y$LN-Q~>s7=p0$w@?;>O+axhg&CNE>1hIv+E3FA7&)gXU3#Le66S# zgNXEXsj4i@l*H-8WojLwNuF=C$_1*;UX#SzdRT%X3S`qC97O_EnHQ0zV^2+m1R07` zFr)F;zqlZXjZyHfG;@7!{5|9WUg5LIv>!fmf^GUZe2#CN1*7!A00NvD`m}R<#$rH~ z$PnW)3Oltd84hwhEx8HhZK+My`DN)*x)3Y2m_4suImOH&uq7k8iqL=%A!L#m+Wu52O)FJ!b&N=~7L@Y*{N=@cL+lLq7U>*W?cfVjfd4w`6XMK(UT}*ew+vgYm zXvgBQc;!<&-B-OJMXH6@Y#KX5<`K#B>tFZlb}Q)Jw2)v2>eFa3eF;qGwt z=*N+W@}N*tY;ezV`e_67!xo4Yl1ven45ry9>G+Y?CVUIS-3LA66J55-*Kudy_IFm~?PuNd z6C{a+T}+$r)+>(xP-M2ye;+|W(7JEZ_!-)1>CTyu75FUDZ}v3aRu7DXsEGgccKf){ z7((grz!TLt^e{={zU^UT<0HkFYbo4jHZaBZ+Cc4YR~E}bOV+>EJ6i%sh8F(6cSo=7 zTohO)JTvG-s_o3canM+}?$?-ZL*n7a`D|nha5e@B98(d&3kT=b)Ds zzKk7$-S!Qpo+jY4hp2*Emw_2L%La3lZqKI3QdzZOk9K`+;CMYkV{Nb@HgJQq&zHiP zqwtS#D2e84%zkMc9I<1<&6=Q zk9*dj2KX`!-Jf|lwC^LNKt;H{Nn}h5lZ|%BM5Ac+^7*$jEXU--_pWMPPd_xwze;P-g4e#*%uWb zZ+~)Ov2W3yVW6s5$6xwQ8Ftj_f~j?goCl(MBcG9cdQ5%c>0Y!eQ)rgdw=QnyI(vdN zZF5F9LhhLke`=1qojQHu6N;!ty$_<>wJ8jJ(J+)GP`0-45S>0WQESF$S|~EWMnM#_ zFKXqydB(ZtZ;VNNMX!Fjh081xrrNqhlaOSP+WobOL^{em_U;UOTyzX{{x1Y`U%^E< z6m{XC3AuiiMU_x%bAu}sQ^wJxGIHgLu^x1LBJF>rjN@R7$zKVA&;)~x1z~zykN}&u z4!5QPtJr@dt|Z#w@*)IPZ}VdWJetopGi+>3Cs%*lC!hC1N zZi=?mqKfDJ)ig;nRd}k((ek#X4rjJ^bBgB!FSvas{F>!+U9z7S#8z%oy0>uA!ymX| zqTG_7S4yr_(Tc8`5>7ai=HZG+K*?>%w=6xANov+*Od;k7Cp9V6JV}0r81NauL{iC| zj}Ic&kBeU+hSG%kxL^*)x4b?b+ENV6Z5@2v@OBdLD?7w%KITCJ8fJQDnS0@05eT~} z>=VT97)pd|=Puc5HZ5|n4ayN*YsY#6c~t~BEFTaOWxZch~q5U83- znx*oPx-1p~v(Y^LQ7sGbp)VaL#Wa|{7IC@LQs1(xjkz1@N#}ejA#5vB;-3Zql$I%< zXPG#LghN;Q1ycG5n62?>&}Tocam8`=1wmdl7!&hhrs6o1T)s^)E8(2-KslFub~ z7ow5e7|j%@fEG<;E)E@*t}>2;JO=29wva!@|1d$2>=;;xlVVJ{NCSi73p4(__N`2Zg-wFVN|WG)&(*Mo)>r6K%P+={7XhY;D)i;|EztO;YNy{`1WXl4$~B z)W{t>_?b^366og8?E$hHI=`KBG=*3h*^xq>yX*2DG?UP>i{kisbktdeJh-Zl%{z@9 zns65mUfs02h=n}txxr4mpP7G1{IP2#c^D|gN6;mRJccMf^IP6N=R&E6{ zR_<^90)3wOFsq^JPe^%2d8H2?;&ps6^HPDp_%^?D2GBq9TBHLUW^XVU#Hy4gP>>~O2fkTJ#`n0F!w7@q*y^n1Q zBwh~`Kh29&DRCX$#IFm3dl?mqS>OA#tbq}1h2?@68pYIaP(r*h9TvkoWUG2$ZYm}o zb7G-15zd;yhlAQphKL<@h4fqSUKC%(egt?(ozHJeM#J%`wUr3w;b>Cv%O6W`>MmR3 zr_O8lZ`yVgTr>H&^|W(y(_>#ZH((4fc828Rf!;59o`|1M5=GGSM`#&K9NtQ`vMjf! zE$4^8CX(Pmp=@L+{ZMhjuNjW0pNjgS##4k6E+gZ7Jv*E+GzAT=rO0CF+jKNh@lf$5oRsaWbFZ3$++hfuWHNhubb`| zA;Z0S!!fm&5bz_Jsle>Z4;Eb-9?Tr5fw-bEG3Kj|t8e+{<%6B8pvfj!$4$W`4og;E zz^#_-YqXNv&IwCb4uF+r*Vm_AqrV9ToBc0nKK~$dIo{X7i@+fCr>&E;x^4=9R=3vw zr{7ZrpMu%U{zNY!HL*6v%fMVjWiN6I5rentd~Ld8J+lyXF1Pp+)ugX0@^qJg3a3AD zArYX2UJ?l!3)4i`Bwa=uSlaGSIye|XlURNm<}1W3w0lsvAf?tuxAfHR0tn8Azy%o> zN7~axTFS2+j(<^gBR}!EwHd7|#ack0PyE z+6a4-2qBZzi4Te+)ICjQCXSv==_^Rse)^?5-7*9Aav=Z}1s{;YG7 zD#oCc?pJ0yXzgRJIkI|7L2*qe*N@GtJ=w|LrERi#qMgSZO1HO=3_J7IIE*wgBCU}# z*U9?uW^$?UQ)^d5<5YecSnw44)HIytmKowyAyj~n+n%?aQ6!?d0zVu~8|oE2*l zJwd>`IS4!B*_X9V z!9|G!O1-IGk!>>uK0rcZLLkc^Z19n39w&P8+p6-6C}TNdCOlNR1bGK3-e6TOH~Ls{ zidvel*QGrGgB9DF{nKnSsPSRN`j)m^5E?Rn<4a>D)n07BefIP(rBCutgA@(;0%wjD zuaYF^6f0636qI0Q77$<0$NLxqePnyJBkNFmsNnfem)NcpzJGOR02-{gYRd0yfxr6z zx~zohjuHx;kkU6MI_+-XU@pG!J^CB?EV>z==$}}z2pi`5E1*jV0H(ZY`C#j5XM0H( z*63YwMzJ8O^l#&T7-}f0s4#p>h%}R==p;?gI}TA`MLD~!SjA=gT(&$RRzf)Vc09nM zEJX1%ta2)Uw<_x-H&`uZM3uZ)UUGBikGs&4WvOK+J*Lylscc5ntcUb`{q<~Xo%?(+ zs#;wICY5iZ#}rSxNIXX3Ua-M`Xc5eneL!!x0kV{A7RVE~w!6SzHEgku0vv|rRwKp; zg1BGXF4MJLU+D0~;!SZ~+3WXxTeNbrd^VkIo(~WKQb@{NOn#4!!UrX2Up^8Zh&e-d z?%>q}D;2M}P7^{TAbZtmG#fq(l?jbfuB1f3N26NE7^p(ZW8!h*!TH_?d)nZkTH>Tc zjh38<3JZf9{pF<-b4F2@25}WH$hv}7s1muaJLF;4>Y;1<6_60bU3HNGio5hS3|G9N zNj7Pzn7|KjADt#;C^IQ#26Top-ET6$gK2(s`syL7NC@ERgWI7w^_i>MVY2FPA&6ki zlxED9`=DeWFD7l@_Nc-$5N4+@|Ic$jv8WE*I0O8_lPExY`L53|wSK_Oi$kb|+@jRe zmfiAxPM1heL%xg`S#ab>E3;z@by7Z*LE=4lz)-yTLs=-~=~FzZHk+|T4j0AzX(pV? zMQLx^4*pGg8VB!Y>NdW$RiC=7 z=PE?b1Ofn*fq!l?-l=ESv7|3vXNg)HT>%{yZCO2piQCXah722WVIPQJ$hyop$R5N3AFGzuKH+@!{+1nh zQA#f)QmUaVH1T)A`srA`UEx8|U4%SAJu$2uQ9reP;_lx_dHtJFs-q&GOPNY+?_T|s z1cEhzae}7OX8-B!yE0r%WQ*yDvbr42?~N`QsXR=!6JzJ>%7zU&Kw8^yo+EX&X5`-` z6n==I#Mu#Rpb&eaO;OOQO47p}{6;6fuey6mV#G!_qnP-+2}5<08Amhexay+F-kj{X z2q(}xjE`?`*&E|xI%C+g0Z$%w@Gp#|o+F{i+qOnegwbxeP`v1urkehc8bpAV zm<+8J@5;KM9(_@bWvR)^vzH*#5=E01X&3U4;5;F}*}$?-ul zkB1OTDqB?zAsdxiER>ZwE&;lAR?muRs9a=+w@eJn==-E4 z=GgQzA^`0ZP?%8egVSmY6CZd#rj85|b?5BOTUV|=TB zUP^)*3KBisjq0+Z(`MJ;lD4O6#J3>XiOBW%JXp6c__dw};nsrjJBI66H28_y`>oeM znHE4hz;Gwykm#H}#Bk(B5XsD8S>f7isDjp>A;mR!%`v^uzi)UXix_yu{~~t$Ld5s$ z*0V&ZrwUxZW67a!$B@m1HFsA7+PD3Jgc0F+@;-)hXc3}{Gamn_{paVkryf^DKwlO7 z_RB$d2m?WJ9gYr~oAY+yOYyp{$N*Mv(n-F7I(Sx779h9oj^4)VCdsj)6rHMKtt72* zDiiFq;mO`b7QCdLwx%V0?hqduT7by#H#Jse%w%HbBsFyi+EuIImUluqM|OM-LK8_; z*--)prRKvFs*;A3Vome=rh58AOK!Vb#g)E`8vDb?{$EUtfw;Y`N5|onmuY@wcx8ae z|NI9wz_=E;#@!7tC9=80ca&TAJzH{v*wP=^3pmA8X(~t~B4Keby?36bx<+Qqqv6cK z?S0_+8FO~p21P&R8)~l9!x{+oHwS;F6gDvf0Yu@ue!1x)1j}gk2t^YyhAz<&Szb^h zxW@wI<@1MH-UDyZY$sGX}YiDbhicULj7T8lMlguL0g% zj7D9f#FS=(CCN&h6#&P?|7y%kc);k>i4-G!^RE8>yhXNn3J4mNPqW`$-pwcxS8+#E zV<&Y#Tq!NZEO}oidzg2tSR}tnlQ|j%TR4;-i+I4}1edM|qso~M&$+MG+VlStS+N4+ zly4|x-yJ)dtT6S2jl6?dMn{c_JTluS@@>UO{f)v8FbgDD(L?&`%wuMj)&MPo0xoUZ zk+_8-1vWw&P$SF^R?R`80ppuaMQxn#ulCumk8_I;farG_EdU=fxO%YPHq|XSH%W2& zwsMg(s z-1v~@I4W(PeDtc=OAZVX(lVYT8~Vs`6C1oWlNdTECq zn%%f=mkn{7Ii@>nOqZw3lFS{(9&EN(6a*w5a7Z$Ib~$Vep1V|F?}JeN!H5U6qxl^p zEf{~#cUVE;uKkG)jjH(kukHF9a$^>UCjnRo<{lDoK0v1Anpq(jAx0Rd;{IQh6bTAI zjYL+%OXtIk2;qmA_D5I!neJWD0Oa6SaGKscGyVN{K~{&!-e8Ui!O1%pt&VVEw0fM> zHJlM;Vq(J~ekalVlP)*_Ku9V`;kQQZ5_rh57OG}j3*t%yt`MR@lpdmFTns^YJ$1>z zK6_4oemw0U#28LSgLmD1aRN*3ER^a^_x_*-WU@77$$0vpZ=w=UyMt-2bC> zZY|SeA07JLCD#3eH)Y3t@N+xFp8P2J^giV>)~9~r4(8S0egkurjKAW}w&Gm*q)P<` zc!<1zm3ub=1@aC43>O9OFsK*md#S+q3g3h<6r76sQg3(2y}gSq%UAxkNZ>)#e{DFn zy+S>M1&aktMA!0Fn)p*!}du!YRKdJ@mQ0_nrE3TPrJm>{w zs4qI5+=u&))Dwz2_yfIKbxCl5`@n8bVT|kyYfVdcuBz`Y^8~;8B?beVn+hqde&5t2 z*%b8%X>0WQvRPm)gm5M(z1SwV)CfNq{ENPu==$rtzbb&+)NT=l`lg*Tsp)4URA67o z^0N&R!x0&pXSDqHis=i(Sfp7vo30w!Ty41rZf1?e)s>r}zolnnw-a8M9Blu1*uJ>b zeto?XzpAx2HNedW43q8#@5+y%ugNzg{9t8I)N6M=uB6oQx#rH#A=75##)!r0dXt_s zKcPIM+v#=Z;?$IGwq@;65yeAp*>G7TL7WglH*js+U#+G|(@UR3reo1V{CAY0K;D~2 zwTBw6a>I(w)JM-?Z0zrRQqtC?oR8R7;3U2wIfLk9val*=P4B}BCh^w;9NCN>#{{vQCd1wD|4ejjoPZ4TLDKhZYYx6UzSmud zn5f4EVAnx{QEaLXu6R$lX^R##0C=wQY3DmYkr5_p!*wSR|MlI59hEV$VE>-bWrvlGl}@LCF`JN z9Oq{tD&kyGN)I_$rd8iQW2fq*1w9srYqh($wsu>ZIt9QkVPQ9XF(HWy6T|1Oxuep* zg@6hlPaBt*C9B*Vaz3}dcHNaRN;O7|8XEdcxH9RZV;&r)1eSLmK4*TeU zVDB$qBlvW{q?{`<*O&ai4oYpG^`*9_iGr|&oXhilrD%8Qqf;e+M)ka#-SePh-U)<@ zkXR{WYKf99aB$3WMeLt~vG6WUH4 zDVJ=ju))wwx^jJX2P_misTEk6F;TSM+ATf2_Me0^O1y9(ggoN__r-Rw0VXRT{e`it zNF9dB15+0Zus*wx|3)~v^;th{Ru!fcG69_M>nwR0D)fhaEFeWYWu5#}M*toJsEmOu zhA=Ce*-$g8N+Y)1sdv9X861B&7xwpp3E zG=~>-;Dz1P3(@~ffQp{R@O!eqyjOK4N4DoBfJ3B-p-KDlPz0d;@J}&p0-Hn2zqB9_ zA-<(Y1ph#E$~CxnSDz+AQv_#^Kd zk&^sI*GOPl@*>MI@uFqHHBED}B%baea)!mS$qk{9Y_0*xEW%<})M~M@D*Kk~K|z~l zAe96NzPXl5n!qzG?RKt55eicZ2+?M!^xgj{m}-bk`jwt(&x11|lhvB&D5|Ix-9*f2 z_O}#7TB-&nX!|7qe?IP<4v&2Z0(kJ^pHR~&#GFa+(hl;0O33i(gp~4c@XUbq?1zw+ ztJkQM+vTI?0eH$tM!Le?L_*kSQqBbY%r(34YL<0;k292piyW~Lyn0;MHkP&0Q5v8d zkJOuZ2>Ndf@w2~Kp!Ig609*15d9Jf1jk@I<%iD$}$ea>j2>E$bGlJ-YWk54Vxeww3 zycoY_3$smuh@GcF>~wi~H6&nEmTu}yJ==B(zr?u!5r-YsX;R#3bjnF|tO)+bSXdcp zU{)93DdN?97@)-&1RiuvsDsKKen6^RWJ;U=pk(lVG6lf`TVIn;rqW$(d}ea;JZddV)+kT3FB%n6W%q%6ss~0vl@8w{17GhiJ3L zc-#=?Dt19%BdDb)$DIF-HlB7bauuoR%Mp?WkvAX|R(?cZFgefzThnCUw-OtMaj>eR zy;_?T|4tjPbn}lv7ogFi-Ok@_$vQh6)%8m<3x~Jz!mp4ZCrw7>Yzn=(qUv#gLH4~f zCfJsfmeAm7s!jJP0hMC!hEMffr(j5foeveoIiy^s$>05Ihv8}o)0(C%>I%FAW_<`H z6)%NnLe5r&;jhFWz?9Gi_OOk&nPjtwe|?duWc@%Oc@dz#I=DP&=Aodyc-3-*_RLLP zE6|hSMmlVL$)if*U`tuZ(H8^Gz~kGI0nB!r_j@_)_DT4UJ5+!?EnZNVdbVD0*pdL4 zv9J;({6+%kbj@!LJgYv39)zD<#md z6Tl7@UG^R76#^=Pvh3@xX{Y+%4H}PmK|q*XGp|8G&j|qU^TiC4N)A5 z$|*kZ%c+7IX9bEMkPQK{AFvXGMWM7EgQ)01~8bAP@85T%CieUBaU zezO_!TG=uobDWP_SI2|S{xO9K2;TohY(P*POghVdeRiNxkAPf>+so}K$~hExAdOnZ ztFk8%8Z37J))I)Mw4ZuhHoFgM7&%iM45@Qg_|==5j?~NkZPxK%oKLInr=Ji?Yy5uk-w1l^Ddo`)`o*3>jhSxn2fYoPDa~pvV)#`4mUV`Jfm%%G< zFMNQ)L0-ifg=3pxgRu(b>95XIy}o?%h_vJlf)kbJjSynosgfZ_pJ-9SIIdq|HF z0m#)2!tD_N@dAW=!x^439~lTBd^Y6qTOyEff$oq@?~|uw$~`CdWl-g}fJ0Z%0Fi)@ z3m1Daz&Hw~@C%&msT(%A;Poq}{-8l&l_MATkmGX%1AuawXeenX-$sQ%;q8T`Kl&>< zLmHr7^f4j;I_@ZHmtQ?jNM^>x8COU4Uv&Mnrz8@+R;667)o+WtF=rR{a$iy}WNUrszc@I2d|v^kjm zW|sk>A0QzFQD!g3`iHyeiZzRpyQ&U17@S+wU+JL7m9T^(oA8m2+Q!G|5aT)+AT|zX zE5_;t1A*?*tH1sU?r$eJzmtW|9CnJd0A<|fbqMiaJfxFu1AK$8dDosgw1gpT=J-*v zW;B6__6K+N!5X9JeSwYRI?A7Tkklj3Yv-jtgWW2!O@WCJQ=U*4E`8CxNIbb}%J+q^ zL37m!_o~jk_^>6pLU(=mj1O~`dJ*)vp8anRThfkKC%!$wHT}bDt`plA7rwMVs}t3- zrB>?0bUt#HW*qgA0fB7=h#3OxKT4rDtTl4bl4mLW*k;7Y32Y#Qj(a)SFgG#e>=iL|i7sPF zaZUjQqj0Yn1yLeoEk{cfFE$`%fjT>ejN_4uu)>`4y^RH9V8^grFS>;7Kx>$7-!Jl( z5m9e`9PwA$StH@lJ0?Rs0x%s!KYhWM$B>Gf?n_rgc@#%J)!2L#VShGMu89XB_yAL+ z%yrT3F^o)9L<#7Qny+y|R7hf-Ip@Xy%o>^*l}!<#(!&qXnJ;n1yVB0e^|_zT z>k|NM0B~tw(dxIgYwUo533k#luj2CtQewr$T1iw(1E>h_+)U)h3wxS!EQr5PsP!*; zc>U!k`NFG16t?W^nst_cYLdb>w0+J2e0(nCNwcL3S4UvI(Z@#)fvghRebALjtLD|{ zzR5Q;wCiA6^Syemj^Lk#l0e2|N7g=GFLTH_M^9+fA2q+1YRdOMsABGk(JBcy66xs&L$Qka>Z| zQDkMtQkZXc<+d5Z?~Pty=qbP7dkAw~bm_p}C&5m8>V9hXxUz7mtpvWM6q}h(<``n1 zJ>BSO-E&0rH03E>-|n!grbsX2<=cNtppu7jc{QU1Vr-$6*RsqMg>&|uXoF?3dhznU zRsz8`{|0-t$~x=CkD;UkVdt{PKW`9A4xQT$(Uy(T1dgoS(XP|>odNf|1lZ}5w=J^f zv%kXts}6_=3flTDb+?2~{*gCZ7f6_eBq#1oTRP#qeKZ6~kvbYd0T~IRg`!#K@(3Up z7tm6q|2l3bq~b2fyO*ZGCu3{OV5o0G{1tg5eBkEej3ASr5O zZV)TJRJ2Aor-8UT5K%0Imq)EGTM1-u-rQK?NgHl=BSz_`iaV+^6n#^H zu%2>+@|32NnITAQNJbVuyW;e&_Ru$4)yfN91l}sJGjBPd3W)1uD&`U*iXZc?j1FCN ze|L6uHiwZ~v#b2|tZX3e0cihi`yCugtR<4zv{0}}0p#A;3BOs%VRbF(5!MafmN@JH zU5`pWNYM3{wA8ois;vXtP1eF`jd*fER-W738Qiw7J$3D{;9CAJ_OXs*lk8*7#N*Qt z@qX|=z$Gy39(k+hAdlKD-$M?2HhMknTpuB|Bxf=i&_E|v-nheRpA&_;Q{}ePWVmsu2~w+%tbE)E?~opKgCTK3y;w1@T+2#_ad9BPw%y|$SC_2r0E ze+UZRG(r$gJ_(&6A|4$lFHJP4WTf8W-v-TDqizGzK=B;pAwyZ_VmCUbpngaoGpa6z zxq`8Bq(neh2_(npzsKNO4r^IS6WXc3!q&RT3IWqSn(i90kAw(I8O8_N9fK_G7@oa2Y8>IcgBXhz%jYa5jP#;ShhoUNXc}aF?u$f^PIR+q8BF^NU#0 zOuk2-J3023fT8?Se{JidA-`MWGd`dnqA@JadgNkA)I;d901re`xO$1VdPni-$I0&2 zCQ|m&oy;=6afCbIE7PdU89jT?U75JSy!4A465p>pK8Jnl`M`Z4+z{B~KriVF@b#rU zF0xGSwIq~qE_?mkmdj8@?#1YpbNY+@NXasyujpj?7K$vAHP7j#wkIp78Y zBQpqB%-E(BZ<5YodgS3Md4}2$O{d?D9dh%be~;c#TJo&%SC`=q-h}_d^#F&77!OMW zG!;1AximoLRlZi?yuMYE|G_C%%P_F!I<-9?tTOIy*GxWYX)E7Pmc5fZTt!w-qpeCZ zn78%g2GGmN@I4Q({|^zHkztby#d{_5o?5k@=4ArQJ>G?Q+h5dI=;DRV%BB zal^H)8-mRkG1qPb*<4UC1`{DKew^(CF;9|V=GrnyYT$bkk%8{GytZa8>#Ee!f;tU+ zgGJK-`^Mt`h%e>i3V{)w$rL{S7+VedoDW2BmV8^o0DpJt$)Iq&GD>FHuKt$T!)-u2mtHMP&#Al2joL#wYcH{UPJ8TD=-GQxo9Anjq!D1 zM_g9@UG4WT6U_P0WBFPkkv}Gj9S!-d+l$f8V28fL2QnQ4rYNjvC{)3}!h0XZ{C=J) z3{V2~;$N@dqPY+kGf#ps;=(Q%ZOkO{4S_FY5S0MtVmO(+*x)Z3J4T~K3g40(VDbAW zx1Z=$!(~Wpr3r!Pu*UNhx%zf(^K$YwpUJ!-C(@0ioD%_3)HaWQa8+Br(@m#DVmto=>^>lvcJ_9o7b}X^2WaR2B5{Bg zT=^R|G0i?~`RyMxn;>8VG9j5dzTx>?LFydalEnU$WBu2@`am#WLEae&kfUoEcs(4S zDFnR=I(pM))_vDRCjP1n+@Q_C+x2fE#{^&?h^rc$-1`epejj_psE%?=&gQeLw#3jadS^8eK zh#{28d@)=yhQ6Q0b1&0A5dlK2k@+HYs$}WYCm>n%e*f;P*wg0o*gJ%eDN!F#M5Nsp z*&+{+9JBRrdjHZRi{_{&=Nsr9Z)%~O_q}$}lsdmo3;5{K`lBwQ?hl17O3({q1=w=L zh%_bN=XqyZuXyW6gAH@VoR~5-e9_G7=T0=y5G7#mf&-&S$H+Jpgg>ojZ3CtZX|6St ze3iKaXOa}2@&Ax?mQhu%T^BxzfOJbrhtl08-3@|tBPbz_ln6+tq;!|0N=QgIh;#@7 z(g@Nid~3ho7-x)ghCevC_w(HMT5Dc&uAuUA6f2S}m+RLD*9LW^hBL%A``KqTKqi47 zr_HrwrCR}~FAR_7D3_#3~h zD^ZN-`_+fv-WoT_LPr+%V`(9FR|lAm)sS1Cc)LerI|=DSnYXT}#U?_-Q`t_^$qc>^ z{ehua?ROscX~2?Cc>8|A>bT@dfJ0l{$E5b}Pu#-^B_zA3YS0_3id6upR1a7&7-KuT zYgQyP?9JOa9HEW3tsX(I^Tpyr!%Cb>;nz}tv|vQnZMt~yH;|_wQce?8LZ`1dXc}V7 ziUx|%^?Mo%0Lc7IpFpknj!>ej{z+q!SFI@Y@ZlZb1dG9AX}~W`6T}BUg}U%I{+BD1$zFy_9HbBTi)tl zg^ied#?(WPw5yrSO5-!V|6po6W_P?Z`S)L|hXkOMV4`)6jRVqDxb3(OzP z8ISo-85WAm3<=NJy_S4|3B%vTdNyEbsd_=`Kz~onBIZl573o?6?ed7TE0idoDD78! zs;U@vW2^TDImX?~l#kWI(Ib*7B5eF%h!a{r8A-3~mQAV8a+Q7=cn8TefgW1Bc=~Yg zOwjDcBJOBGQS4=B@;n=+*5?axE7QtRj_DhfO_RZr>b!&x1;qndO>mCy;d56$kbQYC z%fCdRYjA1iTZQKkeYxm(UN}k98O)UJb2Wi3VSF zHXmk27Z4R|3f2YIef?13z*k_y(wgHZD&FR#M}l~ z-_}BQr%x7xJ4otm%Mk%_{Tu#=6o36>IDkD3^>vm}M|=h;pt4Y(;j+^M&V-uHZhUR<<>lj@C+XSyfrMCADLreY2k$}Ux!IPuKrM{v{LqYMri~@6vXliey~Um$mU`ByYVKsJ7Q+xjhmt`YA><`4PIcK-LKT z&tY=YrR-|WI)lqhG1Q9q2>t)ED(Qb}CXu8IGYn-+5MHr9o07bmm7uc`ODHnpi3}$G zOT$w!^`gE@c9kvCIb4_R9~1f~+9mBlW<|sc$Mb=7JcmF`W49)EdLg^n>5ii87r93= z#R}@Z>%V<}jW{O)693$DlJg?DUr+e*)kI)l#1Mj0gfoo-RJA}Kg608I zs3xQFAZmHr5k8SehI&2MXCrl+j-C8SUwh0KpO(pg7H%hR(zitViIAjg4qjLNFkE86 zTu=3_33uhMC8x*64WN3ghHP6)KwsgH3|uIpefqQl&J?+=4lScC_Q-Ui(~yt1YUgS3 zQDN;*D$K>{Shyd88PXH=yObvA34P`L2_1BxX=*HN z%f@aeuU5&sGa3uTKlfCZMNmLm5uK~JEs4U{0QbMRS^BtpHQ1V~P4;1bKER7bdp}(X zE{?3LQDOv4mSG&2$6Uhob{LZ#VG_hmNz&G_J~wl@?zMdVQcDeAS>g+cd_p2TGl=m! zVzUxiiIt97Uh)@XXuBNof$FSb*1g*SUm|hd(o8< zzkH7pzsC~`wSZlqk{R`$#q$>iQvA`UB(}G3drmlgB>oUY>$(}bI>e}ID7O>(N`Zq;Cy}kO}6_#^v-}^K> zo+uo!fH2BQ#eQ}3IWcWEvY3j2N9-Z1qYrJty;i5wxKi`W?@0e(i%8l&koB!e(nl1% zGUc^+PYIvJ&kRN8T;__8&;G5mb^MJro*vXivXINS)|JzuKMu3Lhmni*>{YWEED)!h+EB0FW|@dlz5Hw~cX?yBS29tPn&-H|ylnUOE9I5(_rrW{(iFm3u2hC?K*G1Rt>Pe+ zX?G5-?xNbDP12`yRMd~s7gC3elaB%+`)0An1irWzebXQ%JO2zYk{|i~!hSllz~wdF z(IQXEr#xOYM?nVrml=6EtsXxW0S=FewVe%(cFkEId0aqL$YtJtk6ABi-2YRi1dMdv zm@S(fWV(V&b;zezMY+gxecmQ6J=;Qaa}|lnM@JoZzB)60tXFp}F!fV1$hOcj@)N$p z6z)??JI#M6jZsvY+|4&)&Ka#{8xfa@j0B4Om;4RQVv8u}-FTBWToPIfBhV8RI3wBy z)u;Q}fs3tPS9W-0$cXN5dUb0e@AAkh$+qU44uh@q87}@Ca1JLJ$E)UuNcNG_&XE7! z-K$g4e5s@c8hcsiFE=T2`>D2@oj=b8x8B%L33usnJuGPT$pA*a_E4=lUy)ef9tPq35Tn zln^5$*XCbAg??r8_NxtTPP%Mxl(HaM$i^C@C(sh?V&QLNwn>=O8j z|9Ev?ji;?xO>XFExl1#Y$$v**tux3UbD!1XkL3I1+Mo*;mJKlTQ@+-GuWy@Urvn7P z?^~&4J$5CCD|}PpidpHFl7rR{#T;A(V5}VvN8tnd6>JtTnaAD6!20gFD2cWAOvd-Y zi^1EMYv6qG?v-}x{Ep>uYmVd1=Pi1)WlIpkBln?L+=$Vfeor*-zxlM?0xN$mK&V3D zl^Rh+G5FheQsRh=f)eQoF1b7L*ajW(zpHB_p%dABxl8wzHZq75Sw=c9bmY3(oCJR2 zMj!jA#c;~g)t3heCJ8-n4rM`{Uj70FhZR%MEJQ zj(7$)vSt|uuV|I(>yE(rUI+vD0Ke({PBqiT2~p8B$1~r)+!HgFD&N<1wr3d( zPzNtc3|nutvF9FHF>(0bFf^%obW^DP+n!;0X(ZM#!{8qL8Q!}PfPkTgO+WjbAu=eu z!1c~R8r|l>FGb>k%mmt+^Ug?M+N2}xU|y0dOr$A~ACcExI^Qzu(-uusDU2%f|88so zDnHtqerzXN*;toN!R~S99}!ycmZBP1%>C3ZZMClDM1QLZUI;5)7zoI@3|PIl!9|nP zpg;%1JxHK`J&{4R?Y)u5Uin+15W$KW_PRJXBoA~oU?lO?rP)0i=)}2B z%Gfs2^R*!L8}^ftCJQa9JF5TsTx^1QTOu@`lb7KM+}iC?cM*K&Lcfq(=9ZkMHJq2^ zZFTWC5B9(DJuyo8Y-%9AhDFq%ZA>Dpr_EF@DUmnkQScC`wBKp@6ajnp7%(ClPuiUB zP=ll>9s6*-G~o3mR@LX-IK_cujhdpM?GH?rn<%nJ5uAA_V+w=@$pjH09QWSJVuSqT z`%+w3-D3q6$zKvFm&Xf#llUlnjEN$K{)0Lt&rSk&8AcfhQ5G&rspQv~uiG)#6AgzitkBep-T{M~ z{I9%$>J{TgTteWo9<17D@Kj>Q3%K{L_YKP=G6~F%$2>k| zf_puvtOy=PFv%f#{I0$ zfm{7{X{P`$ub3mDsOyDo3deu@hJBd{$^gOKyt*}gmvQtf3)S=b+@s8?3}{Muz2Cu7 z{B<@Px%Cnm-@U!2L~Lpg>!$Gz11;~g zCB|{L%+ac~?tJVQMEij#AeAA1afsEHH{;`q2GCCEDXCb z;2D8i+30pQ2}A*UvWUDU9nkXbI>W@D;<9&nxtBL+;(4>B)yRJL$Wr5o%U?mOcTwPk z1(CDja^VD2D}{tEWmA*hgAMij=7A+6Fkbp=72$3Gc=f}VK$~yWnn;oJv!l73d`amf z6`FS0fY&Z!at)>lR`0E~IK{HfoSc6hZD?@U)$P(*E|i*K6}M43Nv391KbYd=MN%DS zKOpYqkXxl8KfOb~Rqcek#&5cGuw%S?e!@P2I_kS^z_?^2D`&tM>vtYb(CGei2%U@>U9Dn|ELCI&*qjAiqnYd3&B(ip{XL0Y^PSNz1_s5;mrGH|m_I=_`qksKAPK_nlk%}( z=Jcq{UD zdp49$28F0$AG9y*f2+8NHw%C<*(WWH@46mV0-#cVbH)ZkDO!TmZHwE^tJ(9G>541X zWdS)7?VwkgkAeU6CFV+@%UaEGN$uY47g7WwObrxPkeCQ3H`M>rC+zA}r^to!td^w~ zKwjVr>8O@MqYnbTgto?ne>;B};T+w9KErx6`}A&{(BnkNN?bPrPej&{L60Ey`8$Zf02oo6wOKEKErI6e;-gZ5o zv(M^t!0JtBP{^*}DHe|r^?N^Fl_3Wlqx|Z|FsQR&96?mz*QMS_5e_vgpUy1@%2WXF zFIwV>eq67o-IUX{oxgl62vHMjTk7J<^UM4X{LSv%?EN>U!8^7ok+Mp!cwF!Gd}c~= z?>B``u|3f@lcA2HCLNRO4rLg1!buOM-Rgh>u9C~y^M$2#hgxYHm|N695AitdsR}Rd zLw_XhHYP>1=}vAf)U$EYV^PfMbDqU-d=8^Sn}@$vO@~QVd>eET_GR+6E0~W9_q=zs zUTIS2D7Jcn1^^3iWC?lE0?n&cEf@6_12S*P+&}C*&N7sFrkJh~d{_cb?dHdgRw^(R zN|cAXqbG5T<~u=k!7#Z8FFL9fh3R@h2EFFdUA>nxY)dTwz74RV>-W~E*}ucX-V~md z9p9_t{bS!Tzy3962{_GXLG;Q%A#&eqy36}VTzNHy5#~qGJ8EPyml<}?uQ2?^!aIy&T_a^X_ChWXDh=pP6#JEnob__!UhiU{7qaNe zY04g4j4bV~Z1VK(ZVpq!2>M4i0jC9?r5H21+eWKH%qEdVm^fLb8*yywl~ZuDIsAV? z0a6NZy)DG~?;wX^jUEq8=Kk3`j(>r~d|`OpB3zx5iOXQFi-z!`+DBoJ+UX$k%6Ii%*^d6u z#6O>#1VG|(s)5wQ z&8m`1xWx-F{prhYtdbrriNyX0VW99jrf!LDff2avBynYL=H5Cu9le&1H~nZ(+U7iBiD-C0n$}2f^zuJU~VE%4oid&NFigNL0y&@>h^M9`QdX ziI84)+P^{_eoe4r0?$}UV|l0obp+4sldCb0NJdaC*QEIp|u1Bdj@)(J+QXI~6rfaV9H7RU0P$&}Y}V0w)sET~a+pbu_&;HGrnJ6IqZ0 zO&Esd(6;usw(UXJMsa;b0~0vU>rD^vO?;+M+G+i&be3>FR|qzM!5xV?H?_%9!c|YAT|8dO8UvOgk;2G9I%clXUE<9D)%R>%% zBW^Qt*&Bf{#{xyX*uq^1rug5b=HeuWKZn+o-r=&yth6Ld&up7lEfEOY))G4IGQWQ4 z!c3E(VTH&a_|I$J?_9)pqEf^=UgBo`-Gi~Pd$i|szEBlAA`bnwRwKI&5t-Bbe%};4 zyeJT!HGcmMU5|hMXW!mbqHL@htwym?%DP5AYICF&@o!P))rxA#%19XkxC8SX-m_1u32G15Eq|!b3te!dG}OU#YOswdS;Zt$a6pTW8%Dr(3TC ziiaH@INs8(_Jn}JGB|m>o+kjUJR|Gl!u8X~P`p5w816VsT%Dts5d2`GL|?l-JjEXE z6on&unbb2=rPp@Nwld$s9T}1+ybPNOM>&dKy$oUSwwub=S2F!UZtF4PU5OVB?tVwU z@p}LUB}{baAOcsQlZO7_$NYEsqniitUoI;l1Zcemx6xRL^nkv!47xp>N;T%>wLVg$ zvqK0@p`Q>nMz?A8Ln%cr-PFX2n>qU{ex3P7S$MtlshaCH(b&75r0s&sDf{)Caij9- z7(9ob2v+;oR_|2623X~5OP4_FvOBw~XKS;gf6(ykF8ferNZ*}K33)Od+G^di;XJJg!fx`?^nzS`x~531_TIt z-UlHE1+-o;8P0%Z5F3-Zr&Hiz(sJ{VfDTGyXl_?VRK+3dg8(-!C`Djm8r{DddjE=c zH^53FS|mOA$;+_3oArNeNZmJ1dCyfLbp{B^KyW=rn)-x?adZGrCABuE1dSU4gk<$v z5@lTCHTeER%NS@ImPvcs+#<LHMspj-rv{Eh{;{ZAZ*u|n zE+5EwO>8H-w>x*F{Al|aSh!J8`i1%toGi2}5R+r*usT`mWW*j7j)b3mD)F^A?P+Vv z*vcXfYImUq==fltg9*95anwu9Aku?u$-2b2s3&!)w6m9yR*M8w_o)1&%)X1*ZTe*y7^3;H=l}fEf-J#C?N@yt5_%8SSvwQ|)DV8nyAxkC z#`iO#xmTGic75f=q!7KxfhV%pmhz6^(OjB-?yHKLeA+kchsHmgd!8Hd3faEmKf5`k zG%Bqux1l3dA%(W~?-w7aJo-mW_Y3?TI=gKp5K)K?2Ri~9uDyiKFBk9FPH`%*sdP`T zuh!s5uv;~7aC18e(_kzLWD{jSi>dmFsB6I3p0rpbUgne*CGu0Xedz9gbJ+DYdn_E| zuc}Xm*ARh}tF;|zyY%&PeMh}jy1>}FEIBP%APcef;U36>XJqE4{2{^_AXfS26Hm9| zWONei)pQ|H#U6OVmXA9f?(aKaan!@5qX;GPz*9Ba?+?xPrLTa~m%kWLgf|O>;cY$m zn>^k$m_FqrC#`w+dw0=hq{klnhw_h9hz~&Em?H$?YKtX=5i!J3^s%IGA54`r>R;I1P}h&M9=sq$`@kwn#4|F-3@2pZt%J!7}fA|;7fGMQkm*f0k#*4?{GKg zioju=mY_(1sp0)UFey9ueAQPvlDi$MbSl;?5s&U2&*}CShbURj)R@SBT z=&^5%hoqaOe#lx&5V`~qkFw>#CHRl+AMI(35@UniEdK8>n|Nbh^Y9R zXwU8>IS^TH1S;g|nFHYZJs>*;f|3r4*6>b49~3Kisc11_HZdO*+UDrSJAT6~- z{Q1oi{r{eeqw0O*VeDSq{2Pj5M9u=B*5TcEXP@O2I7m-C3Tl5JMTBcIzI);-eK}*#f_=u&Vw> zfi3)+w%6OQw<`Y73r#tXZnJ$0+_h^q+AZwJc%_@I&U| zq~u!tI1U^RNt2q~a}=(f9!ce{plFmEbPKaWuem{E;wd!*eVVnLCfO23C)){i{H5Tr(tnuKW$$}etUdS=2RfDhzj1mQJqZB@QMp^tn$(YU&hJU<6+{-b z8mAFNyQaG@dzDZo{r+SHx=VM@ev6!vKl+=!Z`GeUh`F z$b=lI-eUwZNd`Cj%4yXl@67`4v*pn#y3Aguc9is2e8atZJ&Bc+7w^)Qyc{yP8`$ME z@|LclQYU58#xV@5;z)++RohMb8qr4yj4h-^BN<+L|G9nr z)2E@1xmN$6-x>t`TzNN$5z!paC0D?=GtZP%mt>`gctOelF>`Qr_5u3Up0wVvtk+*Y z!jRAU7Gydm_!mq|aHHKh-@Un-`Hm&Ec!!UHM+85Dk4oz&^P4?tXae3sy2p7hDpkEs zk&5&S$e!VZxY6drKhbU!`w)ef;@%AN0q()N@3fcSTzLjCl8Ff8WT+)0CKm?Fd=>xU zVE)9_DI_8QJG=SGFp09Iu@pXqaX>J{G^I5(W8$JagUq+SsV%ShlNtP;kQSTv%8(Bt z`5rT&(TAbr-D~O+D=0a%F4ZQ*(r>3kv9qm}?n(Py;HWGHZkWYne?Uba8)ytc^~^DI zhiji4GS!08LJ;0IcvBv!A+uF1_y7G4X<4RJzW1EU1^BBq_be!uWV8ej*BL0o{>#@g zpJ?6-Bfdas5{8rd_8(_JkbgWgxn2>53KOr}HKLDsv{pt3WjeCsTl(`k)Ky_GcVw5@ zi0`PjVk&CYySPRTemH;ffjhYS^Nz5(`*`yPE6kqWk_}E7wAzBZ;02iY6 ztQ^@y?)`^6vi3IYWFEXfsz1L$SeZd#9jj){FgHH_fO}T~9s5dql;)l>e@13L_xO6YSX#+-RA$vZ749>%qE#{v>`{64IwW+TQG ztO#IJn2Y_C_&yW3bcob&fBw+7Ww*uQ7>`HLMuIak`Rzxy>^&)r$Q=TX`OOh$Iz@a? z9l1|N$Y8Lv@SNRVAYbt6DOOmtKAoviK52W2BZ^>NeCAvRzAtNQ=X6?14w;bb? zS;F>g`q1+*y7@RMCC%eZXT8~#pT@NBm0qwYmz}10ATtCS=HcyPt*CkC3loK6Y^Gx? z`~IdQo074V|7o_v@rB9FX#{+@UztDb+`2MAlV>mdGG{?ET=UoU|A% zIPd}*cGQ!{{Ai{bK657N-nxs`(#Ahcd?zygl4vWMxYJLNxx~vd_=wiAvk^TVi!Ah! z$9wCvIDy~?Q-CCR-PCb>$0NvTn4qMBhaHBwhZ1_62&adyIek}#aAL;p%{V3htrrGx z+}M<}5cWJlqquUrQaJYU%nI#kddYkgNh2zeVp&1LA*`|8&Qj%&D0YY zX2RjeR+9n7>RKN7Q1zVTIB339C_Y=xz){2>kf(X9Ku>}QB!QfzIJ0Qkn9f!Om7*DH zQ0twAuB3SP?5sKkOqUU(Ot*u3<)x%uFW7!o_W&*uLf}YJ0+ZfIWtcSYmc2ksQ$aE4 zUDn$w-GNLoRD)y)84*Z)n#h!KTEZTY!Ekh*(ti=A9rhzncuz&3^$SMIiaVGh4EgT> zj&mUUT`JwZD%z&zy6{{2;1jhFuA$)<4=!+;Ij8ceNDtgNoyu{#Y3iCTw~T_Go;DYfdFeAot+@k-ecpgM%fp*7QoK6w zWXae1T%}!MNdU8>^{-^EUqG(UmCAepu1fjf!7-S8nL&!Mm&gNhUW2 z3S}~53l^Wk;4$U5rS~NLmQH!LzoH6?K0<=g#DPmbhPg zDFa!ytdZzW(DABxJR9w-4KTKMIm=%zDI~NWIZD#+Q(=Y!- z1)|MAH&r|!cD&LS!oy^O@RpssY`s^{TEMv|Lg}n2(*3Q7?uMVvI6~R$tw=!KVBS;X z7}f821SGUX$F&jpbn2#i@g-5Wdb+R^e}U%1>e}Q{_CsMTw}8L5c=&A7<0{ zuA~sDM^0zj^@Uk}IQ#PM%~io3DPnD9>RE|p&3oZZiadS@S^HXX)ft14 z1*h~Ak7YskrH35{Dn@#87iF*VZXPvN9)agfZhqOr+&P=YkeEDTMdz>Q`8Q5bWMiz% zQ`fd+3({h}v5Mz8yl=^4QuPOqP4)a$W#4{Cqs zN21hbfKRR)SG{dpWS)uEuju&HTH}otMXht+RVNxLu7hY34MmQ?H|JsUsu1^CxKKB6 zT>bjXhHSc-QV6hQp$tA3_5~x9tezFXMToRQ7_-ura|`&of6NK<=>(>tW@UVE_OQ(m z0?Oph@la|{ee%9VS;zhdWj#0qluAs~u|s)-I?bKlY=mHVNV2Y~5_dQE-EXazE~>9d zfslJp)ZUM#2Ga?Z$geBq13vNwAHFP z`c|p@@B@KaAH6;sVt@b`(m^y*qX07-?4F<|tDEG?E&2^c?V6&yY!zq*a~ki{MeGG3 ze()eAK8s@} zoTHEb$+MP$HB4PEoRU9ENq+eAyl=&MB$R3VEwG;;GD&*;QLSg6hdGKA-ZmD*y5M4= zB?ngY1%*>Thr4p&_WQih-+Cm7@-K9I68s2T@ zUvL!4bzVD06|E?_bFH>Qx`s7(%ZXU?-;flqJ)Th{1UM16`P_LYU`Yi6H$Q0*D#nMO z(*e4G7^%Tz5=G7+#XqB6^m9?Bs_3D5fXId|L6cXEE+O~ne<@WN2qA_0Pu6po<|u1! zE0KIT7&C%*4#4w`ec!ImZ)MqdMi=Jj<13qt39QRX#C|R-7^7A6DoW2Fi09MSV(wM8 z@gc=0vD;vO)QMH{W95Ng^wm$>O3S$xZCO2;+RWIRK%osE9oxm%>#u+kg9y&|rV`;W zNDy`qkuQ`Raf=(>6CZTV_RcCJFH1d`+M*?2d5CKXbAQNX5rU`i%Rh^&@0QeiXOK`8 zvZ7NlEI}5=?cz-;pN;pb9gznc${BRGjC3>I_vrT>_kjlgS5m)aWwq$nTWl$*@2<${ zaK5_y&?0&|#)R@{eBH(Js+na{_U2d=qK!B&ikJbm;Q6yy3bs9@011VoJsX5G%rze)n(#S4sU&LJsF@Hd{crCL%e~7s zNVSf%0gdhE3|@WZF*@5o)mB{-^EzXPhN}nfq&*O30G4w<`HuNd&=fV-2Kf9Ui*5QN za?*_^T5zw_+M|4dDm08fN4bru+$m8@g24jYZG95_jzHZp?mgcc&`Mj040l(Rk9BB< z6$LO#j713Rhii!l|2kOuAx+T`f9}f>*XegU;iDy<6m9ZRBg*A4ed-byc>);1Z-Q0| z(db1B&t7V_Ngg8^{Jpt=hW?p*APBT|i{cP%3=y+wXQQqkdqP5O%&4=3*z)4_G9a@I zv=H?lwN-1w^_26~2aH|Lp9Wl)jZVHXPR@PD6g8l<-^<|sh972Gsb7(*(ENZy&qms1 zpoE=>t-#>a+(Y&O7gkt*iZ&t%6ip}*81hbq1Q`G?fPwm@|Nge@5(g^Xk5~>yDE6_Id_z*&rj)7$6qHQ31^ic0pgx07D1r5nJRF`tu5?^* z1`z*szl6Vh7*RVZZ=QtpO6IblnL0orrmBLNyMTF@JAs;VH)EPeNNxi_-izqNrZ z)ZtrRENcN&1b%ELR=TYgaxbr)z*q;a*Ik^Ioxi3N3m_)##-;flS%JQZE!g4GR_GuM z<9PUiCYL{i(>>5o8!X(1%t?`?#h+{o>`7P~^i1zDeSDMzn@Caz$eGdHF2?ekh~nWb zjFb*?^^5`J@8>O!ISi5-Z(Lc@C?Yy+^?BR=vtcgSz)-##D0;g%AxV)qJ6Fn#OmUpikLP@t|p`ES)vD5PYLZxGNcaYlI!In+~SaG1P9d0a@)2;>~%QxEV1P?r-bbycIc0Q{+&it7Gld zIU!IpYNm?1my;6d6@X%=Jf>U=+ zt<5JCj?%=84-|u&(Xk=y{_RP+q-+&fC2X@3p++=rb zs)H=X3Kb@kpYNeD44Z0e?TXe{3ee9?xHpAg9KMW?h<4`i(A)(UCzv6$YMG zU@DNrxY##HI4hbMog5@UA0rCXcX%5qQshXj=nRvo->Soi9Xxu|$B3d6TC|yq9pg_u z+*Cz``?Ei7w}fX^fppKU=64hh`#`dFL8@*^Q59C~l&;ASht9#*D#SDnvSajaVswNwXn15oX7!B&q_d%bB~dq3;#rnqGpKfIPKxJ8TC%zql@ZHm0!-xL_T*Oj zao~x{+3_q-*f>L*EP%a?Wy+LMoLrD%jn$C@p&kgzFC39!$%Wse%QKz2s5G}(*NY@z zy&EK!$8{K?&W2hYtg*P+us&xo>5|Uti&9dFV(}+B6p=*MxHmo+f@C$7Y9>PQ6g__t z%{YMp?hOkPu871t&;)}k0#4r0%xG9Q(lcjfRH_F1*|rUtc>YwDf~x)NuONz#xa3NY z;9b?_GleszG1S*{JPk_8DUsxe+Gjx9MfuRZoEgmXu)8(#jXk-z|M6Y!v(8e* z?#NvI&(sGM59Ia|tt;!w4B^76{=Lk`mY&592VoPOA@X{tIW_6i zFT~TIBQ#wlSBTKSk3#|}7tnoyLWQsl%m>tTZs!@+G~HDUz1YuGf2N7ZLO=Kd2A*-% z{$`7EZ*u4E?K>-1G+sZ%fPs7pBPI}{5f) z3NHlWxq6q1Cm$xU)?5<|LemTE?xOP7)|yR6VA2w)UgO7Ayk45$es~~ad&Zk+nCNh% zx+D|k6gCIjsEFG>#Xyrtzro--%CT1jz@?8)Z{jiyeiUXdZzfJf^mJhYSYV^nt*bYs zF+RSYNm{S%p>0YMfmaXaRk=XI><0Y)M!vWoY7VRDV6DhlzA%$X1%r?PdD+NN;R3(9EC4zUoPpUb*`iDZ=KT~wJ2>cMl3??gw zNde2!tioB^o_XEZR(>kjN+RRJ3^NJBVRrie(S8=uON#*<672nV!aap6BB`|h(-LFG zG9(u~8F&tnCNW~38E~8+8V}%V;Hwq-H}Dt{UIg7ObcN4hbTGJ{^0M0ZtYRw#DkQK1nQUetJ@(4Zd(IxE44nn1F>R4>ctn* zf~11*R&kGSJ)zM8s1^>gKw>}wis7rH{Lk{Hn_McwpJDe2_!u?5?*0C|WAH*%^!gl+ zg<4CrDitz32@q?+@>!I>9JwGnYr^uHx1?bm)D{$`XdR23uo8h1C_eaTuOIrqD*o-f zal7~g6y@19gA(=-a1(UwNrXRgg+DpFUU76&EjA0avo8tZc!5O8mb&o5=|hUD#GK3t z+aw%H&d^CBmT-Wvy>ov1a~)NFNGh|&!LglBOZ3x@m1U3T~6=%+FlaIwQ?ja+{|olcbCiv<@2$hwTInUWV} ziZFo^WcsXQcZ4DTGjVG@D|;z~X^^L2G`d0o-h}^JuxK^30ZZJGs87h~7m~SNu4$!3 zJnP7*$I!ziS*Z?mEs-kRr-&iNq+uqg8WK3>Syv1@vYVUiBDg)IM?`{vyU6lK@!Z+kCiNHGPgKuW z!vnk%U`Jw!gbb`mIF)FolCMn!wJ90kVn{*H%ilsC@sm20Nd|Hy>>f%9dr{DbZ7oo& zumw|}a(7yX?d^b66MQNZ>KEIBW+t}=T4oG{lg+t*UU9GcrJ2l16Vy#*f|#lW`^86M zx6*&tWzhCF>PgyKhQJlk+eYHQo+yax2Ei~-@6iE!a{8|h46suFrb6%ON_`s`xo`im z&!b!|DV&8dPu{&8HS8keLik@^torT(1f3st^=3giyFwC`;OYeHO1R#T2C_jQ)bUjj z5Umk@4x-7RJ=BSminb4i|T3of<;?;9?1yWycZieH{Yesttbuo^@WcOF&)Q3+sG zR<1lQQqyPrrEJ=S2e( zQ|UUVY5^qHcqG$cIMo0KN&>l|uCg(g95QZU^@|0BEim+vn=%jGut6EmB#?o?^;Y&V z^`*#tWa_h`;e9@4Jwk2<&5a!umk1N`D8C-b1bUd5gNY6tOE6s>-SHu5(iq+rp6&jU?X&@9 zY1n$(e|#L|#37LbfdJPg-_C=PHxb71NC=Fsq9-_pzE|>6S>QX}Wn2fPv;cm=Gzu|$ zw80;FBUNP}px_!6W-2se}LeKYAqdo!M+-9__0Bq)h zq&VYK7-VfTC9o393G*X_P@Hu!;VPxL8Dq=9Tm+KF_ zC(WE2tFUm}gQ2nkX~w+!4(ssys`u7lAKD)x_{?8Be`8N@tHio4TaGifmixwg?G~q{ zJiH%(AiJj{5FrgE6|9_Nqoqh_R|_MJb}#-~+Um}IF@7XFJ&7&xE?W?E#TKQXJMNY~ z=^%7}M17X#tDU4^>(Db}d+%ii)ryQDY<^9)hErjTR9`^D2>qD?T?>6CRM!(ul}uzY z=yWCmf|t84usxT0R#ym?mgLK_8=*Per(bR^PAM3J;4SC?;5~q92?Di~Ah~@UP)zAt z!7j*^2S5U<1Dk(6N6Nll3rC7nBS8X6Rz{Cx}3;&9FyxTrwD_J2otC@M6;5M=+q+HEJ zy&ELpEIHJ)K9Q#b@c_tSCqsJOO}-7z=nrU>1Yna|+Qk$p%!>8iezOzwSya8#%cHU_ zmBS*LBl7LzQX4|@MC?`&IpTT{rs@sx0g}ACA_pBVK=8A7A4NhZtPr@X0gUYnS?|?o zy7AO(NZ|)p0nAg+_C1d-WBCaVLXcPOE9gGqf1j+uI zKU2sjbe0*`oh^>_%4%2h8WTqJPCG{Z_KotyOeWwb#)J`MMv4k-`h_PAy=WtncB_@z z&-TyoNknJG#dj?5^&!R-{&;6!SY4pEH9H(m96OfTGNFmf`7u8u4~s#M!+MHtXQgID zsvDwE4XnlTAdJD!|bpV_1Po)Xr`|93Etm`8a-xC4OTdM zYafO3u7BeC5@w1+1c5|-kfTCY&-Zr@$E3c}7K|FrAH^Yk9$4K-2r-AMjum;@SCBAB>&pEyhJ2b+ z@xa1NuN(8PegI&k*^{q7p$i7rhNR1*U?1t`<283Mx3U9sJGQjoM4UO_U}N^f6UtI8 zsJCF}FvaCnt_$8o69rs-(Aa@p&F&*X6AEL}w}PL85v=qstF<_9r|Zsz$dX!yNpjBR zr8S`qNd06%3mhv-`hRU>qY5D5gd@zhP_H9J1fDL6lr{Vkr|uLYdwL`rzQHYjjd%B_ z6ay?>(e9_F65{D-LYirA~#F-X=cc+j?};WQgW|>%}CJrJ2Q$v)7YZg z@<@#crWI?$IhOzZ7xDyqGVEZ0AqOzE8#=TSoJ+2;AMBY3mmLc^1_y)p$h%~vMe=1wzttEfIf z!Iwdg*pohW=s;$8pY+?=gM`ME=KAhddtpf!aKOkBh9IiV=iTm~s9qAc$D41u*gUX> z8U@1Bp1seW8+tn!s=OV1V-wh)=usr}T;xZOC6)JGwgQ(8^*ytd2yPH4p(3rl+1c58 zzKfvsvYN7LC{Ib$NX!uHW?}OjJdA*QlkaH0xdW$iUv^e()lY%%RdU4t#1=^Rg0nDI zu~8uSSzEM?L@|B~@|_@>K_LQBncF30y%vK7uLxqLxue<^HOn z1UvRH4d{c#D8p5J@$}@`Nmg5OqxQIN^CQn&o?x zTF$&&7v{5@6-PkX4z4#$NX%+T&`e=*2&}V)EyG~<$d&)&Po07>-He29>g*5eSzrhb zv!Qt7_>c!bpz!!`>xlu6cxhu@Xw(n4FV{;`4s`@RyXGH_wA-;vO_l$Tq^l09virJX z5K4=5hje#KcS(1*bhpyoodPf2Al=>FQo^OXJHEs3oAD24)baA%^X#+tT5GRmAwVVf zv@{JIOm-JW#nOe8IC6i=1DP40KYWoS=njF$hemG=Kax%E-M3G?PP}IM zsp%#YV6h+AHA4Y$1!x37`gqtgnX{3}Ax>cc>FC9N$qW!<%%IrMB5tfyCEcKqqM}F?2AKV3$g>dkW`m% z4Bu3YeOrmZ!ZO3FWx{%iC0Jb~+K2*KnrO;1jp-|1iPyO~CxZ>lM)d3(@K0cDMxD(A zmLKYWA{lX1q#rz{LPLNFJ81z|5b$I6eKx=ZZ!G}C0Hq&gS)dTLd+6cH56CM$3Ai+{ zWAR$Ee{zfibJt0)dks24Y{TTJ_G~N=TTpsCW3Th@d?4%_lWp#$KCvSWvj8Fxy-WRg zeg1}(iUWIsV8fI&y^#hR-WgFK09;H^7dAk2^1P<@c3?fIdIfZVhgi?W9)bf{$m)j2 zFNraIv^<0GT_2vUk9(x^@q-LMEa?xWk7%!+fGmXW*@HuAK3=kFNCpK6f ze8>E@;j665`;XtB93^0Kl>$tvTCQJO$ABjS0|@Rz+j-;U^e5cwfTPL>Kr4A+lCLYO zJWnB}AC6W_6+$v(A=jc{F%ATAV^FE}&1zdP=;(73qW;DKPzx}Q1gWt=--x5zEH{Ce z^V%<^XM%vg)xgzXC)$P&mF9;)>lLe$8d)(a2FhWm1H4Uk^0GCs2^mUEr~-zooSR0g*WuIaq^fT(tCS5ing8%j98t%pR47{QVeBHs^Ek<<&n6_(!OwS_Xdx`H0Ov zH9>JWvY@#qQLtc=B{T}Yu#IG;(XpQgav75Dk-8G=1UM=3-yC0nm2u7b5>5pzH<8W} zmG@KdCu>j*yX&Ce*jw7CJay2xr`-d}@D2!_3$`OE!S~ptJa0aBV%8DA`vq!;5ZG%r z?rE~0Z(!(DE5XW$0mVByXmWiMQtC3IhTkngK{<|gBy4XS`M6WZk}3tG1sOOok7dLT zZ>AM5+%0JnySpOT3~8tdefngfA*Vh5?a~>ZSrA0c14GBxs3v(`v)^fd-h-YS#2h^1 zk5}%}b}#D&ngEqsAlTOVZMm8H21gaz#48495kTlh=2O&80O=ir!CRmtu*>5Rp~nU` z0X>Fuq`i@GdWAjD55QRJHCGDEIhp*&#*;x9)sJgQD7)zMy7P{}M+Cc$rShuaonx`< z&ZU8Q(EK^JX?NcT0bgLD2}GMY_1Q_Fkyw0;qrGI;&HeraO>(_dnR?JS12qr$BNd-@ zcO3;%5cMkp8Y$2wgKH*P-bZ|Di1if)#uc2R8 z;jNv@$uJGLiE^VXSD`|7u#2sog3o^XCw%|lVVFo-(!5lx(j)}3v^^2zJP3&7sQ3t<2E z%C&+Y_cC}4d=r3j2Z>w&9|u7`y?d&1@6zMC%+Legy+*wNtw7B0;DNnVI$}3ofop!M zeU_2k71#E0s|8o)&IttId#M4o58MvB<=_iD77gILiTMVcfpiBX{{rqm+uQOTijTh^ zSVgY?s(QKF0DpxsS7iiy6-1$}*449RjO%?4Tj7183<}yczx~g+iu8?6F@nnd??1x% z%H3d$8cYoQ5G;C&fW$&E@y46h`gEIfzD;ZRzl6ACd(B$;@SjzhW`;2_07Kz*Ulib)g?V-dlFS7?y#61T>{F>gnp3@hQ37Np zWFW#vUsuf$*M*aTLG30XiSzj}1nL9Tt0vGH*o(62YRRa$ebrEVH7x^@ZLrLPe*F$2 zz3s(ATo-`{Bvr2#!Mz0PJ(m2g8QoE@Ha)=X?qHJSzL1*;?kzs%t3z-OBTxqcmtSE0 z{o%5ZPL071@`v%hVH>E%y<|4N+iTZ0)^!guZ2>(YrK=$ZCKI%Nj>58FyZ!D} z1`q;5nXdkAe$QGdN${c#o!XFjuJ?RLc;uJj3KhUC4N9RYWI^hSTs8Ip!x0Z9D+&|hzp5O~pWBwEUxW>?Ut;y*rc@0A~z9LUv! zpj67&kV&wdUK*Tr3+4YUP$=6`0S+uX0=n9ji3ezrz}#ZL83G)2uWwm#&_J_U3if%x z>WRo+*8zx*8GqWj08o^yS%WErM=v>E35oeQO%`D5b z%v6nZ*(-kJnE3n~Y@&JM@x5*`^I%z{DYtD~wGD@WmkBG}^<*Js|NXy^C|)vb z6b@^wLA*YzV*O``XU_~z=D?H*w6d|)v8P6EX3MJsDf45MJL3^HVGNe+Ns+U!x8gu7lf+~?@#`!1x`cuFctqHg?*M>K4c~A1H4Ic9ddu@N zk}5!Q0vx*;9sPh2)+=U0IM}xfc`-d{fgErhJiPEhtdCl6DqC)xahIxUJ`cR) z7@|O<_#;n|H!(H!tnoWuna!Wnh-1v&fP2+LvhD|h=G>}F#@PcT+@pfjW-#{X!n+lX zv*{+&Y1}PyrqDBmY-<^Mseb(l= zxr?~0y{tN2`Sf#gk}GJiJN1pSh)4(pTx>%Q=~s#dwY`jL)$N`>=*+vr8VZxBpG`3P z!SEsztQl@LS%&mR?-kc&&IfL59{&&={2PFXah&~VRj}Fk_m5%EEOA{QVNlVKs>Kc& zUVWMNr77Wj*7yzS4tdS#%7yvgxt?`?TqryhpKG>U%w@D$Om-4~6iRiIp~IH?Q4z(6 zz7pA?i=K5%LXt_D0RLaG9zJzZxpH_UHY)g&*{~MEy;}}gy&Wo@r?gD}dW>8hfJe6i z;>$Aw;NY)W)0ng>PI}_m5sYB^*g7`O?*mZWPv8EeTyNTZ7edcBjuIx8)X zp8dQlNtX~9t*_dw2#spb0)J8nwy$#o+eU=I!9&E!Fw_16N_en@yLnStY4u~b)L>?! zjEw`qF4vvI`NW9QJE#d(`7dAOV$<@iMj0XZ=-4i}xYn@gJ7@b0B>mZ|gcV*MpGx=| zV_{M3^LHxGukIIow1zu!M`k{;Qt+G|D~Ni9S;4(I{# zALF^gqQ=lG^ZzIamHu#5G6}=g zxy0^p@1$IC9w|BJrH@dYgB>>B03{hOtjS5sh1;r!5XwFpwiye)Mr7ldI-#A#%X)fb ztkB9!KPho~DZ@es%texjMXxzV;e0;lkBK=kI*EOLFW&r3&v2rH#X~IEPELqR ztTIj3mtTci zroEL^sQOL75~Y^B_|ZzOW7c7)2$VI7DEgqzmo7|x#S9ef+=3PU2-Ymk2=ZCK!-N5w^_D*ZS8p0fTW@o7HrK_PNuf6Q^=+7@SBih6s7`3_Px=26&ORZ((fGYZ^%j~9^F6ZkWNi6$UwHI<7udg zT1gWYZ8Bx1>$WwS$u=y;RD+!&GOEKzwrblnFJ7dQlH^@pSc>OVi3l+pAJ4K-;1{Am``>8EI~^M;^oymBN-C4qS1+)5@3%s9HNFz~`m{V5CSJ<$}lu%jZ+zrK$^5T^^_ZmUKl`;P&FFs&f?k>2O&OY^Vn?omI(pW%cmJQfth zx6GbDZvR5-+zy2S!RYzn4!iwY{Ymx@r^vi3>Q<{B}H|wfcC$ zPV}Zoq)3!8jdY<%qY2-VV!^F6PPM%?03eouLcbTbSJ z!ON#nY05cd%rRbnW{{L8ipKjEQG7e)<%oZ>*-?Qw)ztiDkQT>W)xaQ%7zPF=C=DG- z*2)^h7LEto3o8{L7aHs!xiBuU4UCAH#A>G*i$-O9S%`>y{n?-M=aeGCL*ZMBbijGz zxZhM5Bd{i4_$-M0LBk~rJjRT3H9!SS!)?B&&{pYcsUIh!WFMs1ziH<1X~^j3zZF~h zmJ;8aX#QhUdK`Q$e-Z0`Ar|!(EkBesW9?%a6dDGwq!5P93h3e z=Own|hihI6{SR{0Y0d3+7~_vuHMoyg0{h2hd3xSZG3x_`f2D$OKJ~w8d^49LpvZ#w zvd%DgC$aoA+PrWP*ZN;lUd;sz_^l!{Z`YaZSJb4~d`qra%^U-n4`1pN7!M}Z{b9QL zEcx(MI|7DJuxnttjG{H-eK!-$xEBK1XNO1FWeY%pB<_8xm&|ryGMkZy_kqYAt_L^UXxjL|BY;L=@5whqr7~c8epa zdwXN}@z=rE{?`Ybm7+kE%&>~{vWKD8541^z)y+EHNcnB%D>dV*t(N^HUZy4iGTgbyj#bWv_rMinD>LOYzNC4TAd zc_^&KU(-tn)m|78kQK3;+`YamPTS*Jo#P-=|5bKbV@Y-SGqE&-^I+ZEaDv@2*pRB` z-y?qG6xOr~%W8{=7#Cc=*YM7In~!bD`>Tiq=>kUnek2umy0ti!p|Qc``?3zJIGFu! zu--Kz3ZubGEz?NXhe!VPusAio&6i#2LLRpdFPeq&K>e4z~H5 zUxeM}NFc@E;9vIA7SUPPDM7tI&zg(rx=q47kd@Fs_Od7gIKcOV(JK+U92D6U5fz!D zVHTy&BUGeO!mbSAq*54bL0C_i$8h!=ckXlE`UsSFgDsa=s=@H0r}^&ccOhG@iYjB`1RaDpggxz~^$=q1p8h+;OW zA%-odc@GrYI$bR;BP6Xf2#X{*@BH*!sXHhR>zn#D-i3oNAa0rGNp)QQ#naDfL`#me zUU=*8$Liu5AY~lgQ^kQp5~kzS|GM?QVMp^Onx7kq47X&s@;K=>KvobtF+Z1z%DNrA z!LUgF2+Gt79dU@-)+Mg0pZM!*Anh)(@ku>tKKB%>O7yA4oX`UZf{!K8b{(Kc2Tu5FhUe zr;kgvuE)!r(THsH(qRW+H!#AvxFm&Zx0K5|9eBv2(x4{Es!zw!U+qiJmGguH!}~mP zXzc%~!f^WBwh(a^o6W4SM?7-66@~^5A24wVQ}=loXH7Q9A-{Lj?-6F5yhtqnJb-9x zqWQV3S%bb&SAgm3G3quZg`Z{wi60QBs=;s~*U?q3yx5C6OfVUg@4qr%s5G^#!oHHSk6py z-ZXW82zFs0<+^ZuZLenvhd`^HecWaqUq(g3gDe~0;L<>33m?s~ff1Z&l)sr+?$yoC z*;LHDz}Qp8k7xQqe@!Azyc<|>vl&)!=*#qTq*yvM3EjbcS|ajkk3&O=jFLViAxrI- z*}i8~ZA0d6VKDaW!o#%J_E^Jaj~U)@40$AYH2#04SmG-~t+5poSt`o+DfGV#k`N8X zuJB0AJLLk8cYc{LOE!c2P|SdVK!NXn3WK?yo$(~x2F2+Hqlp~5#@G>k_5Iae&}E3A z$V&f#qINYPef{WoviTo$1c$7i1s(k8PFLKa{@DY@`uQ+?I*w8Qjf>7H3FU{u5uY7T zM@K4aYVs1cumL^)&PW00CtLRBXH%EMQHa;r_O%HQi{B9I78)IeQK47xK7z719f~$H zaRoSyplOl`L0wL2LfgjN_}HL!xf<}k`W^25ivHLUx62B~wJ$~G?NgZA^Q3X$FFa*9 zBvJSf^XWo#5tKpN?=20^%9E#gZj_qpL$>n_oKBo$TCVRP zn1psc1p@=tDaVQ{aABrFz^^BRB#NN%-+`~$jF(w)-@!{&#tQ$c5QzEO{WpG2(nuD` z;~gJxqTZqDbA`_4wIw3hD!|YjBN|8o?;`tnV@t-1QR?UmdcHr0Kge86=-M1(y!e;I zfSvpoOKMCW{(<9{mF~_pJYpMBI?MJ3XrKHwHXS;4BN|C!AcSwMv7ba6#kbACj>h?X zz#SG9xN^Ox408T(pSxouBn13J*v~8OZbwY%YN)ykLq0a_fgxlNLk<;z8r%ddBDn=s zFxu0NM~Wqdtp$PKoIAwba3Yypy^$4A?H*QU_-$Zi)mU{P4$)O+s*(a9XK{+v?n1u6 z#(pzm@TCY74kUJtSBe*rOhkKa*bzigM4*nCB1Pt%NRxV+=*x|_g_r@57okyI(JJj? zIPSzKSo&Z*W>};CTxCq5A3(ddI200bHK2o4zxE-=&guz^Z_dk{7RJXR_%qHyLH@4; z1~Say_k_KLn?L+jJEK|asIyK>i>Fbu3FAVM;QkfD0vv= zKG1Fcs{F_Y0Ek+O53D({@t+B8Oz854rIMfjIU+tCGtf3`4raxPt(Nkvo)RD>6R`34 z16J_xq5X;F#7zI+@@xT>Rw(sdq?1g4+IH=MKZ~-^^?;GwFmVl2oWsMIor&dVZlB7q zt`7m=8U97gEN~Z6Hn_Qz0?Vy+s;COxf$WoveAb9RDl&_+&9PZsW+!g<&AS)cR`)1P zLW}r^fz)4Sd~cO9i8$AiYpXt?3UmwE{>sEcLawnqJmm$la@ z17JN+^yr>S0X1eBb(C8J{)7j@Q+FV`e|*S3Zm<}XM4Tp4om#x;WSc0|$3!gHN z?O+b+ev}2>vHI0uf^@SDwxpW=!^=`oGRGjir zP!ycH-l0VDIfbjXUtl*C*PQG;NYF~=nduW{uSRQcTA}G5z}F&Go$`J%Tb(qJBw=QZ zTNuEH(iou_Egv>n!I&c)BF0L<`iYV~SUOUfhL*|im^3$gg4VX0$PSy@MMa)2;p_ zMGu?*z4Cpd1@sNTd1TlU8yeJG8ut@HFy$QG2e}4?5PpPrLcCaB`$5X1_n%Sr!GCD^ z)$5iAK)`7*K7Ne_J)kzfEX`qb_zJrIEf*Ag*d+y_hph(AiTj(ltlQ5;0hF)ETTcF) zNWxxThp`q;cS!@ct8^EVb>^7$oa$<8Nk$l_8Y>%~%i@hX-%Ri_)yvY{mn{XJ@ z@N`ih$IgtgxI;|2B-ei?>wRI`ZrI>~$@(9|46C%eI#*LZ*eBJnI4KuE{s7uD%;S&5 z;)1>~Gg!!YqS;t7a=*@WfA_A3QHqPk!*iOk=2TzX%tfbXg>M3R#a!nkd6c$K zP`KMNH~>mzWy0}fEFx0k0%-4{@bF|N2LL#EJzlmQ62%j8m>@wZO;khdx17>g`qYID z+;hT%{fj{{!gMkVmpck8$ATo2_jcR5Fc2xDhMVe4vn+eFlV_F`RMf5oGo%1@U!J)p zC2g|?9j+VG^d`u9EY#BM1XbCD4*_}Uc7g@8(S`Iq zgBY?o{>tR{c4f(*_11mg+V59~Ac~!ldT`X#hD7Kt$ICC<1ZYI`22g6IY~-73GRyhw zV=Y5r4D;8eKeh98$oEg|4m$Zy+A8;3ScBUm!1pe|EHo5bp-4+nV8zb~_9yCh*L&u1 z`~5A^<}9Jb(C;ngI*Hz9l$oUUMC6AFlV}L6rNl##>vqha3C`7E>3I`F7_{{SNMH^5U zh@nC%s`4ljnQ7BNJwv0XGwH)Az@g0W#sJ+Cg?<8XD=WTI1_#*m!RJWtMD7L)^2n;? zBfvl`u0ESW^V~t4%Y6Zx4dn>XE?RC43efT9Tl{#cw+a*z@r!>n!q(*l?SQO~t60++ z-enfvzlCXXwWk#25x0vUjw=iu)fP2i`a-?&;6-LI^2ft1N58lmE*>SW;jYYF?7}{n*ItK+#@?J4oBR-@Y*;gGXYtPe}XRkwY(cEg%hjX0BST z`IQUT7=n>iV0U3oF6PvN{QF59W~*d)uvC2K68HiiU*KRJW6K5`fklF9fNl<^B?OTV z`W?oYySJw7@U_S2$wi-j^AD6E|I8EkQ=2@NpFC=dKwGr4&pADL&?)F)4Tzb~X z%34cWz=_Vct<;aN07x#=yIHLUo~IO3bI+|mlVz-n-S_R9{Z?5TB6s^tMoDHtUbBTJ zG=em8WkeAWTV{Q9oc;pVL43GLvR?b=*@oUG?_UJ)eoGqZb%z!aShN^Im}e)SR&=@k zODcFBI)_JsOrkZ?1KE4Ihon}Ws?(kwp1?OF7BAA=Tv_h7bds}oWuh4kaVHLb`)REv z`mym2F6`c4k;U=`?3r#kDF7r8oY;tG!F;qj>`By|uK+!9bfa9XRVRc6FlcX`SHgI4 z{vJS4`;xXvYB`*gI%_GyKm#qTsI2pFjh?$O*MbDpX71P?bT(pKfLZ!OU|ry5`UzB< zv;SNdE+DNlHa{4YewE)mI#qd0bgrYSIotu{HfMXxqU$Mlx&n38hOb{kiG-3JZjnUs zMT@r#q?>OvLf>oNn=gB&h7UTH6cBYmKs zLr=SV%9yP5vFGFWFhz6jnX#HIuf*T#3pUz@3h3&Ozj}Wk{CJ)#c?DhIj6S$-*NkJ= z5^fW(#vKxI@~0?gsR$}9u}HTZst5-)9oXECcsytg zsU*gJk47zFWQqAr?Vu{!)?O zN(8T2C?gRSQNVx8|4DhjH049io6S}iyME~s{CRQ4g850lC}YHtDo<}P8|50ia?AAS zs@S@GR&OamEkOd#?RGbFva@a(%8?f(+bUd62Y|*2gNZHq+OZc&a*MDng!9Z4P+WW< zvO4!cNeaYNLPtV<4s4Uj5aGq*??&~~MjQqOOp#xps=R|>x)_DJEXqFLNNe!Af=rmv z6;?D_DucOP=Ed_0O>^!EbdQ%WWA8eT3B{gW<7+&|&nIpOh@+3h!YrH>TCi2X_$m(C zaDM6z`5jw>(1ERR&Fup~IqdN*B7B=Z7;R{I4ae%IS#G7{P|WZC!mxkGM6ffU*F(X8 zL1#swMwypdjIz%>RVO?cUB!1l!}tb3-2Wuba{SRuTI{%L)_8`WOPSf_`06vEa{8xG zNt1#pLRv15k~r_W216*HTRv2Moq6QE+Z-Phf-8F8>{oY&!mn-~+PCfFo*=IqtemAG zv1ny{_+&2qOFH_!y#5>sv-(-m@LutWprX>$@RS6-lxc%UGH8-oo(X{8MTuXMS9p#T zDRAQiz4a?LWKzi)@+T>um+=_3WllN*L>=D_?=P)NY z&?tdEr?H(b|HBWuF4qy6dVBfy8@&olBwh;>Qx9`hu zF=&zj{1nzK-BAMmbJF$aO44MDcf~ufc<6dc>-;vjbc&Z8gWtLnwPZzvKD#pKyL=kI`%4Q_Q)2h*V zW5oo~jg$aaNmWIx)}LlCrN4D4MHdqZdF4E09B~i+=&oJyHwy$QRwC0J=$U z-2($N?BxO8rfoBE;l}5|od2(^vlSaNL8^YC<((0C#>dV=Ay_2Q{Z|Z#rQv3evi+lb*y%mA-U)Q_eBZjE5=8*z91`m6L!fexPtAcWu|+l&dcC69Pnoipb1 zD(`pUo!}?e{>q8E1+G*spf3k&el{x(#GXxWbpL2KJo^*4#)3~=#;r-NZEy~Z;Z;vM zvi4x00=a?~9?Mksc&C$YzbaDpXp0ij(9a8rylwBJ4_%;J*d3GS@?Gr}pl*Z&WKpfYH0A^Z%q%MlbuPc1DYUSeXJb%8 z=xnheOY-Obmwz@pE!r9z@A~Lx0;CqB(DGb2*vmks}R!(@cQ!S9L|fRZnQr4xlr^kRU5g(6zUp+ z?rvPhB}0V`W#LT^2gz5b(o1ljgEPv*aGQyMo9%{xx%9avzgYWG?JJ<6!0OEpPVn~! z(o!si^Y9ARdM3hob>VxtivS^Z}VU8xDe0~AQG?`key*`&6_wyKEevlT{3we0$$ zC@^X=M%8bvhgneQ_ZGgZgg2Av$Fr?@Aop-RJJHR72$LsRQUfaKuEqgae}f+6>mPB0 zqi?zn1g1BcueTMTp}@an<8vHERvF03ka}MB1TIk%Yg2MQmM;xj?<}ig!R&o}skPLB zEj7}qBfh)Pg#;!Xx(fZ@=K1~1*%)Gv4&0*UjQ}eo3W&|?1w2pslF#u_rqjwU49fyv z&*j5`{Fhl-yD)lxRT3(5GIPL;f(ZsNNCWRzdBB?z#NR#M`r^r+M=gm^D^7Y1g9eJpo##-?AZ7&H0Ut|=eSmr=Wn*qEhd}}xD5XG9JqI@8UhBP^j;=@ooUas;Y={3F_9inn3Qdt^ec zQo=^EDak1c4K3lN&I{1-HC&&~l=7N$+h+cmQaU%?iNY=rWBZh_jJnD~FDakxZOCf{ zGX@#Ii~|f?`Cee@4(vOVO~L+WE9{RsG{nuMe-X0)d)eYkKXm)$7zu!P;O{~t&M}>6 z3=9+n$#Q;|aPda0;66;B!Lb7)q>uGnK)k3`VLO=yM-EA0bSZ2k~GN-!NxKl34%DgVms~`Zb2*iriRIV;Wp?*ItcmM;580fF+e)bq=={`nXnO?ZhEvpNyKfWdQ z3w)bMh>7@~1aC?{{UYCOk7rWisSo;ft0<|xDn*e#gLnqR1hBnea69~asIV5Zl4NIu zot~1IC#-aCvYjJ}25dgIL)#$${f?v06ojJ&S)Ec8-@kwZ7?BBm1ok5sx z))~&0nV`i5*N`ekUZLd_E19?{wF8p^H!-kxKW2YA9|Y?hwa&>>;Zo_5T<5oQM%uV| z-FlSnT9^3>Y(F_%U9wzj&E|}q=LF}NITNxs`Hy-n;cmAY;*&{*rrbm}f&;)9{|gt= zy>7vPGn}#Fk(h`_n>61Hp+eCX{i`^8PsR55WG^Z#JI;WpIx8#qjwB zLEFducM-(=D$=vt13n380?l35Njayh0g@CP`*V0lFkVDhbtB5?`ZC5Srf)Rj@V2wO zs=sQOlR)C%W41Gpb`7Z}EUvD((wF92+)jDIXA-d7$8ZS6b{jDZcHL|A*NyB;gHeUJ zsmK4!RJver3qOZb)+{uT{NGn_2?8b3S-$HLqaqx22yY{9x1l7Fr_oN3_5GGh%NbFV zFtHGd_k(GB?txML^o`-$#%0Tc@k?^?=t!~ttuH=}=|i;%&hN=`dGK_b#-(``o20-{A#7+l7NwY^aQ2QPy)R+qmTuj|rF)GRPYw;3xO@rWXrBI|e%!%8 z2Cy_%_)k5S9nbX>-Hi@z&-WkUD=>1KZs|`R7417F2MOqs; z13cB*3!dJhSmG-v1@M%sB~6azozg0+!Qe5yk(Z7i>seFYG6d zY4Kk*kQuoedhwGIccZ+%Ng%G0oTbMB)0A%K??+L6b5B!8O~gwoaP-b+w+8S-HpqI7 z=1sESM^FTzs=(9dCorGDizBLraacMyhpY!KEYSXZOIhdu;C&~C0 zQRqAxTYiwFWIDx)$4Tg5eZiS|zd8L|Xz(0$^|TcRb-$=oG&QxD2n0ni+ar_3T>1FG zaJRLG&1utu4=oZ=e3{PH_h7UBNX77X&xp}P1@bq zCiT$j*JLgtO%R`+830W$DV*o7O$NFG`7a1b<0AF~AD>JgUgH`Udr!}LS8|PJAW8?N zXL%Ki;xH?ZauwLn!vXD^3mlFJR3S}R+1O?{|L)qoegPmnhMhVI>~t%M)JDm`QG>Az zsBPw3-j1T{c`)-02>~tvaoW>E^O%?4iS4`h( zv5o8Ywg|vFfCG`wT5?tv~}{bzxV z3-qd(W8V!RnK#YoK|| zw~|@Dia-e5m&=NkkIFU2Rw&sX?bK&tIGMdWNtGMB#05gxQ(#LYxA1aI*o`5oR0pf*5;4{%A0LhYq62@}tijTpLs zt|S9@si;qR#%+O8!_FL?3mGpG^%yDFT%&8#+Gh^S6}SACe;dP3@A9CmU$m+CZ5 zn+!kpMIj*Apa`jYD&gX4S_#@Vh$|xeC>%%vy1ML*YkS$&<>bmT|C>IU&ADqQ8PiI` z;8gCC!HgK8O8TB*U7vtGq`9gM$gsqil*xG1&FSHeGczQDS)PQ-fgnu=5T~@Yk8jBu-rje}f>EYlNzkHLv;t>gUrL5`Lt}*QMbpO3?z4@+N z#%ZElUi90@d@y-*&QaMEkW14})!am6vz_bidLs1HDoFrfPqOvF1X3;F@I~vl_DI%9 z#(i(b%^`|Olp`e^FTCCAMHjZ(J|W&Z)o66|cNVXONC}?E^DBPWz0*18wNq!<=-rz>MU{BGaC~ z!3GobNuM@fWHDJQiqBm1TVE0E$`@NZFg=oz@%O4&SB2v((q8oRXXQLMy65xDn~Fiv zxksh-o{ui2?UkMo*OEq<-^_faV2W^xJljbH>&F1}Y#9W?q#p#mQlUjHt-jfjL;!Nw zKTZ?XPzU7Dt9J4%hZvlJE^(i5%ge?2zj8oce910N-u%@vzO?Qzk@HHDq%)IHQd}SO zCA+pPD#_mOJ}X`o=L1wwbC&wq{UBA=#26afRh(ysA^zTUh=`v9DgOeb$o9qM*my3t z=8#2Pw>SuBrN%n}jD`?S=2Ml-n$?~GmWlm4n{9-s2D{@LaBVL*p7v2CA6GcMBgC@uM~+NXCV&yWFv zDA{WwPOW!96bB}M0l?qBS0J=ep&Ps#EPZL_F7&8GJR<%?4HIP98~L-4OY`N)3Gw+l z`s~J1rk%+~b&xEEn*?K#n~S6DfJ>cK-DuuO*ZFS@`JhmDK6K>c8AVe*&IgF9YdIiF z8!Ryu(7_DQ1K857tCxDb_osPx1I43)*O>h{SmEI6lXnu^Ds-n~EuCsAN%-aPF7VM! z3&UOk4G<3MS0M?@7jiDW_ujB}PWtTjL#31#IQvLTfg+xx(N1_k!pG^f7*9jnRy97q z-q%^`5(=GbG;0K|WPrT^;fkUYSRVmoNyuiR+T$+~P{+RmxNI;ZMGA00VXO7Q>@Sxx zN-GNHHs6syG=UHS0DJw{@}GbFWJq$_5sRpZkRnU0eVoBg;8;w5li@c<9oQIHfceS+8ZrlG^7Q3he6kx&KyAHzb*D?l?g{rJ#M z&6m*dM=eP;|LC6^C~{_>#$aLN%PI2ote>|cG7#qj()ixK&BqQG%l~0)ZqQ8`gaa+* zM!|%Z4Vu;+r21S@R&YxI83Q>Bl|wjE5S-8(FkTovs9&(xlMHP%M3DQMp`~Ok&KX@7 zcH&o*dT?>!vQEYNOGznfGIbUKb&T5&HitE?AkcP*9y>3;{~@!?)r4mi{G#yX%Odsk z%EGAB%jw4&{FK2out8KUS9j6z`QxID#2Tkfip=rI@_Uh@MC~%iIhVJhe@y?Q@Xt59 zooYp+r^22$m>uk4ss=K5d4_hv>;s2K7vqu`5mr!0K%ciz!O0QMKIfIf&7Ac5W@FhT zM{XugZ1_{XSNH};X{Hb{K@qB63a>O3BE^D7#)RYm3 zRGc;@B&j1xU7aDVcPpjNE7j(*97X1FFn%7#Po7n}U#dWi zbS4_Cca4<%FvmH=%YdMOewOb>sje~d$>#>O-SePg&vLJ(UY{W9O%rr>+r%h@f@xd` zj#6|V%TS7KDU9ht2k3CWJ`CVln~Z2yR^)+^HJx+@rz>f@J8h%t3&~Oae$yn9Sk#jb zkEs8gqegN&O86@1sHebwri`xluTwqDMOt!9ScpY%as*u5MI0!aM|MEjO_oPUHpyU@ z>}VZilSrgG>!ijJg)YFgH>amII~ zb6q2(0?H0%8LXEVxOq5~sy#}cv^U(Cs+d*-F_bv1Y;9{aNyHwt1YR(Gc)e21dw>5+ z0?YGyrME~x;9CYtvU6pG1I%BJ<`dkV>w1)d^EjX|e5Jb7Q(_1U+yu-mYdEvZEz%eN z3KLR)TX}K>gXEZd<|7q zk_rwaOrE5U*v6w34#YFp34-hJ%x}VO;s7E%{~%)c^n(IpQ|eVEq&60U)SUJ6A)xyt zjf!V*hJ|$&sE;Yn`z^|PZh}=yqqZ)oeYXas1|T6UeZDXl0vnaUt~fgzVD|hkYnrvTYAk3Ea)fL>SAUIN=dAmlG9#v z#MI)d12GRED1o~Pa@!-&**U~`NL|d zQ2pJdk2$cIP+R@?I>hd}1OXf?5?eWjw|a7|*P;u1zLOU_{Ciz-JW#LazI1V1r`Xww zceIotrlx#&bF;ZBa}1wbF&?tB4+OJ6`KS8<+}@F~38e@(;ck*pDQKen)y3PYmFR^R zGuvMKL;s6oI)Ejr`@=v#d$HM{MGW?OKCQLBv1ytYmuN{W{lIG{eQA2fc|BvNag$!# zUmTf>$Mdvj+IYf!6C00H+0Pg>xRuZT?Ttq)!9M0jQ51jgx250H!KcuqK&_Reepwti z%45N}9blJ3fcQC5J&wr3nCpO>v%VtYPxYTp(6ysXP z=ix1AQ^1&M-=8!GG`hk@6tNVIC22*}vEmx$*x+pjc!_>j2tfh#IMS3?aoXI9soPlz z{bG@y%u?zkE5an%S>TH+Xol7F8}rTlNknty1&|Ay>l0)1$Y3|V%C=6K(ch0B^S;W} zfyzd2bZt3u=|ol>rWJMLV2q|Sd+c)t;Byu$G{<#;5*z$4(H30aalaC*lW_ApPDlMn zA01!5<+m6w3W4Ymc zR|OOg?dqnYIAgCYplYCkdpgnF)4b&lI&;YdK~oUYu@(Uqkne`Emqhx!~9ME zYnM;I-G=U5#^hjtN>`v`y$OVDGDZwcLuMGlzu2E{QO>tF7kNW*3$`}xErq`^;GjTj zZH^8>mY>;n`kH@LLdP;!WMq3PFH&6L!UP>icrptD1;%$%$J|7NGZ=I`14G_y{M_IM z0IFea?NOk7=~RkT=%NCm$nb#MZ>~yc-|VHXuRX>utAUsd0jUkp;zQ}~jWNucX}08z zKyL3A_$#NoP4}>%z4j$GY_?Aor$|fl7yU}oZ^>MLvCLV#s0d;Jd)biWwYXSc}Dzf%S%eQa6(x`TY#v=o}+9c=GZ3xlo^- zWqNQ#J3s~FJk#O@6boPm>u61BJdD7m4m=6lGbSEDBDu0lxQZ?E4N@mHz-Cu#b}Eq5 zC@t#l<g)fi;Y{}~)BM=Gc zp$$jX!pX2l8umEsGPo^cLkFk$vQDsdJnP~oALiOM2+|;(f;B(&+tmQ0_`H7X1Wpbu z(~YnoCnsUa=tYL{xJ`jDi}*4c;}wi~a79J!|0ok-1qwM$eaYzeq)Gl@AqFg2w&~(_ zY$fbj9_;`)%Mf1*%=o|U($Sgr=`XWp>MlR~H(;!=-f1h%HT_X|SJXlkpaUPb zzx?(`@U+EhqBE96MwK3PTBrhns13@c z!dP-lZ0lS*tF}-q89>@!MIiRiwhtoXodf?(MDu(fncQF~dVRF&BYWqK1BD~MFI;zw z2x7Xo^bmD`E|2D6mkESXiVw=Mb#+Catushy4L}NVfdB)nJ=okv!I*KoW&(7^C~L&_ z#`Z@i-aL1AY(#u36Oc^5vl2}@eHDZK8ztae>@RTcZFerxfGYXz_1CM`HFmiwH4epG ze$KhJFlL^&dcOZlc7Nz@LNrspyNaj4(|}r39l@e(# z7E47~pyAZ?gM{9lSkh~uBVT)%gODIHXQY>ezZN+3#Uw_!*tKCC z@djx>V?`q4him;o9Fc^0gC}TrQh)rlnQp0k+JTAkYm{d9n@;g};s9jX%cB3EFm^Er zV#}HQPGUSoC-ZrRdHD_rhax#i?*CXi?|7{H_x-07GP1Yqkz~8=z4uvfg1KYwSU1Ue!UbvHX9_n!hUNUANLv%SuR?QVEt+9T$MWEk-~^!)>iTOX;;mZ$AZ)q63Y)-8@H)$^dak2_~0;+Wso2`=Tp ztca}GvaIcy{%@j6!P%d)#z5kv@eWu6_ok%#H#bE-ML$}}<%^8Q(oA2fpoVw1+`b;I zcm_HK+F?Re{wy6W!PEWgpJ#--m^m>^TAxLpHqr^idce5@&po&k;E&_YCQH?22ylPg zYUQoW7^-r$VjiIiuTFXg_s8)1=zE{VHIq8=#-QsC4)XXtMw4|~PUf-=_PQz|!dw=O zQ^;c}(tTp@PT6&_5hE`k7eK8MD-!IgxSfl^)#0lxCmA{QfRdBvc_-pi{Kr3@z`XS9 zn5Z|KUmQdQQr_$bripEM1-|AohSAR>6{VDV&PmAL8&UtyypJ%ELqwzGphya>G!G;D zY-4W!k~Wsrmw-M2h)ExO5Ak#9G{6<+OCs{Qh?+Paw4AYJn&QIMaI!8zrnbs8NI&oX3z8#YQ%-{4kBaX-X(Jd+{B6 z0{eANte8rm^nqdc?z?y)PPltKzQm=;D>i~--Ep_rjJtjQLzP!5$(qz1`l63JAf+_N zi^XP7-MXOU%wxY{5e01}zGcP>^2?48Jq~rw8R-9*_HdSyvn7Sx?C*)C(RuICX1dB$ z|Ng*_IFAOAE+1|#X3n(Jlg8w7rtUS58Ra;c|HNG!)9jYfpSBho{pY{r1&IU4T#rAv zD+9-|ecqbFAn!Ia>1%`t$TyDuq=6Z7vt&$+#dO`-`0$Go&j$#W2%R5_s=LY3!>o@o zv%xveV|pnNuqh;&36)6KZ_FFHv3GFA=LA~u!k9s6;{%Bkm|*@QFH?<4WZ9_AUU_|RMgF_;ax7bA9&9oU)dciMQtGC$kzosqBYjwseH^mC zn>!XD6;s54l04Ggcy9wCf;)KBc=Y(9gbhdqPAqHfUtJ!r4u9v#4bu_smCn-LX~d^9 zV@;h+#HkC{%#9>!9Mz^!jSPc(%C{aip+{nneS@0spk}&8F<_Pbi%1OLV^7n(= zE2reDAhw}igMe$|elL0VFCnDbNJwLm>~YjVOYHo!xKH%hpU;nFu38=!>Z&^#@PkV) zq+luo9Iv=lKU5yB6R}^8y|*q=Ilf^2{fB*i6@wT{GYnZZljNNXee>q4U6S#$z%iK~ z^NDQz-WTu@A=J`o5#k*z`{G}E-!)UeAGAfp;l3`W7(d!fQIy`i>zGB-w zpH`#>H$Vg{Y2X>8p(6uBw1xT~TZ(W7?>5b(QR0Yd8+N zzq`G|_<~8?id(RGy~SQvy=|%5>YDid_rMc9H8EEvff5f{AzF&|lNTbPfyhC3(p9+g zN1HZSeEeI&8XYnup}Y+lC%H;uWnCs#*o_IZrCuG8ondeI|52 z&wb6uPNiLM@i5--dFiVrqNo#syo_Nc%Uwp%(PV^omexBJaCLJpZrp$Mr$Gpe!bJrx zy&(AJ=x@zxGhkiS_j`!pg63IF%CJ$ z8{4}Ox|=YjP2rGE1V52EyD7SDbmBwzv_!cm(W=dXBtPt_jA|kK=UOYH2akTol#??M zQ~XdEZ#d09U?}`Atj`fyJ_Yw^EEB2gr2=&qgMvEl%*EX6-J1nrchQGP_G!}Ple3kl zN^F_OnHU~eW0RvXAi9h?U?r%hs6dDaJGIk~1SjEaimpBc7VplADX|il+%W z>6Qu(v2#d%62ntUonT}Z4nd*86YNp#c&UiSY{}XZ+*wC0cNb(a*kryx-efWOkG$fV zpO8LbB9R;VEA)#N7TKjM3AjzbYws=FP#EKk*+dGH%Ls#-N zUgUbEhd=eY7V^_c3q#z3S!nF}lvZf!nBE~$rXRLu;CKMI#dOU5x5a!ug0n|mg+`56 ztVXvZd%G87`+CBi$INt;m4B;abpzp^&R6>hO&lMR6}i{eC@{XmPCwA z-{Y-Byk>&)_*PC>@$f@sWH@U?s4^yqs(2%lnvhnxe~=Z&JBeb6|0`01E?>>CXL(8E zpkCYa1+I^Ob?VaoVwbF_eVGB|+9RuIpb)8P`fx$;{oiRjL5ky=ro6Z}b1RG`iO-WI zyycnq=^lZ@3cPNZ#4@hnb%GTGoU1Ve)#!9`X@WhyF`ds|2hy?kw5ET2;S&6ey?{=j z>|#Oz@+KUg&$WmC6Jaf_P6+O#d#P*#p4ylq?$@@lpD1Sg%z_)$s)qC6_X}bgwfk?| zc6#D#$HL=pVAyRtmOhZtlYnBeC zXyxv7szqsi5ZWDVOy?U+L zVCHt`x@x(AeXM{Y=lU&r6?S3dp%9rfb`Ei)<4Xe*F6IZ(xiF>K4JxR-% zyHEEP9sgnz$yFTpFHXpL16n|dUf`AHIdTvf}dZzQ+eGD2bO)?6};V}r{!t&Mm z=xq|O15Pc$?C8*Knz%eqi8I(-2mSxrna7c97T@Dmru-y9k9s$kB}XbRA|s9qD^wtQ zvk%?aD*^OQOA+o)VoLm&vUC>aDXot(+Je&P7?LvM(RVdd7o?%ZukChT6&6e2qLd-h z-=RV=M_m1St54K>7vWQUZLV$g1MIK5eqd2#?vd7#z(!84QkeP9IT8LHjP=BBX2Tjw z1>ZQtSOY~nd$&jCL)qrSs2)OgY;k79A^NzWfh59M%P^mR#=^&MXz%<7I4#eP)Enn% z3Eaua3xo=^_Kx>oh`dVsJs9E1H#@3SWw)Cw%}z*G?lXehhZXpsB$sm~W?cX3 zMjGZ%8uredp#C@a=KR5(Us##4Cw)|3;pT@hJHj=-X3wXRY|Aw9 zEBx)f*&^aQ`?au()rn_DLv9WVZMo1g_!CF$KtXOVRaPek8P=|i9nv280d*3RNGdA! z>ka_}hFN{lSs}g$;SmK}QlVx`m#edMfOz00mySnHR&%vcHd={hips~&2T}#P7-XWd z=y=l$dIgzr=o9{ijWB*E*dda$9gR6<^Io8V80%Kv!(QMrG1)J3EIJPxLQm%Ke2&rO zNM(S7Ah5y_kE*L-;iVQY;j6C`k;Ir>ShKB*~Ufc`pu6Bn zoPQhg$5hQ04}POm`epnd5(Cr_5IObYRteQ<6mqKuwN3cBrN^dKibTz)0{M2rGEjQ& z;D~AR_`WhKnA03s>WvG;O1H_!(R(yH(P)d zNItz8%S#}b^a|L=t+YjpCYs1iA5>$cCGpZn1~ES+!2AgukGaguVYiN5 zMWyd|S6mz$9wOps0K=4lwDy|iv!8Xb?+&+hRF=+%BFqByHi=()yX0!*G1th_nDQ5e zkXav66K95%s~nigTRaR3EGe>9$NV2%!fWZk2yqQ``dz5YF~Jdn%b`)QESv-;0a)rx z%xiWk8vBqaj}^_UDep&d=~>{Rw@3ZgH6Z~;W~=3tURE968;Dod zUf=E!{}o=Tt5l>$m6?&BDd9{+1oW?$x~i>liB5kyA^hZX_r|fM-UOZBhXuKKJTkO= zAqJH~*sOrs0z^U;@3c<(nIkrXSE9dSd4~pmv6bamN)jZPOxWMqUSubXMG9JyPHN>u zMm>>9!v;X9Z?JjKWOq#G zhAHlKy?lu0Kxc5&+?!pzfpCp&s{JFa_s8R+5w@vOlTiQeP)P;u+my`mP@KDER|9MR z>i==g^7vj8$yl1V{`F)8?;GfUVwk0JHDh+qo;-~J4e|4?4M%=R;a6<%Zr-EcmAWEl z4Q{xsbM-=#j*3+K7Kw-C)WEb{=dj$v;9rfii>3ibBmIEl9atTKf%It*Q+zU>$_~84Wubc3j-N3nj z8>W#uKW$T-AU&UJ?nf}g~NUj zeEKlsY^jymnNQwq&h!=k8LR1y;5v1q6c|x+b_5{LgqmnlDo3YeZRs7s6keoGK<{jU zUV63+YqebRZS)+ONgT?wiGkHcNAX=6m)f|u6+3eFX#I>JTY(7OMYP`tkJm>sET~z>evFM?Ij} zxN^_NNn*$g7!I*eRTIeq3bMhD0J{XoKlNdrH;Nf{E1mDP9I#zsYqr|m9uNTuhWvL0 zBt4%>G+P?Nfl$>i_@9fO-?h(gJRb$LP>vVum4Wd8#4^q-g?=N{Z>UXgbjUiXDw-y|gF(2*g9?H&y>%5{XlfwM25M<7F_J7Lr zN|f+wRi8xJqMD-s*ypu$DM#L8A`^4J-bor0xwW^KwD7=-qm_of%p)^gvl8K^?HGmh z*F(Ln5F4$vQOej+upqUH&h>r?2*?DBxdM}^dxI0aMB(3=R7dYGuPwIpn;L>oh&V9Z ztVn8o{vO{LmA_4AoE-SjYnfM?o=IvMGXu9w9Qrr#^@1QBI003@R}1jHAY4Bq?!&6u z^o!~%>6gMR4bR;)7v+1AY#?$U9pj)Ck~6~N1F*ld!?(=1KMv!~E770g%JS*z@r8(s z76J(tE=8LJm~~jCgOQ$of=nXu6LOwe4dHphQh4TrrA0?tDpVQfm6O|P`y%X^F8#AP zN4?=3Yc*;RZNX6-w^>>WT~ zrWe3C{)nd}*H++0Nfg$guRL$)#uiuQ-a;BR3;v8xn+If9cwa^rAVKI-SgL^0SdL)gGv%REu%Lwc2G z?fw`nTtP2i#A<+`Y!=<4&dg3YMrN<$JgOH*)|2!z9VT#z~H2h{A`F?!Y@Y;Q?6iv19bie5@qUHyGbNM zqCoE0-B?901bfeiiH;0xan7b#Mhg@*TAA1B728@BrBoAS%U3xk{rv26d|x)zlA)Xq z;4oSt&^x5MQ@d`;*hoQ6qKe#pz*+c6hce z2auEZm~NMwGNL`Lp+fDUDwWa#3l?}^hNf1$?`-zuWACDIINC2>kW#KMR?27>Bqnv7 zcy6lI=A;9w<5gjUr$!4}QVJInzY;?b)5j2kn}52huj)KFCv%pB;<85Fu*!xjmTq_l^kLu;?_aJvlSWR5^TsTF$h9SH1 z1#J2%o&UI~b!*qVJs3R4qIJ{)NiZWIn!T0qDJKzfr;lYDAxWfKTT`@lKwYAlDn;Ik z=Y|`Q*=WO;9VU3-h`Lp4v1nV(3$AlsD%X0RQyMS*p^Q~(@aTiv z(HGUqT)4WAEfrB@W!vrctK-ABWovC~_U}-0(ZYlnByw9(N8R$mjpL(F`GXTXk7@jo z7=0{gk1cDOOa)&UyAtn{u5TvwLReX^i&hA~z)t$fPTS`cAki={z4_y&T3Ba3077si z7|;(b&amm6CY-tiy|AN63uslzE&g16Oz>WN2f{53pD?Fhy3h|OKvL8+x;;I1H;Eq@ zqv^&DGDmXmt7Z^hmw&1#NwL)bWmF5(I6XDB?S!-0|Fe|_{U!wiWvRXUuhwyJwGF9l z#E*q3;et>*a~gFm6_$&*zeD-d%SBb^){@Q%gq#stv2sL`$^=H+K0Mj?Hzt{_)?;oI z@P(4m2DFc0nJ{&5P4W4@{2@Zri9Bal{e&vff|H9(0B9r)xUKi8A#1DeeOrJh8?a#( zH-Z}^UL%nFVSm%bHVf@%<2>%Z0gU%v-&0|GYjFRskI28~&O!fi7P#X7M#`1Z21O6S zf_fQlB`1?`;nqDn2R>(nnHLN)Qu^FdGGBG-ez;MHbJ~cU+#cOQP??a`KZ0ZcX zv6aVUM#P;uZOy49&P!@Sav`eW2nWX##j;qx=$tn8aj>5&yks_LYX(Be*@Y2f*puK@ zTpT)_8QPiC;=2Dx`9%AE9@v9T*}1?fNjozFR|lRR>NmwpBH5;>sFvy4$ukb`hX%u} z?kpk#=!e9Sx(rPfv_2NFETxI3NSkKxy$kt$RhyEpYcHD5!OGXuTmGhzofKBsYnAP|JAcjnZ$TNHCE?GXbO|HyG zU6cD4*5@(sTMNXx)3JzA*kgib0`uX&AFeR}{pMWHW%}QYq!{(e_n153P2Vapbph_A zS}!)Fm$fR27r`3|+5G%V*GKSB0Yt>)GGJeBGn5teqf88qtL|52h~TB?W*n2}^)8*Y z8}7#)>no3`NjwCZjD(MOF`r}JR4+&vB1dV5i>|b$))h$L`#p7KyYz31Q*%IdEoyB} zi9T`_T;3c!#exyu8EM%k7&fiv-#Nkk=e76SfXP5#M#f?g^-gDAvQY{mkf2=2OEy1w z_|4a7KqrM0T;z1A)U|y2k81p0vsc6f1~Ns3YE{P?hFi9>%~M6_4mdxc3EO$g6j>Dc z-C^O<=eb9wn?|L*It$xt5Yn8jR(lAgb7wxY^G1LPox643E}uxb2isykRk+Xx0q5Mm zof!h4=o+jzf&a^qUe6onXCEXsFic2l{E7~)R!~Kp9E9(aaabHq#L3R;y|#ZRK z@)CFrX-T6wCq$CtNfwFec-d1g_Fap7KE_UFw+@AWepNl;0u)$8UI-uo;fdj~Dtp3p z=E?jI!aJ=$kjA5%3>>8^m%@X1Q90WI%3P)YB@g_-z*fX~ltdM1y*io#&U;9}r+@h` z<~Nnyvy;-Fx(Hxem7q1tNhQ}IUTT3gBx&mxFp2MHDM)psu5&fz*a!qcuqKFb5;vAN zN3&!_*d&N`8-q=rfW3i!JI}v?O&cyEQ}7& zd+=8p!)k{d?x8g+mxBCtjR|5Y*`E`+b^J~5=Q4dPZYr1ZqY3&ql#zS+=Btj$IC zyM+%{O;PD4xEiilneEwR6Qw^}@8;A{iXbIdec#J&jM;74J*3EYm5dlD;dmb{KbBs_ zt=*Zr1jv?8uY9;L**>hMI$RQnzPP1Dycr?+o(C!H!g}r;WCBn;z#p}FyF^ThHHB`- zEA-{}t&1Rl3sklz%cJ>kT(h^okPR|S6v>BnT`Y?lN3#tHR`d9!2G=?GbseB*WuJ4X zgFlJU>I--EEfI?6o}hskJI&-2&5Oj*&${DanxV-P3I?V!BTTHo;ThG=Tn#dBj_V&n zM6+MI;f)yFC{gyGJ1K_@VPGpxfAn<1sq>gxJnAOuS>mBpO**i;Z@(Lpvi+{1q={Z2%29iD^IMC+SPpWMNiAzpN0+YL=&ff($gg=Uo1RITAO(zscUypRT?yfidpz zD)qkvxUhk@;ggLaJrJ(|%NV?;`fFaAT?2L4PFRwg*a8UpV@h$6X_@|i_Ms5ZXDjwO_`BO(3Mm>fO}@Odh}z; zxS+E5pr@F=dDQ7)^>Iwa4glM8L$zycYk^NYQ1gfl>^)TnshesO!w;(~#^kuQ&4vvu z$6cDj5bDNNMdy7djQ9a5**MGwGn~^v^jvjXj>q$UPw$&d#ku44p{X?%1of${Zj)li zs+BiQOAL~l$dJMY@CWSGpWZn0!;Ax%P1P#iJ~&4b6;0s}*904_-%#uB)slTst2ks8 z5OF|Qcfz+i#n1W)M9ji5-cx9FH(c}K;DkAS)v<7j0Dbu7uei^GJzV#66P9MhQe&$iNH_a_l`o_9aBL;9<3VA~HZzZh`C}9au z1Yv>8Ch57vtIzqC8KhQ_vfw+=W3g~ueht{W7y=jLZVA79oHhBA7mg-Cke1ze{Sd|* z9Y53i^Z0DwgaVEO&|_X%7LK>*%g_PAS;I&5S#v;3802_7@7g06^g`H{@XbIF?4p6c z8KnsdIa&fXBh9ko&?dY>K7bKY+4xro=_JEtIC%kS)7Vt5g%vuu4*~E8(hA-~hr&Uc z^XE8JwVZe}Pvaa6qp+iDiXz8o=>Td3(}B7Yy_~JmNKu~&LPHF{3#Vbh@HazV!UfnL!eGk&P8*a2^a^vU-k^_Xr-J*i^~Z6HFsK2iTqAl-OyTCO45={Qs8 z-+)G%%ere=HPDZpdTdPezLZ4k#|^I@-%g8&Wa)NT|1l#9Gi0XW3Of5HK4#Ikwtqb% z0%;6c66zp2_LYvUv)brT9W)-+rdYv62GU5H9nR~G2au`$7wP&no;LPD9%@h^Z+B;8 z94l`+kGJ_Rth&S54c;G+t78;7qJy*^0&@>SKNUcp{W2Zp_Pa@)a0jaa-4W}BvyX$H3Mp?siuwVMH#b2Psf(Qeq z_&Y`z{{AR48L}RmWj>P3Qd9qv6n;p^xLaP3ZOaX-_GGRWywtqcd*k{6NE7hLLRzpt znpXu@OLd?AhWUinHkl!;WuV!FLUFd%Q%nI=ntQ@Q)oZv7< z5E;igH$M9E?)R?zN!4vCinV9kn}WR|q7NnmDXEDcQiT-u27bj-kt*M}YI!M;Zo^$r zBSfK-kVkqt^G7T#$m_oOq~*9@35s~&2}D7TBMPCJuK;wZzYF{$>_*sX+LK!Z%j{TP zStKZ7>S=6jQ;qAS*e5t2K;1CB^(2CSL}-Hy@s@zkkOn9;2qP!dWt{r&*uxbb6_hZ` z4^C5Eg!4GvA??On2p1-P?8BdYxC??(&I2%u<+Ojg{Uv<1KK*lQR(9sQ2F%e0cMqIJ z9i)|94Xpss2%RP}$WJP8-dSHYT(bhi^S%24_Z5kV;9b+~h42L%sXFb(Wkwz(>ZLQ5 zCo2HB0d!$U((2OHk~=)mu)!HvT!xzXZQczA)V++m z|GAowKX*)Xma~rl)u}6cPgWPBQQNpFuFo+U(mR~dab$LBICMzH>2V0S=jHNqNvz2a zybPv;(GHwZSTo}_|0`?}u0vphwLP?!TH8ok{M!ovfZs z+C>NmqgBO7>YV8s`g%FP0fK?xlX4Gqy1W_seF<=2gV_aH=2vSI7C^P(8vT{ceh%e8 zLVssA?(}7)bZ7WqSer)9ysgMds#s%x@P%lEzUBz;DU@R50%N8Sd+w!D^kZqb%F(?6 zie2RI0e$eq!<7jz;ttI@8t1b}ybf2DC;$YMbe2a`-##=liB)PJ)4~ML!`dD}ppzf1 zWP%i}1t*TFOBSvMZgT@y&s`yVk#RQ)K9zmXgA(SYRllAm-bY!oNuRUDGU%Gl&k@IJmoLMo#k^(vLc4Q*bpP>q5Mb zl?X4Df0uJ@ANsCLexDjq&YiA$n+VW8 z{iy}ew!lAPbm9!UQ*O;u%80ng^-3oB*&343#l_P>uK_zyGYZu|cIGb{ekm!%{p zbpbGx5`?bJ@^O8|7Tp?h+Bs0b zGA88>rp22DYy$f9oJvzwq5XgM*S@( zW8?D?TWXR~RxGYo^ih3f3U3L+bdmw*{m;`+4sI8p)rP z)ZFT#L?8Orw4M=tE7MIo@5AtYC6XNeB-lGs!**HIb%hrFCl4Ndf|yPq1j>`#BM|UH zki2m$5|-&@Djzm8_RtSM6Dcv7lwKk7|gT3jW$puLfU(IwExDa7q?wSdv0T#`lxQ z$vb9NU^6|IvA?srFmAl2bn?H?6o{9Y*R_su$eI0;fQ)i@NpacD&Tn7`Z_?@Oy zv^Ek(d74gjh99gk*s7gz4tAy4ZYaC2A8 zfU<{;^!^ha7B;z1mGefw4bPhaHplHxnjgFmldqd{be!MxjV8Mp3PdvoF=3O$A}-#` z-O>>X&RAGZn?;uM~EE8W-n05@R^A zAOE0bd@c*AOZo3JUc7hD*n4=`ZOUCQ7qII8L!5n_I@_v%p(vQs9r!wfB(T$LcR#Vz zfAK!za|27D-;f`aBQ6g7A&GkG@`hDGrp*f63}C5R9QpTmyWS|zLNkr!5MWDF379zUV>=VkW`0k-?) zk@IGKSfwiR6IN1=({dM}c-j=o^cET#4)N5iJ1b8DF8TfWD2B#S5~f?(4)Gk=_wKtK z0YRX~*0$_Y=j1zd4CGi?mEtUoy8;UU(Tmh@y`L8B&~kv|!dk!XW9w%sy;<;0c#FLe zCJ4SH?5s(*Wd|D!5soNjT%?}C>W;k z@?G`il@#Zygrc9nOYujve8Ec+5fcC8W#Ccw>mVDBudOu_a9yzww%9%fnF4|00~R&i z8Xe5^P@JP0aG`&4TAZbv{Kw8KZWOkVCFO5j!vNlS zK4Ad^F+o!dTAkH{{ESnrn9`=`BeGt)s0G%s(TWb zFp*MT*Ny#$#G)7KMV%D_IgXYP#HZICXsyw~#{gxW_QR$ZA5pr&O` zOI}mk{HLV}r7GnMbFdxk+{-qMDX@lg9bKil>m|P;efNeFtDd-^(D!${1Hz^KcQl~=4QyE@XZj)sAIfM>!VG~w_;e94+YEDe zV2ujl_TU0*&v9Qk^2pxV^Jx=$9gYRfZZ+ch>xEl z*NqW zumgmu5~wuk5qWHTCpw_AVhccBW$8+JtX;bzyMQ|cc+G+6!aZUm?lJmY<~J0BMtAN6 z-jSCQ*J4(<4OgjH|G0C~?6uf@I#XGUL%>ra;C81T_3#NLNQPrmv#EbPP{NQ0o3IRd zX$FCsmzbDMZcvs!v>ELikN~1?%uQ`T?xogg{y&uJ$VIMFF0ai?2dOBVFPurwN(H*@ zkI$}d9RsYFZC|-c=Gee$3G)rErn$)rq83{35Y)s#wmIGtFXhqGm-#a!wr>FatO3Hw z(wnN+*u^gi==0;jUx+^>I}yPnvOGMsmb7==?dTEDWiX5=jOu^Jf7D*jr-E~Vj8_?9 z|16Mak}0qhE;f*JFvAbhPkTG1D>Z9XmdrS?7=C+tKW>b?g~84-T60zH+e3)7W4yu1 z+B~!UgiPS$SR-O(IWuzCv^zde6ao;vV1Zb9jv+Cw+`~ zJVh=SQv|x+(ihe8lZ4aV%teOE zg$j8>=;ym`Dx+MgDsm9rwA*7~7L8Mut&H9JrJ>wj`|AW)YTMl&_Ojm>9{4$35kde( z6`FkQUvDlwslSKP+Q3Gy`YLZ=H}orBR$>afXpCq7tjA?F76f*Bvezhi3S*`?AjU3q z*%$*oBe;P7vlc0Pb#jA?u^l1vwMdv}Y^a^$d__I)8tQyD!UdN-(`Mtdn*GyaR3R-$Nz-Ai$!|b6vDe)@?bye zle=NZdWGu(k#9TK`rJRg1ThFw1w(7$)-$UQB2-wZVI*stA~QoUgLmv@8)s5Y*%X=< zgU3)FF`oImwFE;i9GYVH4{eHolx)BTv7FVO!@ZI0v3;#9Yyj=kv+tu?6CwF+*vru4JlV#uL=Lp9T~h&&{ob+PXp5l&6|U74Hof3BL=v zo~=lLAY}Q0Z-qwsU*>1b1kbXtFZ(yI0K*#GE^Ka}Hjs6NEN=4a-Z+!7wvDSud*9d9 z5P1rF?y~N1se?aGv16&_)ISdm!b_6}NaKIVMsL53lYn!bvZZGfv_e|bF)CP& zEhjN`Io&(Ndki`XK2wUQdYC-|H~9aQl@GilGRe}G?H2!mhZrg{Rz`k~JC${Yn--;V zq(?x(7CQ9ozLphb4S%f3Ha52Rk4d)m+e>ELh1w3eR3`?$u&130uLL|k#$0TWG3grx z;cDzw3y9krjx%;u5m@A9Yl3EgsQiJCQZ1m*op7f2+vI6=8V1WoAk1e*#@+MTdr)?J zwoPHUez(nBA8m4{H0`iw3+{6OwNh9Wh>C@!Bv0bsB!upJa=UzI(nH6kH+72oI}J3m zG*tjIR_U1H9hADJ6Qg9mlP=oXz3oOC1#;a(gR$2V8xAhB?^-#bpRnNk*z#idgpBQ# z!1#VKeI5IKdf&{4OVBb3^HYD<6#F!PPx`RhCAP(YVzIt)x5*_o*Mc>^VAB;V)DY4t zteX^UA-w$H%liATFdL!Z{2p-R;Few~Zok4~ZQof&u?~$JkJN13+3}$9qRZ_l8dvOm z`Q7x{LqmTe0Qt{At!7}7|C;0du1_y1yq1{=>UBRtZ*&VSQS>q&t8@9`Zv}d1lov8M zb=pFMbvz>(3QRcf&JRAQf^A>-*!THViHQ-?so&*es3`$#pdkI;g{&xY^GH9U^J49n zLX8dQn456QtB<5Oi4a)$7AHFu?fof~h?L3bxzbRm&9~qsv{nXzP;u5qI*{ONPpvL@ zrwT;%dMS+ct!5Y0ht%_1x}8XIRH&DuN~GnbG`VRc)#*~DKqvx-3$pWFC)ElMP?(MHLkZ8zbGp*n(C z18C@P@oqd*8@x?pI00BeOfY?n1I7rZYF)Q$sMG|EW1gcpqAp2215P77LyF0@(gH~b_N~F6u+wq^|gHr|KPcQC+PqBZt*RmFrt)dyV zm|h!Zk4&O*yvTPyONoW6h5C(DgnX7uvNTgw3%n(Hwk)>XuRF~ROB14Am15C};L@VX ztTI~3U?B!Ap)sDNqs5dXJyo+u0N!)*qp=o%uN$PwCmbV9xrK+Jpha}tp)@JRZI0X? z6ONiENp`D2ImHzX7}NmR|F_7r0jO zjtw|f=_`w~HCR)jcf$Mw(jdN^sGZRb;hJ1nhr)dhs={E=$A6X)jDcLKhBn}942n>x zNZmv6kmc@fP&)sJ@pU8xXg(Wd;WyL7tTFKyK>%CkM1Q$hm@?;zc~VILh`C_SK#+do zD=W8yK|p=JbzPDB^$&@mIl29vhE+VV2qu=>P0ooTh06piqw9?+kOUckq-9jOS+dVf z*COpy!sXz)Op5({3kiOlJ))oSf?QM9ZTJh(f1YPK%@v6|p}bHLY3)pwnR1g-|C+r7 z3s9#9687xr-cBbtV`jx;91 zZHOtCH|J$}@pO_Urd&bm84#_B2J#!)5EUEI=_ebKLgFA3?%Eg7X1F@s$MNI6?1#U7 z2e;3werO1SV*i2H>h!zyv z{m$$qUJ-XL>2q?{h@>Dlyabbw5PT?!X@taTHQ8MXzinbhW4?ZXwZuGnvA<}kk`6;6 zEM$5@2&(|v@-&EQIx}Dn=`@5V%%$P}nA!Yl>dlhH3z7G5?!pr-ePE0zM~(5m?YpgP ztObiR0o0Gj6p-4ymWe?@&GpnI$S{sfaPF~@YWm0|n`9ZS$jIUKUi z?w`gi3a23h0ewg?mR=2R7!vy$$&^hNP4qvQFD6=EadV{jNahv1tNzH zmv18xBImWSgQWw+7TBqzp0=cqT!6olX`E?O^W2=4s$WCoB>(4+Qyjsu|5jbmyYIfUBg$kvGz%l_&1JqMKzPTZDI)C9F zT&|e&q=}k@NyWE_+yC;!gj;5QyIkm%;EOv*qhd!2@Y@=Cb&Yf^X{1Fpg2fHdEd%(O zGp=jUuZVdt5))QLS8}#k{BLs`?h{A_Kg?;17hdm)Gp=9evy52+^M-mt>}mq;jX!_( zqtmc0hdtRuhxjsQE{TuDq>08fr3?f9@-Zc!DTGuTM3n|&Ekm1w@4%=GSNkcgfLD`Q z|Nc9rF$%M_kj^V-iFQF5KKy5uwUl$~PYeF>2rRPK4*(Ji6ySIqH2nKQ@{cEqB5@;k z%n$Uacqza{g@-5lUcXqssQ~XWHz}3ZcndtWW~!8h8%iVe^;Vb<@XDF-DHCPysMhrM z;bA>NBfUC2aB~drWs)EQj23QdaV*X9_a%?Mf-%T1dp`Q19ueS;R!6?)mA37C-w|N(DkfVcsc+Y$n5Z%>)R$+C z04$HIvemG`#weg@WxO8bveW>6AVyIjWi_$E$bt(YaYn1bf|uVRYY3o9VlkRe$F(rE zAZ|IvLjf3*?QTuFHTon`-beIBd=%pj8gSqkgoMn~wUHrCKMigI@pH{^97s*F*yTK-U zkFSV;fpARfe^t9 zq=C%t@fFin~TYZ;$JwN1jv#EB!#z@LP0rpituO5R1NMT{)U)%t^TVMb#C>7|yt-~!03*y4QxPe4?{NA<_NTLi;wbgkT( z;|9aTZv$zQYoN=oac{g7*O{v~$}>-L%I)_V^lWe1Z(2f;7kHV7;IHk^crUvV!TQDT zz>V|!58&%bX=`yyOX63m8b)?b^nQPGO6SA$+%F`2 zvqZ(3=IPkpw!MD6ad?YZX5;Werb_7aJZAT146qp7NtbOdk02BWCoVFT5WaX!k!$Am z0}6;Ts?pj6rP7I)+e-bK?i0Wb%ZNIYS3T*6AT$D{zIR&}tme@7#M+gZ7aDZGd9Zzi zspN-3G-~zSj#-(&gE_gUZeE6ck4gHU!A^D*N`2rU7f^9Um=zG@w*HJvMIG&wrQ){t z!e41=2*Gd$*ytAXR7YF8k)pwT7zigeC)mU&BS^L7pQ+qe1|%ptc%IX4V;ebM8FlnM zSIY~{8_L+t@$%v7$QpfTXv!6Xh6KM-aM>>wv*8vjocOatK2H2@*hsG`7Ea#=Tmq;R zv-5ZKnf&93*)4wP5HvybY(m)mU(G-DzmRK=)4b=5o)rIuP8=#4p;`s)_`%H8Naf5t zPTn0mUzFoBzviS_xyMlL^9l-fXV8Fc!6lA;!*a(0LQztsA-%bssTFFyxKj%*+pQ0wpA#~1 zd{Ru}nzL*?pFX93fizeJU8e7|Iow#ZViT4u2>iWh{Z~w?psEQ5b$9$&4~4O7pK;Wp zE2ob+z*MxHQo5z_F?-N`WcT&kE|#F>RWJe}#sA&*@Bk+MXkm=`J^H6aI9FGx^q*iL zr^>4@?3vG2C&97MVP#3mVJZdMDP zpzPDP65AnAy?d_#5<2SFd14ksECqojozv8w#dW$+b6M0Z8Jz7Sv@_Zp zBVa*@FDAFN{D$TBGU$c?JDC3Xa!=v@YE}(w=CLAq2epO}e>sTlVOmsQBF0GJ9Y%H< z<~v!}Y4UR*J@|eA(T2f{EJ&76RkcI@?j%V+W7O&&>>gb_Fgry~F3cHm6|z*`*^jZ% zeK~rMIuA>ivz?HUg)T_}(8!|XKD7Y_y%QJCUjKPhmq94UGuI&$zTC~X zkcB=Ro-NQ#(MAg(O)jQiKy;RIA0q?R2A~*`viu~$EqEJn`yN~eSo$)?{+?9tEM7B@ z{3gLQ2vR42RXWP@2sCAZj7a!=a1OU&0OX779T2BJcHn3VM$lvM1ykjR;{hT@b|l#Z z(#x@v&gTU5Vo(JGj}>H)`ReVI$Lt0l5-mc_T^IR25RC^6>UqvM|J>+}u*Hk|1jp~> z$~a|lHT{kf9+~Ly7^ek;ej$l7-L?Mm)r|!6ky5e_pkawY$^S{oG{?@aP!wbsfZ3wJ zu%z|WB@K?#$V|#3M;D_KP9vg5$N)D$A>^#Nr1d9II;qu?4I!@Wr}HG=wKa@g&y5)n z&$xx~EM!Bu>La3q?l=U{8RB+DM%{DFKLH8Pfcn7qErzx~iL~!7aH*!~Xh6Cf>hnOh zjEq!8-&GB0Vg>e=EWw0{UH*U@T}Y}P=m(ei{2^=cRGM1@teyX4G4_vgAwUzXyxI!0 zeH{n90Sq*s-#xsP_I9pP2RU7-UQOEU*3d9$?hCy8SM~Al$=_m?{Q3v(a!^m@Ly~LW zIJ!@1=iRT$K!nYpia^$7W+a$8O&L{QNa`CHJ!ej-ZG+}rXjQ^(z59oGblcp+q|t`A z%6pvn$VJHA;09=$kk=h_?*pc*4xdh~vO4RLUlk7~+3{2}QscZ%^d? zFsK?-0>T?WIufG>>rbKx2icH{1|Del=P7&OLHK z6q2vHeD`?kW8|2bIoNJu!8MXfwBwj9^f?7DSs>@?QwpE883pp9Z}xK+`(#E=kuYRai6GCR0Gayc$)o(?P|I7mK5VYq%hlDTcQSF8RZp{Rh- zP=g@O)2Zl}{!nv<_~_()WGzR5OlZ&zwY=9CjmaW;{$xT_2EEK6nFvRAHv>&}ciwyC z14o>X#e2iZh>c9MEh*lL@e>VuNWhxQC? zm2ea`&_+k&AVaa+IdMEGTw%r}F3)k}yFYcwV6NYvu?oBL$=;EuEvO-w2SgC)I>6K^ zg|jbO7M~niY?K!3D+pYVTaO0}zcbS`7yf8A0*5&5QcK9zJvRP^s4$=4Q$=*qpEO6C zqql&PD>RW7OnX|!`J*rv_9tokk1g)!XCQizzHh|vmWrT4ou(nNu*<|CvZ##VAB(Yk z--YijHT*lFGKha{M}QOeL?<#y(oYO2q5$3@m`Y63_1oW)0y}NNDzdI%BTFoKBP(HN z_){MKHj?&nop}DNnrqXQ{BWLk|GFJ|$9*ie_Nx;LkK_r3rd*9*R`$eQV_KYB&>6l8 zhG}-U!H#Y!5aE&S3vPrIbqj{)p?1#n-PML?m+8iG^1S;M*qXox#nHbSZlO%A7mc+N znLD^2B{e0SdPZS z@R4;;_KtHE^`uH~KP1Tg?0K)}lJBz5f06#$9(BXw}AC-rx${ zQObofMT?u_FWfOhJfzHVQANKalArk^CW{5)nX}(FV~1Ub6$@oFy3_q`GcO5rnoKfC zjlbG<YZ{NWpl@kl-|<&155^4L z)=Ea|Ee$B8B#kVmq|n=NjT|Hevu~<{tD_y{^V}Shs>-nfG|? ze*F02&lrO83+0z`do2R-cVde}9%8Du(BF-)7ejlwB;G&+J?$ew+GgdenD#x?mF50O4;sn6x05 zzyI*{1byM59Cuq~J{1(90A-2_Z9_X(j?He6544B1)xc%`&D?LDmbBIy_+#?9;wthz zy-0d;=F;i%GQM`p<=0cktjGsvzg_y3|N3l%e}|veuUX*f8nv|4BU>82i}@Yb=rpPn zgZrogm1ZQx8|Q^+nX{0$6)|Q0 zocX$x#W*SG&2|N%r0l_yR@7Tvh7-pdzhC0&denWGan8r*CAGmqgLi22PnEO#2}NCn z&K-x_?%dlktQ0UR_@jsb9>UC2tr8J_p39QzPC8#jN_3G9S=MJXJ| zo3Q%nN;!l@T+b9sA59{x(*(%>I2^82)!!goB2KIf zi_9zp3pZX}x#QrBkEW6Xy-lc5nj^=mz9&Ci?Hge+bveyg)PJ@~r4q?S$|i9u${t9p zh`K6`#Y_c73+_y%!fSDL-_4iqmeD=8P}vKwm6<_X=b*01gA=qS>9^WZEy;x3ec;9Q z6aAe44mYqXk(JUA+scZnTc=w2n^cHTQM2D}nry$kk4h4Mt2KZgrH=WFh6HUQ5Jx#t zB{d%YEGJ>?k*#RY7qe_Zt)R33DcqiK5l0HT;1nxsqr+_U0%6XWjtMt8MZQC4yx?n3@fU}9gs36rX0MCsXacYOMI6sIQ2#w`#NJI zjfi$&dg5AE^Qy(C9}a;xfAivnKnj6oa;k~aLH7E$%{RjD*+1-%1vH00j@eM7TZj3p zRhA7De5H_isqkfaNs-2?XiL|6yS}wRAxv)DS2cThgul`}$Oq-%?)N9B$VlV;M}kzU zY5ai;QN!r(0ZnkS0Y2jg3-*+^qe*<=F*6$KAh62k&Zt6jiA9p)WJ`|0?S@Kc{-heE zG5d=8rJRZ5Y^GIXk`>Cr`^RW{s(7k2&xuWZu>l>iJWv11GwXB4qg=@HTq4g%3Ps7n z$Yx>(*GIxMnsP2wsA->Ce$MD$%ZVTal_8mO?2b`iE11==6ZI60AB~qYCW7Jb4O;z+ zlp*({K48;i&%!85oY*R!9iL&Z|Lr~EE-nw6`L>n_|xFN ztk^f@#cp6uD!Oj}${2b)HtL9Aa+v62@~2?-l?Y-F1zC{M8@xtM=EG$Z_zl0fz+Dwx zNXZS{TJT^wG9JO@olF6~FgC96IdN9d6R{U#jw;f;&HA1^+v@S{*4TB?d6-P;;yzN& zKdnJ$u+fMDe@TB=L|MMJLBMh9rQLSwD9>vBm(KcD^8S~zYwn56*`NVZa-uRX^{K1J z9JkyzxSMdVA^lm-%hSwAG$tqXok&|#Oa&DJrHq2POd&X&WUm$T8Bm11}+T< zNt#uo8Kx03e0?7VjsVE^2iH7I$Vo`4md5EEfvrU4hd?UL?uY1#fvhk zJp0%pie(GFvpmWj%Ld>E?;j~qQn>k%fFj8~-SLR?D`r-?=^L7jT-@Q+rSS{ejAIG9 z^fq?(cCTOrj`HS5j{@+!vpftX@E;M9$BZ%sP(ryK+^%*OCiYKH-q zhv(R(1L-!L;gJBRXdu4f@7$k!3qSNLqCt^u`pv-OB&XeFZbjPn_Ku$QomQS!81J+x z7(ToADY4S)7`s$9_PnJB$N>LaZ_2^Y_s(=qPjjrGcqpBlW~$2RGbt$gPhU7sA1m@$ zR^Hd5R|mK-#J?Oef&>I64Yh&qAJBdBB-`4#IgnsNiub|UUUBdbmk35*eB7@ETCh_FIgY7CywlY3->CcwsP{nya%a))Pi}Hj(oUb ze-D^E7EGqJbkm_UV#WH^k&l{WLVGTgx!xF$XXOj@;4w@Iy5zFRC=?tGGoH zVLK}OOaCYELWWPO<_gqzV$Yssq}BK-MCyisfR#BtTbj)r;lnpFOm>MCpOj&l4DPmL ztVC8(Mp(5GB3-zRtPq^bTsUrL5Qy506^Eeyx-kgcoJnC#Aml91FE3p*woiMP;wy>P z&i_DdHSjnr@ETd&bQ+3C876PPj}%;Al_g2k{}LCQ>^f&(6-Mxj%FDFa8spNd?#19L zFHGQk4w>QF_?N9lB(|}rlGm&==VO<)azB%_s7zp|lX^di#fB*AW+4Ac4A}D_T&47C zm4TJ}AR)t5Jz}50xHSA=hEiw4*pVnaC_I96+Pz{e3SmDwKE3;ji^w#?Z-d1O`|~dv zsf*8{zY0Mqvw zZbmT7?v#2AGJX7DfL!hGh6p)R-j3LnTLR#jMv)KNV6R$nQ9_E{O2B!WOa= z9#t$0)SPGu!;lBHOcfKBNV-M=IGioojf_7wRsg5Y;qDjl9hZ}H{Kd4+kS`3yG@wLk zR16!-WOb9Xy+CdCRd1>uu@2D{Dq?M-H^+<6BCoPZ?+Y%E%+^&U`Qh1ICwj79 zI}sWD18`>TtNW7-$pnfYH!WZ`mBseX#PwGNkdViqB*e6UnJu-+nMBt@;C&llhDQfp5rIh68>LyYH;>!l z#CLRA2@`hLCe9@Fys)ceSB@a#8WAYgZ-Skqqv>e{?vm(y{`RmS7ziEg|L{y*3n~vC zHnXp>$zFp$D<}T%Ggs*gvXnF+heqn@24VF*b|rxKbUcuw(MBap1V%K!w?%QKpUbUAsS1XlU+dk zZ|`1N%`h?zwi0yGNFEh1bEv_79{%m*y<%;aYDTpYow0(m5^j<2EDaCtbWm0#h*r_* zZH`xi6z*^hhA zqjRI=nIwrs8HIq;UZB7%A+%V3;_l?@t9_l4^+gT+LG<6hOHcO-KN%++&0?b9=!@e= zKA`>^weme@0qwI;(52eCaXG)qN~!U6q`JNRVzu)H<@?2NGId^S1rM8(nM`gtlBS&s zVZn)q4{&1I^WcAAyM>qar|-#b9mDAP?$F%$1&BWAB9v;p32M1DJR7YU0r{IHs9e25iXUH!KYLFgSPQG)Yq42w^NF1oagD#CM$D~r0_~}QRdu4x zrsN=vL=7N+eQq6Nis6^BrwAOio}TU4gr#l=1crI$Z`1)A?@wQE9|3&FQoL3PKn9fvGDS>l8J`X5mer6nCfnjjlz3-3MaOy5lk{GbB4|?63*15{RRgsGATUl1yrZgu)O6 zE;9CCC{M~TC=E4U_H8Bv)l4BM#cKu;SA9G@_M*be%`T%R4j=-s&gras9Sqqc|3aAs z`yZ}P4oWZ-Yaw+d6*?VibV0ZpBQ!oJK|z&H=668^3Qe`jAHm083Z!|E>YxI;C{Koe zb5096&ewts{(yM2HA0qcz@T}}x>a23bo%$w^OSok6MMxL$$sg%7a$CiNmv*8Fu8JC zA(52<$5^F#9C0nAx7%}^hz|nHLPP~a#Vn@<7mtkh5#|C(cr^q{Qk6`!tWc$5-`+^r ze9GVoDrjzHKWNWS5G32o63^~=LuZVlZ%P_CL?n45C`5@%G?y5DT*e~b?w564boEaG z&;27A)Oec*!R|YbVQZlbtSM~Hm!)m2G-{G#jKY^*?>`eiV^*%ddR1WycfF8IzFR~} zp8^#$Ri^+s{uuLkq?8>sA1vI5SpQ*03R(tzd7K_}4Bl@NSVWU{4m_l_DEy0&ASV0U z5dP5YjshC&d?1$TN4v@L%Hz7d*Bcpz>bIxJwJu)lXSTfshGe~c9;{+!956^TSbPc# z16ro`aN`ykHe#HH*@q!cg+`9fTZG*n*wMQLCN-a@e>_4l!n=IL(nYio!JW4)Pt@2P zW?#Q%E`^3){jX{kpS+|wcPO^K7=XUgC@N$*=6|%Sl$0=`IDjy#ht@8K%*-uXqk zAqX%8CFbt?-+|4~S1LaZMh?oV!qtu+ZtNrLJTDC?W)RGdqJx{3Q_&(ry zv)>e6x-KdZQ`Qb+T!+^_yR2aEBV|aCn|53cTWBvWZjL)OOvo`Y!brpt=eU%d$xaO# zynDT6$GGn?`)X`rq?EsB*ck|^4mG4TeCyu^6G@uHe5_U%`xwi~87+;DxoEm$$(Ld6 zXzao*OR@4J4i`SiI@0cG6=6Rs2=Hi=_5E(~A)+FoV0G5X*j?wvo?+{2?=Q0*pKcid z3E~0;+cxRGGq*6C;s3~Eg*|~++%oC%cg|buGSC_gHk_`wfLRDim1A%^E~#>5!E&#| z9HT>fT>yE#Ab0ZQtjhxLAMkb8{_0tU>S-++mEL*VN`=p2a^)zHl4neWMTgxxNfj^Z z7TkfeNIi|+vz$J)1x_#(ZG$YSD9#~8zf?aiP?q(Ly@FEPQtSf+>NOlh^n>Jn&DFa%~}*dU?5{*O@t>929%KG)?(`$2Wm{R2Wpr`7c3& zj1@r6UMVR6xVVk99ZaUo@ZaN64c(?MvGpNSto z3VC+_1kq3pl-9;!0>IkA1{gUYTTm$-p&P;UZ+;O6EGv&O&Q8R9f@Qur6e#&7)at2+ z;OR3uF-Hl~x0~4QWGuYs0og9epHrw8X!AbOQ>m-i3P5r|JHRY6%ZlRBo}*)QoLA;9 zoyRS5fI_0>kyLkc;$kzCp^2u5hqh{OsIev&Ti$5@^P%M9xw~u5anFco9$fREwh;ZI z49bd$*Y6_fH7B!E#%se1J=r^9V`t6};T%B)k9Pc038AaM$$Ca}K(W7xz(3n!4xNXF zIahh)9<&aA<~iO%;xFwATEkBvtIqDR*sr2MDQdt zdxiIQ`r=UmxW#qda*V~Kugge{zgs{yy-;6}sRN+?^r=J`puen9Y4={yl3E3{gcRoi zLEwM-p1?lHZ0I$AjL(Z!Hx;%ZS=rLC$94KJnw#Nui^do-`V&nLp_F>wnujR8k^{li zbYv%n^YLZbYIQv_CyUI=7`;wu=-_inl1ehPM;j_RgYdtnS08AoMD4Vot3p?@=WCV7 zw2KAsPSid)Xcl-HsQ5n62+Y+)Yu`=|7!6a+B}U&ne6_49;7pXz*p@^jd6yWVJ*|A)t zZDu1(mhF20Bt1BN936;4PJ%dN=6=7Wo_$eO&8-B1V_M-rK4VjHD-la92;#zq<5-rWUXr|C|{T;E!1FzX4WLe*Rf?asbYuSSu8AS1(kk|xv;H$+~^duTGRrz9($6kG30MRr&;n0bDS>+~d#m^Mg7( z6OTpO^D+)Bwqi6ii1$jEj3X$(96T&L1*gpXMuqdxy<^@>!3JywspHI~8d3S2;itw= znr?d?Coau#7$SxJ%IE;(eN+x2Q7#DMv77)o^k&U)06vN1k?rj0#0$*ONocysiu_`$ zBYXo*ULu6Ok0xN9n5-K-b_V&gkF&F&siVYz7#)b!9G11mv++tKv_!N$b~UY)kY_5 zH8$vO-NFG$=*CA3dD4&PH!~ZEM|-3c);&W?)gc8gL}lXedjQJ%Y-d_?EZUb1W}d|D z3(PyMS*qX2fnyg>q!?r1<=8PHA7A9$%)4fj{Ice#dz~yBy~P|4IT-7?_u5*;~MT!+L9G9OzjxrDdi*l&j8-^m33y!!Qw6v-X2WMIgGg@Z@6do|^Nvr~!_ zH=lVTEM7JDu7BeRJ8+)pBQM8uymf~AFppX4xBf4?5@upSRCNMiNm>YUTzI;@M$V;* z8-^1GdGyTAs||rj^jUVLBAY9^y5?f71XL;4U$s#6dACnl<77<+mRwqmqGkRp>~2o7 zZ^RpR-7ZF6Nr&qjwocA?6{98vA%T*~lk1)020B##0`Bj!FQ767jKbFVWgL)&uusu9 z9)9TV7=v*TMO#}BEp6!ZTn*&5{iue%$N=ltZo`fNN*HV^2>`^7pHB^d_$S>jd9C$S z5<@mvzI55Sdpp#@-=Gvp@N3$o3lV7APXG2Iu&pq~(6i#i*iY>wbc{)Wgy*=I?!Zr* zUh8(p>G#Qm8`Gpng=a_>lNi7SBN)kz?_B~@luo8-6OPW)rVK5=!oIh3ymA3?c+k&( zvDw3P%HzfablewuE8vf&rHK=(ZKE7pNxUh3hZbne^fd4p2riQRHILmMvT?~XL{w=h z%eklF$LZLo1#OrnR|AuOZIq=oya>t`)wcf3mW2Bm%qK>dh+d!x?6o{o+V=xNIxs@u z-GP05wX9fcSu5)v8PK%Rf5~GKWWpMZ1;f2VN!-+)8AF^$iKsC^l#h}qY9p@>{ zgmHW~bhQ6&J_^vU9C+m4Q9sRe+~VB4ZbCtE({S!p)=%ifaej=!GlK@@6w;od2qxHR zyrn{>JgcNKKtz=@6v*ldE1&Iv3ldlK$R2L+{=)H?Wg3e|a@mXkS)ioV;-*_x1q0Qb zwv|J0bydS7B{^->;7FbmZjd0)~7SqNa8c0Dm561}OT>*Tlff6FVk(`4Ei4-%5Dp^6&0d-i9 zUEKkmJImh3fG(nRK5_=6O2Ye&4On3Bn!IY2_ls2pMejYB!7#9A78Y=Pf`P& zzi`sF(jg^p%B`c+-TS+n6P!Lb`y}$SV)mZqoBHZG(2D9gU_O$VED8z7O{5=5o(z!y zII31zHPBmZQ1Aciu!T+cho;jouRz96sz?~`Y;Q=h#(9?cCf~`27APx_ssKx>w_+^T zN|95PsBjXgY^sBL``0b|{gc^OxTFrCxHg%7j=YLU(zf86o0a&zkNHk|zjG<3spzwn z=z4sU=?Q-tM-#mheFpVInfe#-xvR*mCBKOVgZJvE0Wr8I3I;>_Sd};=I}Q;)XZ=Si z%ru0KvCK>hs>t-)#tem@{~c>O5N>$acQP7k#Dm)(6t!DZm;Je0;SBw8F6>|VX=$4y z|9pW63ts{nVXiU9#ou5>xqnrxef%I#;r^{}8vV}M&j?EK>E(~x*V^72C^m4^oUhW% ziE1TSXWRA)CK_by+D)$=RJF*#@(TbSw4Ob$|4YB7yd*TI2v=aXcU6C~~DJA)-yxzbGhb%oreHOSl zGM)78b+cbcJ+_h9LEYP&x)n#YO?-#*bng$MF_TE$y}f5T9Q6UVwkNkWRc$K80|jl7 z?+0RtSZ$L&o8K;|)u7B_|En9D3}}iN9GgoH2|FM;9UHh(B%D70fjq2X3g z2TsvJI3#!(-b!#fn6UdF&adcSDje-lI#T_amQ(p4S*(t!E@z^hDoaF|t;-+VTgoFx z9g3{K=f3$`L{j^5hLB2!X0$miVw(;1`4}DO3^1U?oy+u-i)k@zo2bz z?3%ih5gjn*4$rm~KIx*gmH6seifLytrO5 z$};YCWar5x0Q_o8_O|W zyYJPiss@Z!sM^AjiZTqXUY=lq1q$2;Qf5@!MDUvy5AzNbudHk*t#BOq-kt+~F=n-Iw%A~0whFrRUFl0I@w|gI9BNnOK=f96W5jb`xF+74n zc7Lt1Ct?BSJiH*|BGpXSAt3v zhlhAMZldOwZVvXKLKwb3JFTTO1m<){L3qE3#P&E^dfEYGJsVnWhCO zUHCVbl|&06vY*co&Sxi75z)W^li!nWT{zPOMvOdhSCQ6atP_Simyomm>i3uy(ho zymsfPnmRy+qIiuD{iAZ6yGiwUAIKu@d7&h8+zkbvll{5>Q_JI#Zp~{1V&Ty6^==Gx zr}(iO{}Op$3_yOZZmnS5YM$8B#c#&2%x69blYZ`8Qt!Uaz-u(iPGE$OX`wqF8P466 znYyR2d;Y`MKzWvNJe%eH&$h#2Z+7UL$8xR}`*91@5K)J=0dNNN^SjXeha%iLWx%M= zSc;b8bKjAYoolBd2f(4g9)DFfNyv*-vS#1sfbL~^{irDaX)WrZlv1(^Fw7|lY=%5L zc9O56!cQ5EdDqeSf2}-Qpu;T$tW|)+7}#UL*|I&0coOWP9zigZFY5}*enE9@#w<-V zk1W!I!2`xT-AG_713sB}7xrAqQsK}C#NAB@-Q;er(Qt+sQ*1*lBzv&U1uoIPAC)C4 zK*=hb|3cKk6)7nyidN-*m%6gcEULHm(4ytNa~V67V~|5Nl*W2WriRPzM1s>^SB z?IVDIiA@n^LbeiI3O@N708P#yl$`U0ZqsAcp%@?%I@Wh40J(;>KrQi;3Wx*{!QQr zbhJMEYrCk!mEUa+C4DUo7-y@G9jF#MsRT3IFO)59nUtnz5DO@}>ejF1nRyzLxkMYj zzLS`IPk-!%_q7NwzF&#qIk&UPe`g;xz=#6L?pahQFgXm^owbo!3k#0y_yI92fof~@ zdp`C7aZiAkLNcLWFoJ(JX7mew#-k%FaBmGFSG4%nX`|)sMN}OET+SV)byX@~1q6%` zCg9JiJ!VfU1i+?rO*tev%tB!m=$0-WDldIGoyslj!9W)ic*rgixcwzqzuzge!UH8s zvo?Wk$uerNta8`haLWY*p~*9z%msVkDbfulB?HcnYF{@*wv#TiaC^79_sIwr1bha* z&op(+b%~-P0A3n?x%`Om!3Mj%#wC{)s1873o^~pOg{=(vaPHGYc>6n!mr?h>aDY3= zMTaBYgm*>w2pf-Vt4K)UI8O&$<@SVtRv!ZRpM3{nv8mR)a#4G)f3^#nMUEOlDSZ1< z17XxM!Ot$SgPnY!>(s)28+~33+%IY@)sY{kw+|cp4q*EP!9*akrvqvL*`=40xmspd zf&L6J0fYBU>pXo7Y#w%RdrlcmyEcCQyPCD16Ka)K-Pn@6RW^85`xb3?$B!1kB7v(T z2kL9UCv69v=&70oV=xl2XXYkWh8CkWue!dN;*NeI$j@4g$mgr zT?< zwk}#XFWRZRiXQg+uW_i8i73{l0fi9ef};Gc`sYlo5Byp_K1a3L?Pm`Qp%hwOCrAWCgZbVqMW`~S) z)X25;3xK6k3iv}@asQLaEY(6RW*=PV(TAw{(pTRu%dq$ZrTKA#-%!or4s{IgZjIm6 z?{}|?((~ft1|26A$PTscVIhKI5WsHGcqsMZ+qsxLN-cR;V-3O#-S%mms3~DI_M&)vTgT<(_1X#SULrL82yk74E92em@`&;VcHGczTMGE;)HGe;gSeFNShjS!SsmmMVG*c(I+czMrB@j-c)BAW=! zBve^6M5k!Y*7Da@z%U70$HbM3S9abJ5QtAFHb=CD5J53hpnG=j>|`_1biZ0BnH%s4t55q#&Oj$kxG_Oz!YxsZ!Uh+bOZWB_ zn|Mcq$Q~JBpMV3n<~n7{B;VYQtg_BfW`6V|5rQn>luES>PNk$=)rSE!8nzzOY4N$T zm%5D(XgFe^$=Uw#QK2-Tg(B%ClAA%$PxBp4+mzeReDz6B@-3^({2>$QoS$@~Bf$f! zL_9>4njhUDq56L6+#vBow(r2xWD7nM#%*{l=-;T_r7fR;5gRI^_N4i~i6FaHtivb{ z54?XIwZpY;_VpWFqBjL^KlTUSRoVS(nb-6{=iHk^S$oIj$}V3~km z4F{#S?m;~05>sJw1fRRNwD|cd7tCR11NWL(SZcSYDWS>ZvS|0D^_1=g>{AMYCqb|vC z2h4DFb1KUbc*b&gPnGtbR6h0o2)LwE?IA`qHX1^|wnl1kr&P-b67th!jCe+tuGelAK1~2v`m4 z%InYXKArWSz5j=2@H~pq@%Y1orkzW8a$g=#Ek zLVc#AbU{lG-uSqzj(^sy2>(n6;{am~4Wc{4PW%oz&=@~OpO@Prq`mKgV-{_I@tSp+ z@vgz&>f+4u0pq?walIg^34FMCdGZNwb3aUpCQ<3p71`n!k_yRQp3;|U5IPF~Wt_w9 zyC)}9uB#yeu-gbGS7Hr2kIx&)WD1(CFvj`=6ygpzvlemM@V%y(uebB z{%;`pHVm&FGKMHT<2?QKlLq@j^v?TJhTO${5`VxDnHDW>TLx%Brk z*}xu*biaF(OhC^8jx+H7#Yca^SM;|pR4^G}G@4pVwxrNeK=Sj)gt&?y^)J-f8SlGj z)$F%&Ff`BDdh3h9%(7LRS54dyM{CJf;gB&O_96g7^n8nuL7nV)eogPn>t5h$e4xJ8 z9${GR=jU%^4$ksg(K#{Cm2vxoX!jp_$pDn2q!+ZSI?hnANtD^ z)$@oodHnGe<97cI$yP|U6p%3HX~zCmXjFX7kZQfHg&54}Ifk%KV~nr+R z^#V6KbrH+#v3a7Wt$?_|64R$9V}Gziz_k?284f6zcQwqVGs z!_`e>3uXmo6^8V(cxy`9%;B;d=wos=$ZUJ_;?geTVT$aQ5}U}?makhVp8JEkrfKKI zYI(zi1Su_fF%E!a;HMQMR)ajRpoO6Fs=aH6uAqzW=06!n#NOaQr`S_{_KmwWv9-_W z$wyG%+lbQXmI1ELIRQ*w*muMfz}{3Dp)+zP=X{LZ;;MoSn0FPpH_}nMIRg1;QqvuK zI1T>w@3UL;Fr#2Zg z4Pzf7c8%7HT(%}|`qGc2RV1lVEC?YM2jEx`TSS{RMw;EUFv5D%>-yfyoJeT+i=^gs zT{nEtBvc)9{kTP=J#t&&@YwwYbwGW_IhTCyU|h$&Q#fk(`PACzbM#vGKyFAc^-q`w zc1$CnBMo+)ol2H69Dh^Pf9=^d1P;y8HLZ|ne0;5Eer7-u>ZXKL)$31k&gG-15Ky;* z4iJk>>4&1oI)g9k2n4S_a#RpA3i3^;(LRF4^EC>vSqMB`_NVyi3{;xnQ&?!4XUELJ z{iMZ7iOAill;)9dHzmP%xx&)HXb^I4Kt|>lsY!x{XLxmlX@2cL@!ZK4Rv_N0jqK7P zEl-1GZRt3}A)&Ar(?U+cAxoMbU@~i+U5l4G3icT1?>ur}OYf!jF^4pb0e_R#=)J-5 zU~BqRknQf~fzV1E;p-ON-jcLSjB@i20@(l;v>X>mQ@QHesJmhm8sNV#K5961oZ6L| zS09r_xL~k;TbsU%WYr-wpSfp~gl>@IMZA{lR7bKCytc>3K1;t>Z-?FnOa{4Pm~ zaVI@&C=srS+lb&^hRZ7pna@SW0X21U{L;QGX#D`R+P7d{=}iQsyiK}aB17qA(sBNm|qv z?-z^l*#E4e5gdUW0v_0LdHk?jd|x;X5)D`!$J@nIAo)tH%Ra$5qS z7O?0aVSt&x5C|AFuqad+w_e~bv5^a7Ux|sl){oDh63}z8JFM&si1G33IOBAaU>G8i z}B9sguMYgkz>1i-}$Ya|R!2+ED010|@y)vC++ zva>LlfDj@DY!fb};)AQQ!?EPLUZaADLEaMP<HZJ3_o}r4riYR#8*$+X`;U=K( z-La(AaXX;|Vygo;ZB(OD1+WX|htGQs7H90V|I&jTgtd>|8&dA-d;mWYsoR!8dUF9K zG%qcJeH!&ANnSDtw(yhKWp+ByGonBnZRlg)?g{a(UTg2xN0+U>Bf|4>T-^e+thEXB z2{F*=x&AySY+Nd%mylfBVgLAK{xw#0DyI)v>`d2&7kg3;oV>eZ`BUcl$Cy}EQ1}rw}A${%b-V(sz`u*b#u~Ncv>iMBVZr1#rY44 zui$#i5yhwRZGUL}xHW;xW8!^mKM9|fBs6x;-2seb03{)8dA+bu(w!Dg2&8TK|62RY zsH*n&jTff7LAo0x799ezXb~_#P`X<}Lh0_3Qc_YxL_$OfK}t$;gM>7QfFOtk>BBoxULFLtWBAUU=-L-rv zkO$#I?}mkoN16(lp^{1=v`rN&tsyP=AR^V-`Dk}S^0QbedvkQe%Y#wan34p8BGR$c zyIj(((zKSH2o47I$!xtxUP0h1Sp58_cvbl_tX6Q8s%8w*ysVX0aa9!qsrq-NSq%4g zhZQc&lgC=%|9nK@vitF@+IwP2H-n=y32G7RZ=^T}=@8MuX zE>JumuDf@SC~4;E^gmY9uU>>R=i5<~&!fcg;O{n^66fBkebceueOs3XO>*6p&eKF? zGTGbIg_?zF8g>^oWka@@v}px>sMt@22NDbJ!vZqLK|{lFPnJ4Ly6?`6`kw4|-rxd_ zXLEu7i)k)f<_Q~6l1!%hH6FBw^(oyBL{0$NWNP{KE#9g%@7 zkFzJjE@s-PB=cX_Su)aZ7hPFq(zW4o%EbUpI8R6u{oI84NxGXe~%aC2Ve3_%Et54~<7^wLb>HHTzAE+Gk zz0Xh;@w3yfh-Q!7v{Bcy8LOm8Jh)n)Pw4#jxl%uruK z)5LKn2Y%SW*;=6a=by7d_3Lk&2?v64&7D7HTi0xq2ftL@VR{q zqYPJGdlg0S!GXo{n)^9V@WGVm$d{?rN+RZZNcdd4T$1q!QqN_gpm9_BaW6{xxhYMDtu z0I0rm4}1$?`~|^a*rt6*2~xBLMGS-ph;`_qFh(@=+HI#|d)#u0>5R!r)1@s;VulG5 z51<1bZ)Y|a^}Mc_&1?D#AADdknQ}i55ky0TQbqFT*YsdwY+uj88Btm&B6?u>!~l#o z_E;MF{P-y*i#}oWV_XobDAjEky|KAZ0oa5HA2y2wXz_P`%Gbp}z0`X=RU86b0WJIa ze`-#@sMQ21W@d-;x4XNC?}t{c14IB)6R3yjUdi>?&fJ5NGXX|#)sTjpnFw-XgUhw& znL;!OMbW-K@5aald;`rl1AZ#8gZhHaJ*zcg1n%A&CL+b-y}=A!9*;ZzIjLpgUVhPB$;~0@8=g7mMmP@E5%5Q+h3MWA3{# zw1mse&76E6+;QxbxD-e0!T4*%)+bgy?s)L$=aN)=)<#mnuacXr*u8^Clq) z;}EBBhf0LUzDy4Q7ib|m0lSYrcN)7}_+6V*L}>ukwouxMsx$V4xJO@qf53x12jm8v zuhyfni$lf61!z{%-k^K`8(xB&7#>}zO}cpQA~`zthGvw7f=1;c(}+xb%9za z^pulBS#A`FLI-$0>bKqM&4`LI*|;p@DdO6#RHU~y7%;G2I(PH(9SU1aL{hSMO{&8B zxhNclj0T<{>A}QEnqh)O`*x2{c#X$@4W!%NH&cSXp6e~G5Z_8VEoo?$NcD5Tu@ zC@F~zo1^w%#Rc)XuJ`L69M$^g$u3wi$hn{GutN}k{p(8t}t1CDc5>Ail`MbeTRRvAEsO=@U(^H{xw09zV7L!iz z$P!y-NOr4%k@mB4HdOIkkA9fl-Jg%L7n860C5COWAET8|U}c zkkwu@ep1xe`YH||RAOCi#kVNjJR$=+^@g~`%XnS26pm{db`iKnXF~_zJZ%d^aJ;eqi<~R#h zowKwXP1v_Q)yL{@SzDLKGp6BJ)D;z4sqqB&VKov#U`iMzhNoCu^0_{U5MJyZg^a%A@l%TsdLNP9$CHFHS}aSthin`dI}y|qBiV&^vUnJj~z!w zLp_szvjm%U8|qFFbAWldvUi05hg~-2TdnrX`>LIbj}E{p0J+Azkh`fbS}>hvorzn5pVOX5~?nEz{<7vLUO%6NnlPrVFqrxf~Y7+O^ zztmKD{_1DH=-rz$~YRbrGf(QbyF-<4lwunEfFot|@T?!VbP zEt1=J`>(bT^dVA@TBmZngOdewe#D33G;4*WYGm_18_%Ek>Xq9GJnv$k zFfoT6U}O~f*MAU`NU6r7=}EZ0l9oj@wQ+5B@v(w92+uqZo_isZb?;A=x85G&7n?*D zG0C1Ja5en>s*XtFLoAd13Msd68`_~3RJ_zKH_|cMl#y;qz z#?dGpEHKLiMXPtgPrWm9-1N{bFFEn>(mP7^sR~q(=j1-?y|$eqHlJAbK92Er^~N6@ zRA2=zK1pZjIu(@cmIlw~B0w^8Wq=&pS^3k%4W$J9vo&F@&o?B9t^3+lN@acbftJu7 zH7RP@BV+zBqq1RW<$H;80LImTEjj`peCS?0s#6$o<+9|q$-x8!MY56~GeCmC(E@a( zEXR7%hHm?>B8d-QG72=o;}7+$iCS9JU#D7|zc8#9u*+zS_RI;E#NY zewZ0aNx`}68UO6HmR4W5HJldtkk)@}OiKCVT79(c{NZO$N@01?V@?jPC~R=jZfen{ zz3};Ab-59SYtvbLjJ@g3|IfcdqT{-xVjY#BkwFgY^b+k|)*bXEHB9K^d&m6(n*bcZ z?kC53kkQ=4(l$LM#h@AJ?*Xqr4BOCnu13hI9`~9F<*mQ^14+a#QqlV(v){F zgH2_<0@Lm$df|C6sP*4_V1>eq-g{_P6?7GlT`ztyDQ+ z)2R@a-zr&V9QL-je$`oeyzuO`7ivlx(Tsr>on%7shx}v|=slDVdb!)LfXA_O^`R9= zic9P)5*kBGr&QzwE8p^?scMdPWULN?>I#iF^o^3}XeFw}ByuqEwvM(D-Ln!n*u?VY zeevMzPu{HUs2uDY2yj?B5OvdlAPNz;nX^lZ#AR^VC)c{DDl)ij{i~h>ugaV3AumM- z?&j`~?zpcc{Ku+zp`x!03XE8{QOM<3BHoQ@fNl>c;G5^a%Vh}};p#d>s)|wC%ILEJ7>G2W3FUo6ueNpE9<0f>u=`)n zBhqT-rtmeTIu?^we?IA8Uu-NEOc0g}tmFA3K%?o_yYg{? zXqJf;r@w62M zrA+0%`g&PVO3dqjdTD79qv$@3d&1SI1lPf}PBOc%QXIy2vDXly7cvU$2Y4gqced0W zZ0MSXyg!}STw-Z_dq@@h=sYFAJGzbL8V3XE#Ls(2lb4tJekmt7(#Yj3KW=AD`Pl_w ze(F_4a?%vZwx__p^kZV{_tw8Fp22g+uA+<1H=&uzWj;X2 zSrm8emTh8t<~Eiruz%Qs_Z)6?jK~47)ZqrI=!LzSx(DLxm=_W?Jo)mVmlZe!GA^pt z-9_xg>&{!@(2*S-aN#(%ec>&1r@PLJMYXscj;J(zdw=Ttax&ez3C7+DAb)N*vuL4i zp$qW*OQ$*V_v9YX+f4Z>bi=dmSCCCbPZT zCH+m*=KQ@8n!$~GlCxNw-ZFH!;B~RQ(R);xPaMoeqWkc6t98nC3IWUnPiYNF=D!OO zhzB1~@vR+M0%Tw9XTEu1wRX8L6xsXnfdD5nagG1*;eI z9o6ioiyO4GD^NKu5FBX{vT?ZVl4fN>Tj*A!zqPvw@T>p6x~C)1${TZMQAc~H^Fi0xFsmLYloDS{Ia_| z(1zfC&wWWENft-b73YjXPShUgVyXLk$|Gy&ZEUbhOItTF9y3&5FqBvOiFBG@58dja zvZK^2P)PQ$_}jfPKk?zg{-mbZd2J&=jMQ^&*x@ttGe-$smZ3rMRe_V5hxoe33VHm029~ly0+)x z!o^+6%Tp;xUiIfsVYSbUEhRt6uQ;w(;E5Oc;Zz%F}hmhB0C6$O(Yz9(;zG0`(h3eI6Con>#L)q`>S@o!|f9`1BV|&#lEVxr#}{ zqQHB8l5_E&UWCu@i&?#JoICOJh)_M~y9+J@lyOoV&9dN1rzld2TSCxkH4=_Nj&Ft^ zSVJ*oD>FZ1H&snjpCRXWY$?0po2P_jlhO(l*;`}UB9(bx;_WWMhSDA-?|{IN?fVr2y^BXO09cHn!*uxTk5J^2}3fc-(EOMaBT;+#D#e@bKx z!lYAivhx!H^P~P($vLwIDufk|SLX3BW~}F$c>llBJse`|tCu z6`Kl$QlmrUD~B*hIlbIqxzIexZC1G5h3LC z^6hetqz@>47FPGjb!Vaks=G`DVD5K4*}?}Jagp5yK`XochD~zrv2O^_wD5Nk%1RC~w7O^@wRezg@rtkd0*nsd z(=|{|e#HYgTsXI^kA0lv_iAx|zwtt57cJZbE%_~0THqVnyJd%AUzL}gev$F_E_Dd> z9l=vg3v@O>xlrKzM(a3Lu7wiL08%`_Opkcua!Nn}s&%DLJBdNtIP}E(c?{yhP zkY-u2M>x41Cdmk{fHX1(3VM{!UL2G44c2EuqT<&jAVpVwOf`J0qJuS{!zM0??G)uy zZe{`9auIaGddXGaaLcRb^UMWfrl_iqJ^UWh`4O*9 zFT?fy;$u@8M2`dg`mCVyix&-%BcXoE#`v^`_rAps&G~&(M@km3W8$sfeZHOsryWaj zc%*R2wOPnoLA{2MC6>LuR#HeG6jN^#+Z>$MdHj8sh;lG>MTn>(dXom2N3i6NC+=YF-U> zWSIqV4k8EmL;gjTv^@EPI4ma5=omL;3fu;9Q>2rJ#C8Dg>pZvI^6Giv=K1GO%}!j| zI!IyFu0<9xYL%j2y^;s3!}$HGPeUOKQL~_aerA!V?0|tq-7UnDK-Csk8YzY+Tv=qP zhY17+%T|ri$4j*npE z^wJ_ru#7{JTe{REkue*43C2%>1_W)pHhBTxARbCbWCa9h(=T_ zhjf1L2ubD1X66J_dXT+MV7?8FRQ+BN?sVmIe~4~jJm$kwPdkCpRZuA`9}0&v-gckv zwMj%zdPAF`$kG=rshP{}hMD~6&Sf-Jp@6(dt0(1fpes}GS+Yg(Pp#Y|l1)|ahjFP^ zavT|<@lcbd;I1pXlyy>`nxFg%&j1RDmOl~1z-oJPbxX8YbskXTYfSmG zc3=bWkXu%jeTpKQ2D;uU+Na$IDnD|pCVZZ2wUCL)bxqLpu}WzF{VHSLnZ9$*lJih1 zH(XR8EjgRwE?&%5Du)(lRu;Waw&!MOyV#m=-R=mu`WuIWzAfvVE@gBgT{gdPfTz& zru<%A9Z@OUvo@gmfTgP81S%#w9cKaSbbYmID~6W#bA)tCb(wKN1Ed0k)%qF}u5i22z4*5mSUzWvfKV#ybDh~6)q4K2-w+TJ5}L2u z8Q$Y~FWuuOIt|r~8j09vW#+MOWGg%uziEHw1igMo{`aQcpykldqBg4wl{nN4_t#^) z+A#o%0Exh}qbb9!^QADyAS%TdHYq+g9>T2?=>D1f*Z1?EVc#;TI{K)MY~9pVkdpcX z^(cKNHNm@G8Btzt(h5Ha!Ia_h=7z(6H7)-+fdy&&TiXaJVC(>CceAdQG1^}F!F52A z-7Z)e)rr*-w!TzLG8lV+oqgitnJ7Y0BCUkc^)g!h9~YFv-F~dStsw*0SYk1)8kXhN zp3uh8j9E^SL6l4SZMy+Jl<&t34~vr%vFXjXV|$(d-ZL7sw-0{*G@H1XcyNN;B2+_z zt+a#9qDv#gWVzKmp=*|$mIIX=HPv#j;hGBxBG&$XNv=Yk4DX>W4K>Hx>)dpSwd497 zZ;GQzUY=tVM>9^!0p@Wa$X4Va*nOd1=DuO+gYSwGz~NAy1W#d5{G2sJEk3fbB4YCM z8`*gjS&q*?^kad~{!_3bWnPq!F<%c3ZN9|x`j0hNFq3F$Gc(LjAAzf5-tWF(oA4?N zT;$o8vO$#ku+Y|=k~1PcyX57)5CiP!c4`|=uGqeAZ5|}(0S!jU?LtO>QwX{j=IC)9^eN(@yNM?Gf-V(}bZMzEvkz1KB26A+v zmLgDV_vqA52?`wV&DES^5??p+hiE7*=yGN_t{L!^f?1r7+SYzBIhCqwKv{AQDwL z&i=Xn(N&5;XSH4ulh7Bh_NV@EF(~F}KX?V3h7=m)bAi~1o2T=8`3sb=ge2`mU5lPkM2~~C%H=nu40E$%0t?y{AR1glPK2n=S z8-C)aT?1#Vx%qJI?G#gU5<7`%sH^F(4@(?e$b&4FP$)+^YW54(+*%HpPxl$ zWA)q)Fr$0g8q&AMcg#(&{A95#M;CnNP^ut|PAg@4zS%y&Sr4@vOOR|p1Hbk`PYDxq zyf4K{n6N~yd0JRmzAoIX>HkR>10Wg!mm}mm$WYY5c=mQ=s#ICd{|GTzU`rJuPkBFf zyEpQ9@03Cf6JXYH;AsZWDi+X`IfuAE6sY}ZCnW<3I1H^>9^w^O;U#{hcfasC+t^MH zSXX-lZars&4k))U5X3kcwdpbn0e%LoI+5o;lwITImi^SHer^20z)Zqfd#}I{mr+^*Jy+v{_oW#UqSK(Iwne4=ro3GN1&>^@axPu(hV~U{9os+(>gYQ- z8{=Wz?IjL7*$wShFq*M_FoVx3u#3T(M?!8*@4jIr$j*P1LEc5lZS8D%9nI7f*6|Mu z64>NcJsrl{BnaNrhMa1GDjH$a`OPx8eH;HSt4pNsbB2^5oU_`HSe=kx;EEQ5jU^}P z9c$}2mK)n75C40#Y^`5I5Z-_}ff6veZC^&k+b4U&jzm3%c7pdwot_JG8EQY3+v6<^rxi5ay0~+4wzC5p^syam{)vM(R5Uqfn z%VCa#7t_u6xy1q9XZC>742w;0kEP|(RF7FFB2`vzJPra$(>TH|=Wh;n!AZ2rr5$GppUpTw0Q)r0z_mwfVz zCc0F1ji~xO(?>tC#vkW23`GfG)G1WxHs=n;dc~iMz<=tHk*{E;A&V zgrDEUG#Wwbv@%y8g{c{{H)g-P+xnISA)HR@q_*o(bgpQ%JSbc$@^1*NYB-bX>au=C zCaqvVKFU)T$igJW$(_ce^kLslyA1;nt)TfQQQY0zi5u_S@Z>e4bp=^O#v!Vwc~U%IaV za$3$tt5DX_+YdUv$@g>*SN7bum?AM7)gSBSq#<7C*jd??bv zbv2LxLg>7zZ#1Xuu}n7Qc<`huPhMS04-(V|2{vT(mH%=x!Dj-f!B_RlSUPW3a2VLt z{_CKRsQS_S^0T_3x_v*1L36JRu*|{44sqg@$?Hsl*5a{09XPZ(VQsGaw*M%7mUUzV z*FX-=7bKZE8Th}@8;=*L zFPD%L7ube_-jy^2Mqkxi>vnRZPO<@6r&gZzBrC9T*28QJd;^yM%*L3XSN4UAc`a3$ z8%04FFzN8rCZ-r1L{eM}T}`bUlX9N;l102)YRs+-h;>PY zaSXegVeINpB^gC_gFtY0%TLv zY;HBt5g;cFXsyKsjpIm%v9_8rp>;=Xcl}m+5#E01I%f zB}dRj36{%ks%FZZ;2z0=4-RE=k3M~4ZAdt?r38{yIr@v3lB2#lHC{8yE*ElyuqwgbW5jkmmp03(P{kBn(Aa^Ln?a^Iw^%vB0Ed8v>PuKGOd_;l;&H zLPCMTQ&_l^>YwL#Mq*5Ee(kIA8InW;_wuG)8K07PdE(}znfrYaD8b9QXGZ}+T<<5B z<&adGW;o=;A#;kYIqej*J5UD9v81!!yEa0adPNC%gkw;w-6{pW*^_ z>KhZ`qrS9C42uBD)cBlXjM>8ceMa_cD28l0OT8*&U(c922p;Ja5yon{ZLwml;3cF4 zR{B{>;h+KAO;HwWz=KYts~zn}xDHj06HGRLa7RPz@7NbDE&1y--)DT_JdnW^u=OB9 zl~7Pw?V}sx^Q!d`8d5~u1zjLOo&?G%eSpz@Bo-gjw2m=og@0S@7Oj>yzb0G4#C45l zWW#{P|Av}cWkSXIze^s|)Hi@S*#9JhbDV{9+Kh8)DKEl{44|g@=dHx~4o%&rQT+$W z5AtpPv5eKgoC_5SoOQHCr0<)pmCTJ} z4~j?x34HOY#U;0lDNL3`wu3g$Q? zA;5DIN=(JHc4q~9;w{idy}J5=D9z{%CmY0IU5<-2kxV3HeLNj-+CvflANI_AV1&&X zgq+I}pNj+-A<006am?BrG*WOcRWyckXw1c%1(~upb$fX@u+ocITMx5=$hcGS{zngkZOKJwkCG{>dp=3cWIzgu9cq^T@O8hpienIdXUz z)~>e@d;V+GfLh|muL1Jo34ZAp2?s88Diwi6TZBVHUm<2Ek3-${2yQehdW6aTSA%-! z;{#TJDq|*!$M+2uSp2Fwx_kP*toR8?g`@b`hknry()%Xzr9@t0GY;!s8}1lMEYGX4 z3sm@Rw!y@v9)R716)_~wZX3ubVZazD>p*&58U-68SXLWM1L4cdr=ik#i-^HGkBeGxPYT;*r#(q{WV2WeK zI-Y#^Df=aEO{tkBELnLVcefzP>?5>?ka7a>iH-1C^9HZpgwP11Sfl-J!13!0A>au^ zarru%MflwLDKDag*tPC_F@26j4V3eYCAU?5lUr=0KN$R{4Sx0m`w zb}x`iWe!c^i&JRgS6_?j2A2v9f4J3Hj8%MHGU0dHoyokcCXdBr(FG)1f^k%nmX6VN zF?|)i@OiW@>vYDGzYgUmev(Rcp64}pq66J*=n|3U*5L6JCT34&uoI~1Kf%QlF69Kk z_1RV5bWjK~FgvL`-97K~>3!V?jE^DGfHSxN-c_`rJrz9A zx&C=%!emoDeReFAqSBJ}td42i!EW<0?EPL+F%& ze%uvFzgT^~$%JtsJg8LF8t z5IH{})|B?JnX~pLrpxacC4tRt~>%7 z#`I-XBOi$yfm!37Rt0+|`-PM^Vh^l)3X7P)RsoY=IHxY*P!CG>6-TQ8+`yjHFfl?X zbwZ$-Je8f;b8ZYuT=7AJL4QvzPLa7#;bujF+Dj?Q_``vUy1cg?HQh`*d zc_ubvu5Tt$I*r&L5AS2EWL05TZ5{K=LC#Zad8|R*g7l)@y5z37Mk$$nNj1!f#{am7aS;5F4IE=5f8}}Q+SH=KDxq5FEQC= zrCcIvYO#8&C&?fGxUbpyza!)nv%**zxb||Clkt&kE5WHCp9&V~T0^5L(Y$io`8Fp3G4cnm#ZEWO;KiIe3}n9Q-lU4oc6Tk&CKN;@2)H{r zJEkj-W>wp^{wbaR+$%I0%`%Wu0xrQCC|*AAg>9B3B;JDeKq$M`#=Q|}L5KI@ccu|S zWD+`0umkKH%eA(QoGgNHb^=b!8kzHO$Fi&BZ^`^77pMiICalKeyr6`^(DY3KG77PN29DXtZHU0RwOi!@g1R)yqC( zhu?mi1pvk;B6&f(%)UVZhYjN)sP@Vgp-NG;>9jWu<+lVG*pxr)`tJTo0O=02HZDE9 zA-6WP=GHw0YI7}P*)M-J;#dV&4w)&xv&eBtkk_F$x*6Dn(!k_j97-&>y=q#5f zS(#e;EEPIvQh8pzN(>Iqu0+6KGtYR1IK(9)=bryFQjie-LO8v9R*7aO>9(3sbXBLX z1+it8`OAazncGW3#@BBHSBtd3JSF_zk!QE2pd39*wom7*OMb{7Ea3HHVvwDEV<(vl zz(wYwJ-7Do6*y=W2B;Sz1WoTi87{s7l$OX}Q{$bvki6IR#3V@V+u$^gP78k`DWFaFnk6ZV<_5}Mg zhj!V*JW3Yo4+w3Hr%>7GPlm~iO@M+}H}rmoS^~*Hg^h`aa1w?WcCQPa?>z^=*Y5AD zEs4PE?SMNS4FoHi6$@h6CEq@!oqdx>k^tmevR(a4t@e+LdMx%A<4XhIR5=-0#8}l@ z2`SeVR+^!h-8U5QiE+U38-^x*i7eKKXWxIy9?Yx&l!j!Quf%S_4y?}@7%=%t@JXi4 zZh^3d#w~e>NiYFupsUG?0Tp>@5&s0r5E)wfQpZ;P>b4eIGFF!O;mtMG}Y}oxs-FAQSn4;i%l)gjBc^ow7b+rEzO3QOLx6;cTeX zA-4F^8Z)ptkPfT)@q3x7d6RoVXqY>y1hnpAm;yCk=*;LW^REKt=O~BU4N!OWig_bL z6cK=oMa$ac%2^P!-4S>Ew)a!Jqul#bvTIqs$C7Y;b=)Kc$DwegT!a{p0ypyHQ`@@A z$g@S&ES|Q2iSA_%s82Hrt;^^e)xi-G${q)|DG88kN4zx^@M3*aZ%q3SO%V{_XYnsW z52<#~q#3N9!y;D2_n6*5J+F3q#igCT5NABMDyoQpAa!4sI>`+quQPvJ8o&RMmAa|F z$bse9nzYJ+_99*$;Wa3N`Qqi%yq;>;>c)ppgH9Ap=B~dIi$ z)(=16lKgM|aiPv_T9ZNt1KoS|Boa zv%2clYMaT~o>GSBj1KX!X>4nZRA)z^+Mze^-sTfl%a z-_808s-_Cn3>-v59+lyyp@wXYK5bLy5tIpzW(D2LmS}Qe@s!s#@Veh0c!uR60NVZVZ|3Wvezl8LE_%(fPNbFzZ0Qq z@q6qS+dlQKhg;Pch_)KUV`17TFs}x9{c{9tSNis4Lv9cL zjh!p;=+u#*=Anem4weHL@Z`(M3A}Wq!Y&%P%L54v1nd(b&MdGuK<81{e8SHpirh7% z^x$8~xF7+Fwz-`Di>7`_|I@QQ44Lp$dg*U16#Lt8Fm+I0Bu;Rhz;u^gh`eCVJ7T%i zO^D|gfp$I#@sI;JBPR^>(L39v0aI!yjBP6P^JGR;gOBiaroJJc_dE! zja`~b??Dn>Ybe6R2h5F-mUBq0$`?{0CwhNJ7>tn=4LQ05^I?i>S}8z_GqCZb_3sTj zt0wQgc)hs(5%0^RJmFq|nO@1P-fjaoU=cv2U@j>J;Cz>A3zXvbs`Glh3TDV2<~Rjr z2nmHT8nDEfo5VkQtHA)7xwd7@oiBy)P$884XDb3gLD!@Hd_+ZlaQ-XQDPz_)W~d19 z(oEpo0iBFSE4kjN-GE6ckf?gd;a+)TTG+mw@Xke*VodZt%d9snZ7mnkAmAX zh)*+bx1AnlN5!V!DCmGW?g+9%ZCa=OhwD~H&_WY5)Zof$Q(S0ZYBd!Fzqj>iRWS!nw63c0ZyeJumcQoKS4- zo1U!&7pHSqL}%Kx6eOyB&T74+V}5|S@K)D6Rjorl7>lOOZF%~i_Ti{p!UqwZt6uQQ zFF*ReGBdL9-%g;0kq?~Zz=-ih=(tcOt&A7CXdsg~QVQoRStaZyjmz0p_MP|t15C6lr;oi=uNfG&HNuwQd_$0uJgJeMTbmjB^ z-AtiM({y^sR_v-@L{tic{B6|sG;MmH?KwM79uD;MlZ(h)diHkovV!h<{U>^scbqTQ zY4ayVKKODt2V!8z)X5v3{}dDoJj8Oe8jSOnr7Ym^dA;Jv291N;MBNC^6$&akG!)yU z7s8@{(-MF{^567fH*9zVhb2_+KkUi4#}qN33~avja3YJzx?>T9PdFrR$#Mj=P)TVB zZn5u}mJ6&w;aRrb_$g&irlVsn58~dULU4X(PlC5jQsw|Q676AugB17XlAPX8LeUD( z_K6+)ES+@9CtjsFq&ik_3C1>LDl~YvuoK%0c4A}#GFs3=a^_%{JLm&7A-+_h%H)1m zLFvT@A%d}@Ag2FW#_0O5ISCm(Q2Mmg24Ve+_7L-wcZ^Uk{H3pO#&xUk>^CQbp-3<^ z4Hg1Igm{f_l5Zf`Hp?`3zH`V4%>8g~wOmxU>86^w4D7XjiriQHP3p)P2)Lf0Az5S` z@r$N`K)_Erq2fN>bOtRZ0wI!5om;_G~uN{7`! zGlU`4z*U5g2U67U$M2_@y7Xq`0g4GKES0l;1;4yg9n>M+gG2CK74upBpH1DaC5?tgeb@}%dNg72LA7Ys3DnS zs&V;H;MgU92aNhJnlZ_?j)u&c&%RLZ_iQ;_j2r}1mdn+5v>@;W%R3=25mb#aFbOYh zAj;`$%S}(kEx}0y5ZfG+Ko`8&Oowb3$_q8T5w5SL$74nBo#Y8SD?(MD(>`>WwsPTCx*$9a$=>zs=3MjCXTEQd@S zRz`-&SH>k?j~5PPNH4&PNtD~$TaDd9yzjDvSYzP%0RKSDnU>A7o$ z1kpIWlhGjlZ1axYbPb@!Iv^lZ2e3~aSy#Wr^^Dk_n-_m5poXEz>1bvU%U#OG`Rjcj zU|_Le<)eH4YIR}iBF{_hHQ4pRi(f!NBh{OlDmAUIv_E;JS&P59?7J)dq zP~Dxj_5x;{HnEke4cUX{l`;--gppnJN*)Oxq>BAANC>aCU8GN1B3tX-u#CY>L$FPf z!hjj(^avS4_`nXAw&fuQv}W2!x5qVW_b;n)^%AvEF252BZw}!h3s7;!LSO@fvo*Ch9WV;mPMY zC_7l)-7tb5?Tfx|=`jQsB{T@63cICHa5mACms4`2zL`=tHogmfh{~Q*2q9kM8{a}* zUN~tJuBry}5)F6?hKBhf=HUqKx+yEO+J%co<`Q*HuMMjld|uReu=a!+C-pD#{3H#p zs?ax07q0ydqeZw0P#skEzxB13ym#QL9vemTL?lFq_b5XFp&EPj|fMM6Sh z6Kj<9e0;|VI94LqW4S-Q#KhDzZe`nBkVCNh3u$^Gkh4eD$Z7m}Fa8mV%%QjZbTC@v<?ocez*`EoDxH`^d;+>pqE4^^8NqF`Dh)QTH;D} z0{$$s(7Ye62&pXE*;Hb=4JX>tfdUE#_Oo&<{h`au?xnK2tvJzpq6L~Odk#gB2B3`Y z34X{7ks8FCB}wg$T%ad|#ZH8b2+iP<_J>rz&`qDE0TZE0l=z!TL7O)3Z|5}$p7z)> z9kH+_D92H35m|2>KLyDtytenQ>|hF-{Mbkh5oztW-A@RD2Bz8+))Y~|gq=a&7~6o0 zQz+FMR-KMqUa@yK2IkFhB0%jfx4O?q3`v*)7d{;WA3TjA-HYo=Gf80+m_m=%0-;Z6 z-zg&Y{Sj9iC~U!S2#*Io`N7GKdF9{52N|NmFoOvCP4ES9QqwM`>)y$s)K8nzBAAqe z=0xpbbAab!4?YM?a+4aov0-Ww1_o4PMt|K@1T8WKOvh;FQ6_d7;qo`2bioNi%dUZ; z7fQ;|Bvp2xfcW#f!Yw4>e`LW1f*tZ?KYwUjZcwt5npwctX}yvQ8Rt(>6_VbJViaz8EhrDCLn zg_CNGaNwTD-3g?`(We<&eYgatJ5nJ!N;#%rxCz33D5dOGn_Y~L4d;d@f&^1Ay7v+5h*k*})b@pqBX(Y5DwKrrSBYgb;$lvvJq& zZ%09C2*~Sh#~t>dcA$4;XnS`<5uX=>;d%B2M)6K+6i{PzEIoCJ`W|0ghvBg*LCOSJ zyNW6>WETjc5L%Wz@WGuwj(KPU+6!=^LKF({N><>b=7Kr{uy%zoB<|Vy2{Jzu+oFp6 z8OeLU5e`y{i>r^F0Io|lnxU|}5eS6`$0oFF9iBQUy%qt4=i$8R1iQEG$Y@xV0;C^X z6|RyBx5;l&-2|l~5RjzGyVX@MZXfnCdFZWENV~@7iC}x*k>T+pB?B%k2<$*giwT!$ z_8sQgjIm4wD%g69U#hn-F!H|oGa?L5nE4=f={1hVOW0({T7nFReYqwna_X%BXy#Y7 zmD@kOAN({REK&GMKCAa8Od9~RFu8#NisgV;iJs-o z%l+LD?wF}ZXm871br>U+lhxMdLuNJx15Ym2EG4$OGQl!$jeZ@}KZ_W4-OV-qpVpde zrd=c`IH`zLi$VtN`jMjMFJAO_ke5gQ?-TZ6GO*)#5_d6h%N*Ui_>%z652ZlIG93?C!CM+&zfRd3HlaWVBUq+$iQK + map + logo + + + ); +} + +export default CanvasComponent; diff --git a/src/hooks/useCanvas.ts b/src/hooks/useCanvas.ts new file mode 100644 index 0000000..0280173 --- /dev/null +++ b/src/hooks/useCanvas.ts @@ -0,0 +1,214 @@ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-continue */ +/* eslint-disable no-await-in-loop */ +/** 리팩토링 예정 */ +import React, { useEffect, useRef, useState, RefObject } from 'react'; + +const X_PLUS = [0, 0, 1, -1, 1, -1, 1, -1]; +const Y_PLUS = [1, -1, 0, 0, 1, -1, -1, 1]; +const CANVAS_WIDTH = 804; +const CANVAS_HEIGHT = 420; +const PIXEL_UNIT = 5; +const RENDERING_COUNT = 200; +const MAP_OPTION = 'mapOption'; +const LOGO_OPTION = 'logoOption'; + +interface useCanvasType { + mapImgRef: RefObject; + logoImgRef: RefObject; + canvasRef: RefObject; + mouseDownHandler: (e: React.MouseEvent) => void; +} + +type OptionType = 'logoOption' | 'mapOption'; +interface DotInterface { + x: number; + y: number; + r: number; + g: number; + b: number; +} + +function sleep(time: number) { + return new Promise((resolve) => setTimeout(resolve, time)); +} + +class Dot { + mapOption: DotInterface; + + logoOption: DotInterface; + + currentOption: OptionType; + + constructor(mapOption: DotInterface, logoOption: DotInterface) { + this.mapOption = mapOption; + this.logoOption = logoOption; + this.currentOption = LOGO_OPTION; + } + + draw(c: CanvasRenderingContext2D) { + const currentOption = this[this.currentOption]; + + c.beginPath(); + c.fillStyle = `rgb(${currentOption.r}, ${currentOption.g}, ${currentOption.b})`; + c.rect(currentOption.x, currentOption.y, 4, 4); + // c.arc(currentOption.x, currentOption.y, 2, 0, 2 * Math.PI, false); + c.fill(); + } +} + +export default function useCanvas(): useCanvasType { + const canvasRef = useRef(document.createElement('canvas')); + const mapImgRef = useRef(document.createElement('img')); + const logoImgRef = useRef(document.createElement('img')); + + const [dotsState, setDotsState] = useState>([]); + const [context, setContext] = useState(); + const [nextOption, setNextOption] = useState('mapOption'); + + function drawCanvas( + ctx: CanvasRenderingContext2D | undefined = context, + dots = dotsState + ) { + if (ctx) { + ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + dots.forEach((dotRow) => { + dotRow.forEach((dot: Dot) => { + dot.draw(ctx); + }); + }); + } + } + + function parseImageToPixels( + ctx: CanvasRenderingContext2D, + img: HTMLImageElement + ) { + const pixels = []; + + ctx.drawImage(img, 0, 0); + + const imageData = ctx.getImageData( + 0, + 0, + img.naturalWidth, + img.naturalHeight + ).data; + + for (let i = 0; i < imageData.length; i += 4) { + const x = ((i / 4) % img.naturalWidth) + 1; + const y = Math.floor(i / img.naturalWidth / 4) + 1; + if (x % PIXEL_UNIT === 0 && y % PIXEL_UNIT === 0) { + pixels.push({ + x, + y, + r: imageData[i], + g: imageData[i + 1], + b: imageData[i + 2], + }); + } + } + + return pixels; + } + + async function initCanvas() { + const ctx = canvasRef.current.getContext('2d') as CanvasRenderingContext2D; + + await sleep(100); + const logoPixels = parseImageToPixels(ctx, logoImgRef.current); + const mapPixels = parseImageToPixels(ctx, mapImgRef.current); + + const dots = []; + for (let i = 0; i < mapImgRef.current.naturalHeight / PIXEL_UNIT; i += 1) { + const row = []; + for (let j = 0; j < mapImgRef.current.naturalWidth / PIXEL_UNIT; j += 1) { + const index = (mapImgRef.current.naturalWidth / PIXEL_UNIT) * i + j; + const mapOption = mapPixels[index]; + const logoOption = logoPixels[index]; + + if (!mapOption) break; + const dot = new Dot(mapOption, logoOption); + row.push(dot); + } + if (row.length) { + dots.push(row); + } + } + + setContext(ctx); + setDotsState(dots); + drawCanvas(ctx, dots); + } + + function getCursorPosition(event: React.MouseEvent) { + const rect = canvasRef.current.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = event.clientY - rect.top; + return { x, y }; + } + + async function changeCanvasOption( + option: OptionType, + point: { x: number; y: number } + ) { + const store: Array = [[point.x, point.y]]; + let count = 0; + + while (store.length) { + const [x, y] = store.shift() as number[]; + dotsState[x][y].currentOption = option; + + for (let i = 0; i < X_PLUS.length; i += 1) { + const xx = x + X_PLUS[i]; + const yy = y + Y_PLUS[i]; + + const maxPixelX = Math.floor(CANVAS_HEIGHT / PIXEL_UNIT) - 1; + const maxPixelY = Math.floor(CANVAS_WIDTH / PIXEL_UNIT); + if (xx >= maxPixelX || xx < 0 || yy >= maxPixelY || yy < 0) { + continue; + } + + if (dotsState[xx][yy].currentOption !== option) { + const isExit = store.some((dot) => dot[0] === xx && dot[1] === yy); + if (!isExit) { + store.push([xx, yy]); + } + } + } + + if (count === RENDERING_COUNT) { + await sleep(1); + count = 0; + drawCanvas(); + } + count += 1; + } + } + + const mouseDownHandler = async (e: React.MouseEvent) => { + const { x, y } = getCursorPosition(e); + /** 0부터 시작이라 1씩 빼줌, 좌표랑 배열이 반대라서 바꾸어서 넣어줌 */ + const coorX = parseInt(String(y / PIXEL_UNIT), 10) - 1; + const coorY = parseInt(String(x / PIXEL_UNIT), 10) - 1; + + await changeCanvasOption(nextOption, { x: coorX, y: coorY }); + setNextOption(nextOption === MAP_OPTION ? LOGO_OPTION : MAP_OPTION); + + drawCanvas(); + }; + + useEffect(() => { + async function init() { + await initCanvas(); + } + init(); + }, []); + + return { + mapImgRef, + logoImgRef, + canvasRef, + mouseDownHandler, + }; +} diff --git a/src/pages/Entry.tsx b/src/pages/Entry.tsx new file mode 100644 index 0000000..412aea8 --- /dev/null +++ b/src/pages/Entry.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import Canvas from '../components/Canvas'; +import styled from '../utils/styles/styled'; + +const BackGround = styled.div` + width: 100vw; + height: 100vh; + + background-color: ${(props) => props.theme.DEEPGREY}; +`; + +const Container = styled.div` + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + height: 60%; + + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; +`; + +const StartButton = styled.button` + width: 250px; + height: 60px; + + border: none; + border-radius: 40px; + font-size: 2.8rem; + font-weight: bold; + + color: ${(props) => props.theme.GREEN}; + background-color: ${(props) => props.theme.WHITE}; + &:hover { + background-color: ${(props) => props.theme.GREEN}; + color: ${(props) => props.theme.WHITE}; + } +`; + +function Entry(): React.ReactElement { + return ( + + + + 시작하기 + + + ); +} + +export default Entry; diff --git a/src/utils/styles/styled.ts b/src/utils/styles/styled.ts index 3a95bf6..5059f75 100644 --- a/src/utils/styles/styled.ts +++ b/src/utils/styles/styled.ts @@ -10,6 +10,7 @@ export const theme: ThemeType = { LIGHTGREY: '#E0E0E0', GREY: '#9E9E9E', DARKGREY: '#616161', + DEEPGREY: '#434343', WHITE: '#FFFFFF', GOOGLE_GREY: '#f5f5f5', }; From d2cc7036242b53bed84929335ba79d037838cd22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 00:12:17 +0900 Subject: [PATCH 036/138] =?UTF-8?q?[feat]=20[#79]=20mapbox-poi=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - mapbox-poi를 사이트를 참조해서 분리해놓음 - 전체적인 형식은 기존 poi형식을 따름 --- src/store/map/initializeMap.ts | 10 +++-- src/store/map/layers/mapbox-poi.ts | 70 ++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/store/map/layers/mapbox-poi.ts diff --git a/src/store/map/initializeMap.ts b/src/store/map/initializeMap.ts index 71aaa73..cf1e42a 100644 --- a/src/store/map/initializeMap.ts +++ b/src/store/map/initializeMap.ts @@ -2,10 +2,11 @@ import { RefObject } from 'react'; import mapboxgl from 'mapbox-gl'; import 'mapbox-gl/dist/mapbox-gl.css'; import dotenv from 'dotenv'; +import mapboxPOI from './layers/mapbox-poi'; -const LNG = 128; -const LAT = 36.5; -const ZOOM = 7; +const LNG = 126.978; +const LAT = 37.5656; +const ZOOM = 15.5; const LABELS = [ 'country-label', 'settlement-label', @@ -54,6 +55,9 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { map.on('load', () => { translate(map); + map.removeLayer('poi-label'); + + (mapboxPOI as mapboxgl.Layer[]).forEach((layer) => map.addLayer(layer)); }); return map; diff --git a/src/store/map/layers/mapbox-poi.ts b/src/store/map/layers/mapbox-poi.ts new file mode 100644 index 0000000..5882534 --- /dev/null +++ b/src/store/map/layers/mapbox-poi.ts @@ -0,0 +1,70 @@ +const mapboxPoiNameAndColor = [ + { name: 'food_and_drink', color: 'hsl(22, 55%, 55%)', layerName: 'food' }, + { name: 'park_like', color: 'hsl(100, 45%, 37%)', layerName: 'park' }, + { name: 'education', color: 'hsl(51, 40%, 40%)', layerName: 'education' }, + { name: 'medical', color: 'hsl(340, 39%, 42%)', layerName: 'medical' }, + { + name: 'arts_and_entertainment', + color: 'hsl(26, 25%, 32%)', + layerName: 'arts', + }, + { name: 'building', color: 'hsl(26, 25%, 32%)', layerName: 'building' }, + { name: 'general', color: 'hsl(26, 25%, 32%)', layerName: 'general' }, + { name: 'historic', color: 'hsl(26, 25%, 32%)', layerName: 'historic' }, + { name: 'industrial', color: 'hsl(26, 25%, 32%)', layerName: 'industrial' }, + { + name: 'public_facilities', + color: 'hsl(26, 25%, 32%)', + layerName: 'public_facilities', + }, + { name: 'religion', color: 'hsl(26, 25%, 32%)', layerName: 'religion' }, + { name: 'store_like', color: 'hsl(26, 25%, 32%)', layerName: 'store_like' }, +]; + +export default mapboxPoiNameAndColor.map((poi) => ({ + id: `poi-${poi.name}-label`, + type: 'symbol', + source: 'composite', + 'source-layer': 'poi_label', + metadata: {}, + minzoom: 6, + filter: ['==', ['get', 'class'], poi.name], + layout: { + 'text-size': 12, + 'icon-image': [ + 'step', + ['zoom'], + ['concat', ['get', 'maki'], '-11'], + 15, + ['concat', ['get', 'maki'], '-15'], + ], + 'text-offset': [ + 'step', + ['zoom'], + [ + 'step', + ['get', 'sizerank'], + ['literal', [0, 0]], + 5, + ['literal', [0, 0.75]], + ], + 17, + [ + 'step', + ['get', 'sizerank'], + ['literal', [0, 0]], + 13, + ['literal', [0, 0.75]], + ], + ], + 'text-anchor': 'top', + 'text-field': ['coalesce', ['get', 'name_ko'], ['get', 'name']], + }, + paint: { + 'icon-opacity': 1, + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 0.5, + 'text-halo-blur': 0.5, + 'text-color': poi.color, + }, +})); From 0b749de9e4877c643a75168e79a4b2d0bc75fe0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 11:11:28 +0900 Subject: [PATCH 037/138] =?UTF-8?q?[refactor]=20eslint=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - enum설정 중 알수 없는 에러가 자꾸떠서 껐다. - 알아보니 tslint와 eslint가 충돌해서 난다고 함 --- .eslintrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index e02b996..e8253b2 100644 --- a/.eslintrc +++ b/.eslintrc @@ -18,6 +18,7 @@ "react-hooks/exhaustive-deps": "off", "react/require-default-props": "off", "import/order": "off", - "import/prefer-default-export": "off" + "import/prefer-default-export": "off", + "no-shadow": "off" } } From d0998fd9854217613f3dd1ddd1ee81c64b377a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 11:13:17 +0900 Subject: [PATCH 038/138] =?UTF-8?q?[feat]=20Style=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=93=A4=EC=9D=98=20?= =?UTF-8?q?=EB=A7=A4=EC=A7=81=20=EC=8A=A4=ED=8A=B8=EB=A7=81=EC=9D=84=20enu?= =?UTF-8?q?m=ED=99=9C=EC=9A=A9=ED=95=B4=EC=84=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Style관련 하위 컴포넌트들을 onClick이벤트가 발생할 때 넘겨주는 string값을 enum을 이용해서 제거하였다 --- .../Sidebar/SidebarContentMore/ColorStyle.tsx | 3 +- .../Sidebar/SidebarContentMore/DetailType.tsx | 40 ++++++++++++++----- .../SidebarContentMore/LightnessStyle.tsx | 5 ++- .../SidebarContentMore/SaturationStyle.tsx | 5 ++- .../SidebarContentMore/VisibilityStyle.tsx | 3 +- .../SidebarContentMore/WeightStyle.tsx | 5 ++- 6 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx b/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx index c791208..3db1a54 100644 --- a/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx @@ -1,6 +1,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import useInputRange from '../../../hooks/common/useInputRange'; +import { StyleKeyName } from '../../../store/common/type'; const ColorWrapper = styled.div` display: flex; @@ -49,7 +50,7 @@ function ColorStyle({ type="color" id="styler__color" onChange={rangeChangeHandler} - onBlur={() => rangeMouseUpHandler('color')} + onBlur={() => rangeMouseUpHandler(StyleKeyName.color)} value={curRange} /> diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 1d8cc67..2168c5f 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -12,6 +12,8 @@ import { FeatureNameType, ElementNameType, SubElementNameType, + ElementName, + SubElementName, } from '../../../store/common/type'; interface PaddingProp { @@ -103,18 +105,26 @@ function DetailType({ 구역 {section?.fill.isChanged ? : <>} styleClickHandler('section', 'fill')} + clickHandler={() => { + styleClickHandler(ElementName.section, SubElementName.fill); + }} name="채우기" /> {section?.stroke.isChanged ? : <>} styleClickHandler('section', 'stroke')} + clickHandler={() => { + styleClickHandler(ElementName.section, SubElementName.stroke); + }} name="윤곽선" /> @@ -124,24 +134,34 @@ function DetailType({ 텍스트 {labelText?.fill.isChanged ? : <>} styleClickHandler('labelText', 'fill')} + clickHandler={() => { + styleClickHandler(ElementName.labelText, SubElementName.fill); + }} name="채우기" /> {labelText?.stroke.isChanged ? : <>} styleClickHandler('labelText', 'stroke')} + clickHandler={() => { + styleClickHandler(ElementName.labelText, SubElementName.stroke); + }} name="윤곽선" /> {labelIcon?.isChanged ? : <>} styleClickHandler('labelIcon')} + clickHandler={() => styleClickHandler(ElementName.labelIcon)} name="아이콘" /> diff --git a/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx b/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx index 5e23985..155807e 100644 --- a/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx @@ -2,6 +2,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; import useInputRange from '../../../hooks/common/useInputRange'; +import { StyleKeyName } from '../../../store/common/type'; const LightnessWrapper = styled.div` display: flex; @@ -22,7 +23,7 @@ const LightnessControlBar = styled(Range)` `; interface LightnessPropsInterface { - lightness: number; + lightness: string; onStyleChange: (key: string, value: string | number) => void; } @@ -47,7 +48,7 @@ function LightnessStyle({ id="styler__lightness" value={curRange} onChange={(e) => rangeChangeHandler(e)} - onMouseUp={() => rangeMouseUpHandler('lightness')} + onMouseUp={() => rangeMouseUpHandler(StyleKeyName.lightness)} /> diff --git a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx index f4f541a..fb4635b 100644 --- a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx @@ -2,6 +2,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; import useInputRange from '../../../hooks/common/useInputRange'; +import { StyleKeyName } from '../../../store/common/type'; const SaturationWrapper = styled.div` display: flex; @@ -22,7 +23,7 @@ const SaturationControlBar = styled(Range)` `; interface SaturationStyleProps { - saturation: number; + saturation: string; onStyleChange: (key: string, value: string | number) => void; } @@ -46,7 +47,7 @@ function SaturationStyle({ id="styler__saturation" value={curRange} onChange={(e) => rangeChangeHandler(e)} - onMouseUp={() => rangeMouseUpHandler('saturation')} + onMouseUp={() => rangeMouseUpHandler(StyleKeyName.saturation)} /> ); diff --git a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx index 3ae1adb..87bdb54 100644 --- a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx @@ -1,5 +1,6 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; +import { StyleKeyName } from '../../../store/common/type'; interface CheckedProp { checked: boolean; @@ -65,7 +66,7 @@ function VisibilityStyle({ {list.map((item) => ( onStyleChange('visibility', item.value)} + onClick={() => onStyleChange(StyleKeyName.visibility, item.value)} > diff --git a/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx b/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx index dc66cf8..762c537 100644 --- a/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx @@ -2,6 +2,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; import useInputRange from '../../../hooks/common/useInputRange'; +import { StyleKeyName } from '../../../store/common/type'; const WeightWrapper = styled.div` display: flex; @@ -22,7 +23,7 @@ const WeightControlBar = styled(Range)` `; interface WeightStyleProps { - weight: number; + weight: string; onStyleChange: (key: string, value: string | number) => void; } @@ -46,7 +47,7 @@ function WeightStyle({ id="styler__weight" value={curRange} onChange={rangeChangeHandler} - onMouseUp={() => rangeMouseUpHandler('weight')} + onMouseUp={() => rangeMouseUpHandler(StyleKeyName.weight)} /> ); From 21eebab4d1b71266e4c9bda41f6bcdc5ed924828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 11:15:06 +0900 Subject: [PATCH 039/138] =?UTF-8?q?[refactor]=20enum=EC=9D=84=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=EC=84=9C=20=EB=A7=A4=EC=A7=81=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A7=81=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=AA=85=20=EB=AA=85=EC=8B=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - enum을 이용해서 매직스트링을 제거하고 명시적으로 표기 - 기존 변수명들 중 명시적이지않은 부분을 변경 --- src/hooks/sidebar/useStyleType.ts | 3 ++- src/store/map/initializeMap.ts | 20 +++++++++++++------- src/store/style/compareStyle.ts | 5 ++--- src/store/style/getReducer.ts | 3 ++- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index 04ef08e..cc76e8a 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -7,6 +7,7 @@ import { StyleType, ElementNameType, SubElementNameType, + ElementName, } from '../../store/common/type'; import { setStyle } from '../../store/style/action'; import { getDefaultStyle } from '../../store/style/properties'; @@ -38,7 +39,7 @@ function useStyleType({ return getDefaultStyle(); } const feature = state[featureName][subFeatureName]; - if (detailName === 'labelIcon') return feature[detailName]; + if (detailName === ElementName.labelIcon) return feature[detailName]; return feature[detailName][subDetailName as SubElementNameType]; }) as StyleType; diff --git a/src/store/map/initializeMap.ts b/src/store/map/initializeMap.ts index 9ff6611..fcdbcfa 100644 --- a/src/store/map/initializeMap.ts +++ b/src/store/map/initializeMap.ts @@ -13,7 +13,7 @@ import mapboxPOI from './layers/mapbox-poi'; const LNG = 126.978; const LAT = 37.5656; const ZOOM = 15.5; -const LABELS = [ +const LABEL_LAYERS: string[] = [ 'country-label', 'settlement-label', 'road-label', @@ -28,6 +28,12 @@ const LABELS = [ 'natural-line-label', ]; +enum Sources { + polygon = 'polygon_source', + line = 'line_source', + poi = 'poi_source', +} + dotenv.config(); mapboxgl.accessToken = process.env.REACT_APP_ACCESS_TOKEN as string; @@ -36,7 +42,7 @@ interface InitializeMapProps { } function translate(map: mapboxgl.Map) { - LABELS.forEach((label) => { + LABEL_LAYERS.forEach((label) => { map.setLayoutProperty(label, 'text-field', ['get', 'name_ko']); }); } @@ -63,26 +69,26 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { translate(map); map.removeLayer('poi-label'); - map.addSource('polygon_source', { + map.addSource(Sources.polygon, { type: 'vector', tiles: ['http://110.93.147.18:8080/boostcamp/polygon/{x}/{y}/{z}'], }); - map.addSource('line_source', { + map.addSource(Sources.line, { type: 'vector', tiles: ['http://110.93.147.18:8080/boostcamp/line/{x}/{y}/{z}'], }); - map.addSource('poi_source', { + map.addSource(Sources.poi, { type: 'vector', tiles: ['http:/110.93.147.18:8080/boostcamp/poi/{x}/{y}/{z}'], }); const layers = [ - ...mapboxPOI, ...road, ...transit, - ...poi, ...water, ...landscape, + ...mapboxPOI, + ...poi, ] as mapboxgl.Layer[]; layers.forEach((layer: mapboxgl.Layer) => map.addLayer(layer)); }); diff --git a/src/store/style/compareStyle.ts b/src/store/style/compareStyle.ts index b1587cc..88459d7 100644 --- a/src/store/style/compareStyle.ts +++ b/src/store/style/compareStyle.ts @@ -1,5 +1,5 @@ import { getDefaultStyle } from './properties'; -import { StyleType, StyleKeyType, objType } from '../common/type'; +import { StyleType, StyleKeyType, objType, StyleKeyName } from '../common/type'; export function checkStyleIsChanged(targetStyle: StyleType): boolean { const defaultStyle: StyleType = getDefaultStyle(); @@ -7,8 +7,7 @@ export function checkStyleIsChanged(targetStyle: StyleType): boolean { const filteredKeys = keys.filter( (key) => - key === 'isChanged' || - String(defaultStyle[key]) === String(targetStyle[key]) + key === StyleKeyName.isChanged || defaultStyle[key] === targetStyle[key] ); return keys.length !== filteredKeys.length; } diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts index 7fcde3e..e28724a 100644 --- a/src/store/style/getReducer.ts +++ b/src/store/style/getReducer.ts @@ -6,6 +6,7 @@ import { ActionType, SubElementNameType, objType, + ElementName, } from '../common/type'; import { getDefaultFeature } from './properties'; import { INIT, SET } from './action'; @@ -49,7 +50,7 @@ export default function getReducer(IDX: number): ReducerType { const newFeature: FeatureType = newState[subFeature as string]; let prevIsChanged; - if (element === 'labelIcon') { + if (element === ElementName.labelIcon) { prevIsChanged = newFeature[element].isChanged; newFeature[element] = style; } else { From 1056cd91d713931bf9cd947004458c95e644c145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 11:16:04 +0900 Subject: [PATCH 040/138] =?UTF-8?q?[refactor]=20map=EC=9D=98=20naver=20lay?= =?UTF-8?q?er=EB=93=A4=20=EC=A4=91=20=EC=88=98=EC=A0=95=EC=9D=B4=20?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EB=B6=80=EB=B6=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - naver layer들중에서 변수명이 겹치거나 선언이 잘못되어 있는 부분 수정 --- src/store/map/layers/poi.ts | 2 +- src/store/map/layers/transit.ts | 18 ++---------------- src/store/map/layers/water.ts | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src/store/map/layers/poi.ts b/src/store/map/layers/poi.ts index 5c5b6d2..032a646 100644 --- a/src/store/map/layers/poi.ts +++ b/src/store/map/layers/poi.ts @@ -309,7 +309,7 @@ export default [ paint: { 'text-color': 'green', }, - filter: ['match', ['get', 'type'], 'place_of_worship'], + filter: ['match', ['get', 'type'], ['place_of_worship'], true, false], id: 'poi-worship', }, { diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts index 72b9cd8..7828095 100644 --- a/src/store/map/layers/transit.ts +++ b/src/store/map/layers/transit.ts @@ -12,20 +12,6 @@ export default [ filter: ['==', ['get', 'type'], 'subway'], id: 'transit-subway', }, - { - type: 'symbol', - source: 'poi_source', - 'source-layer': 'poi', - layout: { - 'text-field': ['get', 'name'], - visibility: 'visible', - }, - paint: { - 'text-color': 'green', - }, - filter: ['==', ['get', 'type'], 'taxi'], - id: 'transit-bus', - }, { type: 'line', source: 'line_source', @@ -48,9 +34,9 @@ export default [ visibility: 'visible', }, paint: { - 'text-color': 'pink', + 'text-color': 'green', }, - filter: ['match', ['get', 'type'], ['taxi'], true, false], + filter: ['==', ['get', 'type'], 'taxi'], id: 'transit-bus', }, ]; diff --git a/src/store/map/layers/water.ts b/src/store/map/layers/water.ts index 3e8ad46..a4d6b9f 100644 --- a/src/store/map/layers/water.ts +++ b/src/store/map/layers/water.ts @@ -1,29 +1,29 @@ export default [ { - type: 'line', - source: 'line_source', - 'source-layer': 'line', + type: 'fill', + source: 'polygon_source', + 'source-layer': 'polygon', layout: { visibility: 'visible', }, paint: { - 'line-color': 'blue', + 'fill-color': '#000000', + 'fill-opacity': 0.5, }, - filter: ['match', ['get', 'type'], ['river', 'stream'], true, false], - id: 'water-section', + filter: ['==', ['get', 'type'], 'water'], + id: 'water-polygon', }, { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', + type: 'line', + source: 'line_source', + 'source-layer': 'line', layout: { visibility: 'visible', }, paint: { - 'fill-color': '#000000', - 'fill-opacity': 0.5, + 'line-color': 'blue', }, - filter: ['==', ['get', 'type'], 'water'], + filter: ['match', ['get', 'type'], ['river', 'stream'], true, false], id: 'water-line', }, ]; From 78ce2888493fe17039a005fde387c90e371f0744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 11:16:46 +0900 Subject: [PATCH 041/138] =?UTF-8?q?[refactor]=20type=EB=93=A4=EC=A4=91?= =?UTF-8?q?=EC=97=90=EC=84=9C=20enum=EC=9C=BC=EB=A1=9C=20=EC=9E=AC?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=84=B1=EC=9D=84=20=EB=86=92=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - type들중 enum으로 다른곳에서 활용 가능한 부분들을 묶음 --- src/store/common/type.ts | 63 ++++++++++++++++++++++------------- src/store/style/properties.ts | 6 ++-- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 3e65f44..d2e169d 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,27 +1,42 @@ import { init, setStyle } from '../style/action'; -export type ActionType = ReturnType | ReturnType; -export type ElementNameType = 'section' | 'labelText' | 'labelIcon'; -export type SubElementNameType = 'fill' | 'stroke'; - -export type FeatureNameType = - | 'poi' - | 'administrative' - | 'landscape' - | 'road' - | 'transit' - | 'water' - | 'marker'; - -export type StyleKeyType = - | 'visibility' - | 'color' - | 'weight' - | 'saturation' - | 'lightness' - | 'isChanged'; +export enum ElementName { + section = 'section', + labelText = 'labelText', + labelIcon = 'labelIcon', +} + +export enum SubElementName { + fill = 'fill', + stroke = 'stroke', +} + +export enum StyleKeyName { + visibility = 'visibility', + color = 'color', + weight = 'weight', + saturation = 'saturation', + lightness = 'lightness', + isChanged = 'isChanged', +} + +export enum FeatureName { + poi = 'poi', + administrative = 'administrative', + landscape = 'landscape', + road = 'road', + transit = 'transit', + water = 'water', + marker = 'marker', +} + +export type ElementNameType = keyof typeof ElementName; +export type SubElementNameType = keyof typeof SubElementName; +export type FeatureNameType = keyof typeof FeatureName; +export type StyleKeyType = keyof typeof StyleKeyName; export interface objType { + // eslint-disable-next-line @typescript-eslint/no-explicit-any [name: string]: any; } @@ -29,9 +44,9 @@ export interface StyleType { isChanged: boolean; visibility: string; color: string; - weight: number; - saturation: number; - lightness: number; + weight: string; + saturation: string; + lightness: string; } export interface ElementType { fill: StyleType; @@ -47,6 +62,8 @@ export interface FeatureState { [name: string]: FeatureType; } +export type ActionType = ReturnType | ReturnType; + export type ActionPayload = { feature: FeatureNameType; subFeature?: string; diff --git a/src/store/style/properties.ts b/src/store/style/properties.ts index 83b0051..b1b3661 100644 --- a/src/store/style/properties.ts +++ b/src/store/style/properties.ts @@ -4,9 +4,9 @@ const style: StyleType = { isChanged: false, visibility: 'inherit', color: '#55bf40', - weight: 0, - saturation: 0, - lightness: 0, + weight: '0', + saturation: '0', + lightness: '0', }; export const getDefaultStyle = (): StyleType => { From 3ad1ea025eb370ae386411241641909e47f2ae81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 12:13:55 +0900 Subject: [PATCH 042/138] =?UTF-8?q?[refactor]=20poi=20layer=EC=97=90?= =?UTF-8?q?=EC=84=9C=20polygon=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - poi layer에서 polygon속성을 제거 후 landscape의 building레이어에 포함시켰다 --- src/store/map/layers/landscape.ts | 33 +++++- src/store/map/layers/poi.ts | 171 ------------------------------ src/store/map/layers/water.ts | 2 +- 3 files changed, 32 insertions(+), 174 deletions(-) diff --git a/src/store/map/layers/landscape.ts b/src/store/map/layers/landscape.ts index 376db32..f9a9724 100644 --- a/src/store/map/layers/landscape.ts +++ b/src/store/map/layers/landscape.ts @@ -37,6 +37,8 @@ export default [ 'footway', 'pedestrian', 'parking', + 'fountain', + 'gate', ], }, { @@ -47,10 +49,25 @@ export default [ visibility: 'visible', }, paint: { - 'fill-color': 'black', + 'fill-color': 'skyblue', 'fill-opacity': 0.5, }, - filter: ['in', 'type', 'retail', 'commercial'], + filter: [ + 'match', + ['get', 'type'], + [ + 'restaurant', + 'cafe', + 'marketplace', + 'fast_food', + 'bar', + 'pub', + 'casino', + 'cinema', + ], + true, + false, + ], id: 'landscape-commercial', }, { @@ -98,6 +115,18 @@ export default [ 'public_bath', 'community_centre', 'theatre', + 'fire_station', + 'post_office', + 'police', + 'townhall', + 'public', + 'college', + 'language_school', + 'arts_centre', + 'palace', + 'toilets', + 'social_facility', + '', ], id: 'landscape-building', }, diff --git a/src/store/map/layers/poi.ts b/src/store/map/layers/poi.ts index 032a646..7afc001 100644 --- a/src/store/map/layers/poi.ts +++ b/src/store/map/layers/poi.ts @@ -1,175 +1,4 @@ export default [ - // POI - polygon start - { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', - layout: { - visibility: 'visible', - }, - paint: { - 'fill-color': 'skyblue', - 'fill-opacity': 0.5, - }, - filter: [ - 'match', - ['get', 'type'], - ['arts_centre', 'fountain', 'gate', 'palace', 'chungmoo art hall'], - true, - false, - ], - id: 'poi-attraction-polygon', - }, - { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', - layout: { - visibility: 'visible', - }, - paint: { - 'fill-color': 'skyblue', - 'fill-opacity': 0.5, - }, - filter: [ - 'match', - ['get', 'type'], - [ - 'restaurant', - 'cafe', - 'marketplace', - 'fast_food', - 'bar', - 'pub', - 'casino', - 'cinema', - '', - ], - true, - false, - ], - id: 'poi-business-polygon', - }, - { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', - layout: { - visibility: 'visible', - }, - paint: { - 'fill-color': 'skyblue', - 'fill-opacity': 0.5, - }, - filter: [ - 'match', - ['get', 'type'], - [ - 'fire_station', - 'post_office', - 'police', - 'townhall', - 'public', - 'ranger_station', - 'government', - 'bunker', - ], - true, - false, - ], - id: 'poi-government-polygon', - }, - { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', - layout: { - visibility: 'visible', - }, - paint: { - 'fill-color': 'lightgrey', - 'fill-opacity': 0.5, - }, - filter: ['match', ['get', 'type'], ['hospital', 'pharmacy'], true, false], - id: 'poi-medical-polygon', - }, - { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', - layout: { - visibility: 'visible', - }, - paint: { - 'fill-color': 'red', - 'fill-opacity': 0.5, - }, - filter: [ - 'match', - ['get', 'type'], - ['place_of_worship', 'cathedral', 'chapel', 'church'], - true, - false, - ], - id: 'poi-worship-polygon', - }, - { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', - layout: { - visibility: 'visible', - }, - paint: { - 'fill-color': 'brown', - 'fill-opacity': 0.5, - }, - filter: [ - 'in', - 'type', - 'college', - 'university', - 'language_school', - 'school', - 'kindergarten', - ], - id: 'poi-school-polygon', - }, - { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', - layout: { - visibility: 'visible', - }, - paint: { - 'fill-color': 'lightgrey', - 'fill-opacity': 0.5, - }, - filter: [ - 'match', - ['get', 'type'], - ['toilets', 'social_facility', 'conference_centre'], - true, - false, - ], - id: 'poi-etc-polygon', - }, - { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', - layout: { - visibility: 'visible', - }, - paint: { - 'fill-color': 'skyblue', - 'fill-opacity': 0.5, - }, - filter: ['match', ['get', 'type'], 'swimming_pool', true, false], - id: 'poi-sports-polygon', - }, - // POI - polygon end { type: 'symbol', source: 'poi_source', diff --git a/src/store/map/layers/water.ts b/src/store/map/layers/water.ts index a4d6b9f..6b5642c 100644 --- a/src/store/map/layers/water.ts +++ b/src/store/map/layers/water.ts @@ -7,7 +7,7 @@ export default [ visibility: 'visible', }, paint: { - 'fill-color': '#000000', + 'fill-color': 'hsl(243, 57%, 50%)', 'fill-opacity': 0.5, }, filter: ['==', ['get', 'type'], 'water'], From 6f36f5361e73a34316e9448a7fb7ed483392fd0a Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Tue, 1 Dec 2020 14:58:09 +0900 Subject: [PATCH 043/138] =?UTF-8?q?[fix]=20map-styling=EC=97=90=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20key=20=EC=A0=84=EB=8B=AC=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - key를 통해 설정된 유형 구분하려고 함 - key의 타입을 StyleKeyName으로 지정 - 타입 지정에 따라 타입스크립트 관련 에러 발생했고, 해당 부분도 string에서 StyleKeyName으로 수정하였음 --- src/components/Sidebar/SidebarContentMore/ColorStyle.tsx | 2 +- .../Sidebar/SidebarContentMore/LightnessStyle.tsx | 2 +- .../Sidebar/SidebarContentMore/SaturationStyle.tsx | 2 +- .../Sidebar/SidebarContentMore/VisibilityStyle.tsx | 2 +- src/components/Sidebar/SidebarContentMore/WeightStyle.tsx | 2 +- src/hooks/common/useInputRange.ts | 7 ++++--- src/hooks/sidebar/useStyleType.ts | 6 ++++-- src/utils/map-styling/index.ts | 2 ++ 8 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx b/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx index 3db1a54..6bba068 100644 --- a/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx @@ -30,7 +30,7 @@ const ColorPalette = styled.input` interface ColorStyleProps { color: string; - onStyleChange: (key: string, value: string | number) => void; + onStyleChange: (key: StyleKeyName, value: string | number) => void; } function ColorStyle({ diff --git a/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx b/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx index 155807e..28bc1d1 100644 --- a/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx @@ -24,7 +24,7 @@ const LightnessControlBar = styled(Range)` interface LightnessPropsInterface { lightness: string; - onStyleChange: (key: string, value: string | number) => void; + onStyleChange: (key: StyleKeyName, value: string | number) => void; } function LightnessStyle({ diff --git a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx index fb4635b..1f75552 100644 --- a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx @@ -24,7 +24,7 @@ const SaturationControlBar = styled(Range)` interface SaturationStyleProps { saturation: string; - onStyleChange: (key: string, value: string | number) => void; + onStyleChange: (key: StyleKeyName, value: string | number) => void; } function SaturationStyle({ diff --git a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx index 87bdb54..9a5a186 100644 --- a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx @@ -47,7 +47,7 @@ const Circle = styled.div` interface VisibilityStyleProps { visibility: string; - onStyleChange: (key: string, value: string | number) => void; + onStyleChange: (key: StyleKeyName, value: string | number) => void; } function VisibilityStyle({ diff --git a/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx b/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx index 762c537..b69c10d 100644 --- a/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx @@ -24,7 +24,7 @@ const WeightControlBar = styled(Range)` interface WeightStyleProps { weight: string; - onStyleChange: (key: string, value: string | number) => void; + onStyleChange: (key: StyleKeyName, value: string | number) => void; } function WeightStyle({ diff --git a/src/hooks/common/useInputRange.ts b/src/hooks/common/useInputRange.ts index d82a72c..b3150e7 100644 --- a/src/hooks/common/useInputRange.ts +++ b/src/hooks/common/useInputRange.ts @@ -1,14 +1,15 @@ import React, { useState, useEffect } from 'react'; +import { StyleKeyName } from '../../store/common/type'; interface UseInputRangeProps { range: string | number; - onStyleChange: (key: string, value: string | number) => void; + onStyleChange: (key: StyleKeyName, value: string | number) => void; } interface InputRangeHookType { curRange: string | number; rangeChangeHandler: (e: React.ChangeEvent) => void; - rangeMouseUpHandler: (key: string) => void; + rangeMouseUpHandler: (key: StyleKeyName) => void; } function useInputRange({ @@ -25,7 +26,7 @@ function useInputRange({ setCurRange(e.target.value); }; - const rangeMouseUpHandler = (key: string) => { + const rangeMouseUpHandler = (key: StyleKeyName) => { onStyleChange(key, curRange); }; diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index cc76e8a..ed13175 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -8,6 +8,7 @@ import { ElementNameType, SubElementNameType, ElementName, + StyleKeyName, } from '../../store/common/type'; import { setStyle } from '../../store/style/action'; import { getDefaultStyle } from '../../store/style/properties'; @@ -22,7 +23,7 @@ interface UseStyleTypeProps { export interface UseStyleHookType { styleElement: StyleType; - onStyleChange: (key: string, value: string | number) => void; + onStyleChange: (key: StyleKeyName, value: string | number) => void; } function useStyleType({ @@ -44,12 +45,13 @@ function useStyleType({ }) as StyleType; const onStyleChange = useCallback( - (key: string, value: string | number) => { + (key: StyleKeyName, value: string | number) => { mapStyling[featureName]({ map, subFeatureName, detailName, subDetailName: subDetailName as SubElementNameType, + key, style: { ...styleElement, [key]: value, diff --git a/src/utils/map-styling/index.ts b/src/utils/map-styling/index.ts index 59c7af9..2685a4d 100644 --- a/src/utils/map-styling/index.ts +++ b/src/utils/map-styling/index.ts @@ -2,6 +2,7 @@ import mapboxgl from 'mapbox-gl'; import { ElementNameType, SubElementNameType, + StyleKeyName, StyleType, } from '../../store/common/type'; @@ -10,6 +11,7 @@ export interface stylingProps { subFeatureName: string; detailName: ElementNameType; subDetailName: SubElementNameType; + key: StyleKeyName; style: StyleType; } From 30ccf3f65194d0c6cdf65a5e556a8f6f0894c251 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Tue, 1 Dec 2020 14:59:24 +0900 Subject: [PATCH 044/138] =?UTF-8?q?[refactor]=20Mapbox=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=86=8D=EC=84=B1=20enum=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 세부 스타일 조정할 때 반복해서 사용되므로 enum으로 설정하였음 --- src/utils/applyStyle.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index 940154e..fef28fd 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -1,8 +1,22 @@ import mapboxgl from 'mapbox-gl'; import { hexToHSL } from './colorFormat'; -type colorType = 'fill-color' | 'line-color' | 'text-color' | 'text-halo-color'; -type weightType = 'line-width' | 'text-halo-width' | 'text-size'; +export enum ColorTypeName { + 'fill-color' = 'fill-color', + 'line-color' = 'line-color', + 'text-color' = 'text-color', + 'text-halo-color' = 'text-halo-color', +} + +export enum WeightTypeName { + 'line-width' = 'line-width', + 'text-size' = 'text-size', + 'text-halo-width' = 'text-halo-width', +} + +type colorType = keyof typeof ColorTypeName; + +type weightType = keyof typeof WeightTypeName; export function applyVisibility( map: mapboxgl.Map, From 6eda66c27e6a4c22abc9003d085dafb604a3e6e5 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Tue, 1 Dec 2020 15:55:53 +0900 Subject: [PATCH 045/138] =?UTF-8?q?[fix]=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 텍스트 사이즈 조정은 상의 후 제거 - mapbox 속성에 따라서 set 함수 달라서 조정함 - 색상/채도/명도 중복되는 요소 많아서 하나로 조정 --- src/utils/applyStyle.ts | 90 ++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index fef28fd..d2cef38 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -10,7 +10,6 @@ export enum ColorTypeName { export enum WeightTypeName { 'line-width' = 'line-width', - 'text-size' = 'text-size', 'text-halo-width' = 'text-halo-width', } @@ -18,59 +17,66 @@ type colorType = keyof typeof ColorTypeName; type weightType = keyof typeof WeightTypeName; +interface ApplyColorProps { + map: mapboxgl.Map; + layerNames: string[]; + color: string; + type: colorType; + saturation?: number; + lightness?: number; +} + export function applyVisibility( map: mapboxgl.Map, - layerName: string, + layerNames: string[], visibility: string ): void { - map.setLayoutProperty(layerName, 'visibility', visibility); + layerNames.forEach((layerName) => { + map.setLayoutProperty(layerName, 'visibility', visibility); + }); } -export function applyColor( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string -): void { +export function applyColor({ + map, + layerNames, + color, + type, + saturation, + lightness, +}: ApplyColorProps): void { const { h, s, l } = hexToHSL(color); - map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); + if (saturation) { + layerNames.forEach((layerName) => { + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${50 + saturation / 2}%, ${l}%)` + ); + }); + return; + } + if (lightness) { + layerNames.forEach((layerName) => { + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${s}%, ${50 + lightness / 2}%)` + ); + }); + return; + } + layerNames.forEach((layerName) => { + map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); + }); } export function applyWeight( map: mapboxgl.Map, - layerName: string, + layerNames: string[], type: weightType, weight: number ): void { - map.setPaintProperty(layerName, type, weight * 2 + 1); -} - -export function applySaturation( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string, - saturation: number -): void { - const { h, l } = hexToHSL(color); - map.setPaintProperty( - layerName, - type, - `hsl(${h}, ${50 + saturation / 2}%, ${l}%)` - ); -} - -export function applyLightness( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string, - lightness: number -): void { - const { h, s } = hexToHSL(color); - map.setPaintProperty( - layerName, - type, - `hsl(${h}, ${s}%, ${50 + lightness / 2}%)` - ); + layerNames.forEach((layerName) => { + map.setPaintProperty(layerName, type, weight * 2 + 1); + }); } From de83978c7281a4a375fa64cc08e319a0b5e69b78 Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Tue, 1 Dec 2020 17:04:35 +0900 Subject: [PATCH 046/138] =?UTF-8?q?[fix]=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20=ED=98=95=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 스타일 적용시 key값을 넘겨주는 것을 추가 --- src/hooks/sidebar/useStyleType.ts | 1 + src/utils/map-styling/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index cc76e8a..2227745 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -49,6 +49,7 @@ function useStyleType({ map, subFeatureName, detailName, + key, subDetailName: subDetailName as SubElementNameType, style: { ...styleElement, diff --git a/src/utils/map-styling/index.ts b/src/utils/map-styling/index.ts index 59c7af9..9e203c1 100644 --- a/src/utils/map-styling/index.ts +++ b/src/utils/map-styling/index.ts @@ -9,6 +9,7 @@ export interface stylingProps { map: mapboxgl.Map; subFeatureName: string; detailName: ElementNameType; + key: string; subDetailName: SubElementNameType; style: StyleType; } From c6f5695462bf3286d3aad16e3cfa32b6ff7ec67d Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Tue, 1 Dec 2020 17:05:36 +0900 Subject: [PATCH 047/138] =?UTF-8?q?[fix]=20applyColor=20=ED=98=95=EC=8B=9D?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - applyColor를 color, saturation, lightness 모두 변경가능하도록 수정 --- src/utils/applyStyle.ts | 108 ++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index 940154e..d362acf 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -1,62 +1,82 @@ import mapboxgl from 'mapbox-gl'; import { hexToHSL } from './colorFormat'; -type colorType = 'fill-color' | 'line-color' | 'text-color' | 'text-halo-color'; -type weightType = 'line-width' | 'text-halo-width' | 'text-size'; +export enum ColorTypeName { + 'fill-color' = 'fill-color', + 'line-color' = 'line-color', + 'text-color' = 'text-color', + 'text-halo-color' = 'text-halo-color', +} + +export enum WeightTypeName { + 'line-width' = 'line-width', + 'text-size' = 'text-size', + 'text-halo-width' = 'text-halo-width', +} + +type colorType = keyof typeof ColorTypeName; +type weightType = keyof typeof WeightTypeName; + +interface ApplyColorProps { + map: mapboxgl.Map; + layerNames: string[]; + color: string; + type: colorType; + saturation?: string; + lightness?: string; +} export function applyVisibility( map: mapboxgl.Map, - layerName: string, + layerNames: string[], visibility: string ): void { - map.setLayoutProperty(layerName, 'visibility', visibility); + layerNames.forEach((layerName) => { + if (visibility === 'inherit') { + map.setLayoutProperty(layerName, 'visibility', 'visible'); + } else map.setLayoutProperty(layerName, 'visibility', visibility); + }); } -export function applyColor( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string -): void { +export function applyColor({ + map, + layerNames, + color, + type, + saturation, + lightness, +}: ApplyColorProps): void { const { h, s, l } = hexToHSL(color); - map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); + if (saturation) { + return layerNames.forEach((layerName) => { + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${50 + parseInt(saturation, 10) / 2}%, ${l}%)` + ); + }); + } + if (lightness) { + return layerNames.forEach((layerName) => { + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${s}%, ${50 + parseInt(lightness, 10) / 2}%)` + ); + }); + } + return layerNames.forEach((layerName) => { + map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); + }); } export function applyWeight( map: mapboxgl.Map, - layerName: string, + layerNames: string[], type: weightType, - weight: number -): void { - map.setPaintProperty(layerName, type, weight * 2 + 1); -} - -export function applySaturation( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string, - saturation: number -): void { - const { h, l } = hexToHSL(color); - map.setPaintProperty( - layerName, - type, - `hsl(${h}, ${50 + saturation / 2}%, ${l}%)` - ); -} - -export function applyLightness( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string, - lightness: number + weight: string ): void { - const { h, s } = hexToHSL(color); - map.setPaintProperty( - layerName, - type, - `hsl(${h}, ${s}%, ${50 + lightness / 2}%)` - ); + layerNames.forEach((layerName) => { + map.setPaintProperty(layerName, type, parseInt(weight, 10) * 2 + 1); + }); } From 6ab2c818fed32bfa1d1dbaab9aa34dd9db778f5d Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Tue, 1 Dec 2020 17:06:32 +0900 Subject: [PATCH 048/138] =?UTF-8?q?[feat]=20[#81]=20=EC=A3=BC=EC=9A=94?= =?UTF-8?q?=EB=8F=84=EB=A1=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 주요도로 스타일 적용기능 추가 - inherit visibility는 아직 visible로만 처리 - line section fill or stroke에 대한 것은 동일하게 취급 --- src/utils/map-styling/road.ts | 98 ++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index 85c72af..f95b92e 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -1,13 +1,109 @@ import { stylingProps } from '.'; +import { applyVisibility, applyColor, applyWeight } from '../applyStyle'; + +type KeyType = 'visibility' | 'color' | 'weight' | 'saturation' | 'lightness'; function roadStyling({ map, subFeatureName, detailName, subDetailName, + key, style, }: stylingProps): void { - console.log(1); + const { visibility, color, weight, saturation, lightness } = style; + const arterialLayerNames = [ + 'road-number-shield', + 'road-exit-shield', + 'road-label', + 'road-arterial', + ]; + const localLayerNames = ['ferry', 'ferry-auto', 'road-local']; + const sidewalkLayerNames = ['road-footway']; + + /** + * visibility 가 바뀌는 것 우선은 여기에서 처리, inherit (visible 처리) + * color, saturation, lightness 한 함수로 처리 + * - section + * - fill : line 'line-color', symbol, polygon 'fill-color' + * - stroke : line 'case layer'의 색상, symbol 불변, polygon이면 불변 + * - label + * - text fill: 'text-color', 'text-width' + * - text stroke: 'text-halo-color', 'text-halo-width' + * + * cf. 채도와 밝기가 크게 달라지는 것 같지 않다.. line인 탓일까..? + */ + + /** + * highway : 분류 보류 + * aterial: line이라 채우기 윤곽선은 한번에 처리, 채우기 윤곽선 확인완료, 아이콘 없음 + * local + * sidewalk + */ + + if (subFeatureName === 'arterial') { + if (detailName === 'section' && key === 'visibility') { + applyVisibility(map, arterialLayerNames, visibility); + } else if (key === ('color' || 'saturation' || 'lightness')) { + if (detailName === 'section' && subDetailName === 'fill') { + applyColor({ + map, + layerNames: ['road-arterial'], + color, + type: 'line-color', + saturation, + lightness, + }); + } else if (detailName === 'labelText' && subDetailName === 'fill') { + applyColor({ + map, + layerNames: ['road-number-shield', 'road-exit-shield', 'road-label'], + color, + type: 'text-color', + saturation, + lightness, + }); + } else if (detailName === 'labelText' && subDetailName === 'stroke') { + applyColor({ + map, + layerNames: ['road-label'], + color, + type: 'text-halo-color', + saturation, + lightness, + }); + } + } else if (key === 'weight') { + if (detailName === 'section' && subDetailName === 'fill') { + applyWeight(map, ['road-arterial'], 'line-width', weight); + } + if (detailName === 'labelText' && subDetailName === 'stroke') + applyWeight(map, ['road-label'], 'text-halo-width', weight); + } + } } +// if (subFeatureName === ('highway' || 'bicycle-road')) { +// /** +// * There is no highway layer and bicycle-road +// */ +// } else + +// else if (subFeatureName === 'local') { +// if (key === 'visibility') applyVisibility(map, localLayerNames, visibility); +// } else if (subFeatureName === 'sidewalk') { +// // weight 관련 속성 없음 +// if (key === 'visibility') +// applyVisibility(map, sidewalkLayerNames, visibility); +// if (key === ('color' || 'saturation' || 'lightness')) { +// applyColor({ +// map, +// layerNames: sidewalkLayerNames, +// color, +// type: 'line-color', +// saturation, +// lightness, +// }); +// } + export default roadStyling; From a5cb6eca0d1e785a9270ddeb8b3a0cd1b80db68e Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Tue, 1 Dec 2020 17:23:02 +0900 Subject: [PATCH 049/138] =?UTF-8?q?[feat]=20[#81]=20=EC=9D=BC=EB=B0=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EA=B0=80=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일반도로 스타일 변경 가능 - 하지만 현재 존재하는 일반도로 레이어들이 section -> fill 관련 요소에만 의미있는 변경을 하는 데이터들 - 관련 데이터 추가 필요 --- src/utils/map-styling/road.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index f95b92e..4c59070 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -35,9 +35,10 @@ function roadStyling({ */ /** + * 20.12.01. * highway : 분류 보류 * aterial: line이라 채우기 윤곽선은 한번에 처리, 채우기 윤곽선 확인완료, 아이콘 없음 - * local + * local: 지금 있는 것들은 section fill밖에 의미없음 * sidewalk */ @@ -80,6 +81,28 @@ function roadStyling({ if (detailName === 'labelText' && subDetailName === 'stroke') applyWeight(map, ['road-label'], 'text-halo-width', weight); } + } else if (subFeatureName === 'local') { + if (key === 'visibility') applyVisibility(map, localLayerNames, visibility); + else if (key === ('color' || 'saturation' || 'lightness')) { + if (detailName === 'section' && subDetailName === 'fill') { + applyColor({ + map, + layerNames: ['ferry', 'ferry-auto', 'road-local'], + color, + type: 'line-color', + saturation, + lightness, + }); + } + } else if (key === 'weight') { + if (detailName === 'section' && subDetailName === 'fill') + applyWeight( + map, + ['ferry', 'ferry-auto', 'road-local'], + 'line-width', + weight + ); + } } } From 4383336f729a69c3a8e07a6b96a3ca2cef4e07cc Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Tue, 1 Dec 2020 19:34:52 +0900 Subject: [PATCH 050/138] =?UTF-8?q?[fix]=20=EC=8A=A4=ED=83=80=EC=9D=BC=20w?= =?UTF-8?q?eight=20=EC=86=8D=EC=84=B1=EA=B0=92=200=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 0인 경우에는 보이지 않도록 수정 하였음 --- src/utils/applyStyle.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index d2cef38..7f56716 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -76,7 +76,8 @@ export function applyWeight( type: weightType, weight: number ): void { + const weightValue = weight === 0 ? 0 : weight * 2 + 1; layerNames.forEach((layerName) => { - map.setPaintProperty(layerName, type, weight * 2 + 1); + map.setPaintProperty(layerName, type, weightValue); }); } From b2c986154d608a3fb1567ea88b4a0d1e8abd2b7e Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Tue, 1 Dec 2020 19:38:26 +0900 Subject: [PATCH 051/138] =?UTF-8?q?[fix]=20=EB=9E=9C=EB=8D=94=EB=A7=81=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - water way 지도상에 표시되지 않는 요소로 불필요해서 제거함 - 우리나라의 경우 맵박스는 시/구/동 구분이 되지 않아서 그외로 통합함 --- src/store/map/layers/water.ts | 13 ------------- src/utils/rendering-data/featureTypeData.ts | 7 +++---- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/store/map/layers/water.ts b/src/store/map/layers/water.ts index 6b5642c..9a5d5f4 100644 --- a/src/store/map/layers/water.ts +++ b/src/store/map/layers/water.ts @@ -13,17 +13,4 @@ export default [ filter: ['==', ['get', 'type'], 'water'], id: 'water-polygon', }, - { - type: 'line', - source: 'line_source', - 'source-layer': 'line', - layout: { - visibility: 'visible', - }, - paint: { - 'line-color': 'blue', - }, - filter: ['match', ['get', 'type'], ['river', 'stream'], true, false], - id: 'water-line', - }, ]; diff --git a/src/utils/rendering-data/featureTypeData.ts b/src/utils/rendering-data/featureTypeData.ts index 51893c5..747c971 100644 --- a/src/utils/rendering-data/featureTypeData.ts +++ b/src/utils/rendering-data/featureTypeData.ts @@ -41,10 +41,9 @@ const data: DataType[] = [ typeKey: 'administrative', typeName: '행정구역', features: [ - { key: 'countryLabel', name: '국가' }, - { key: 'stateLabel', name: '도/주' }, - { key: 'localityLabel', name: '시' }, - { key: 'neighborLabel', name: '그외' }, + { key: 'country', name: '국가' }, + { key: 'state', name: '도/주' }, + { key: 'locality', name: '그외' }, ], }, { From 18bd08f076dd5efdae89be6204b7466204d3a99d Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Tue, 1 Dec 2020 19:45:30 +0900 Subject: [PATCH 052/138] =?UTF-8?q?[feat]=20=EB=AC=BC=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=84=A4=EC=A0=95=20=ED=95=A8=EC=88=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 물 구역-윤곽선, 라벨아이콘 포함되지 않음 --- src/utils/map-styling/water.ts | 82 +++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/src/utils/map-styling/water.ts b/src/utils/map-styling/water.ts index e13fa2d..2261cb5 100644 --- a/src/utils/map-styling/water.ts +++ b/src/utils/map-styling/water.ts @@ -1,13 +1,91 @@ import { stylingProps } from '.'; +import { + ElementName, + SubElementName, + StyleKeyName, +} from '../../store/common/type'; +import { + ColorTypeName, + WeightTypeName, + applyVisibility, + applyColor, + applyWeight, +} from '../applyStyle'; + +const layers = { + polygon: ['water-polygon', 'water'], + symbol: ['water-line-label', 'water-point-label', 'waterway-label'], +}; function waterStyling({ map, - subFeatureName, detailName, subDetailName, + key, style, }: stylingProps): void { - console.log(1); + if (detailName === ElementName.labelIcon) return; + + if (detailName === ElementName.labelText) { + // labelText - fill + if (subDetailName === SubElementName.fill) { + if (key === StyleKeyName.weight) return; + if (key === StyleKeyName.visibility) { + const styleValue = style[key] === 'none' ? 'none' : 'visible'; + applyVisibility(map, layers.symbol, styleValue); + return; + } + + applyColor({ + map, + layerNames: layers.symbol, + type: ColorTypeName['text-color'], + color: style.color, + [key]: style[key], + }); + return; + } + + // labelText - stroke + if (key === StyleKeyName.weight || key === StyleKeyName.visibility) { + const styleValue = style.visibility === 'none' ? 0 : Number(style.weight); + applyWeight( + map, + layers.symbol, + WeightTypeName['text-halo-width'], + styleValue + ); + return; + } + + applyColor({ + map, + layerNames: layers.symbol, + type: ColorTypeName['text-halo-color'], + color: style.color, + [key]: style[key], + }); + return; + } + + // section - stroke + if (subDetailName === SubElementName.stroke || key === StyleKeyName.weight) + return; + + // section - fill + if (key === StyleKeyName.visibility) { + const styleValue = style[key] === 'none' ? 'none' : 'visible'; + applyVisibility(map, layers.polygon, styleValue); + return; + } + + applyColor({ + map, + layerNames: layers.polygon, + type: ColorTypeName['fill-color'], + color: style.color, + [key]: style[key], + }); } export default waterStyling; From 23bb6955b626fc4c6f5d07d0444defe08e8aa9b8 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Tue, 1 Dec 2020 19:47:13 +0900 Subject: [PATCH 053/138] =?UTF-8?q?[feat]=20=ED=96=89=EC=A0=95=EA=B5=AC?= =?UTF-8?q?=EC=97=AD=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 은식님처럼 all도 묶어서 변수를 선언하였음 - 라벨아이콘, 구역 채우기 포함되지 않음 --- src/utils/map-styling/administrative.ts | 124 +++++++++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-) diff --git a/src/utils/map-styling/administrative.ts b/src/utils/map-styling/administrative.ts index 6032339..8bd318c 100644 --- a/src/utils/map-styling/administrative.ts +++ b/src/utils/map-styling/administrative.ts @@ -1,13 +1,133 @@ -import { stylingProps } from './index'; +import { stylingProps } from '.'; +import { + ElementName, + SubElementName, + StyleKeyName, +} from '../../store/common/type'; +import { + ColorTypeName, + WeightTypeName, + applyVisibility, + applyColor, + applyWeight, +} from '../applyStyle'; + +type subFeatureNameType = 'country' | 'state' | 'locality' | 'all'; + +const layers = { + country: { + line: [ + 'admin-0-boundary', + 'admin-0-boundary-bg', + 'admin-0-boundary-disputed', + ], + symbol: ['country-label'], + }, + state: { + line: ['admin-1-boundary-bg', 'admin-1-boundary'], + symbol: ['state-label'], + }, + locality: { + line: [], + symbol: ['settlement-label', 'settlement_subdivision-label'], + }, + all: { + line: [ + 'admin-0-boundary', + 'admin-0-boundary-bg', + 'admin-0-boundary-disputed', + 'admin-1-boundary-bg', + 'admin-1-boundary', + ], + symbol: [ + 'country-label', + 'settlement-label', + 'settlement_subdivision-label', + 'state-label', + ], + }, +}; function administrativeStyling({ map, subFeatureName, detailName, subDetailName, + key, style, }: stylingProps): void { - console.log(1); + const mappingLayers = layers[subFeatureName as subFeatureNameType]; + + if (detailName === ElementName.labelIcon) return; + if (detailName === ElementName.labelText) { + // labelText - fill + if (subDetailName === SubElementName.fill) { + if (key === StyleKeyName.weight) return; + if (key === StyleKeyName.visibility) { + const styleValue = style[key] === 'none' ? 'none' : 'visible'; + applyVisibility(map, mappingLayers.symbol, styleValue); + return; + } + + applyColor({ + map, + layerNames: mappingLayers.symbol, + type: ColorTypeName['text-color'], + color: style.color, + [key]: style[key], + }); + return; + } + + // labelText - stroke + if (key === StyleKeyName.weight || key === StyleKeyName.visibility) { + const styleValue = style.visibility === 'none' ? 0 : Number(style.weight); + applyWeight( + map, + mappingLayers.symbol, + WeightTypeName['text-halo-width'], + styleValue + ); + return; + } + + applyColor({ + map, + layerNames: mappingLayers.symbol, + type: ColorTypeName['text-halo-color'], + color: style.color, + [key]: style[key], + }); + return; + } + + // section - fill + if ( + subFeatureName === 'locality' || + subFeatureName === 'neighborhood' || + subDetailName === SubElementName.fill + ) + return; + + // section - stroke + if (key === StyleKeyName.weight || key === StyleKeyName.visibility) { + const styleValue = style.visibility === 'none' ? 0 : Number(style.weight); + applyWeight( + map, + mappingLayers.line, + WeightTypeName['line-width'], + styleValue + ); + return; + } + + applyColor({ + map, + layerNames: mappingLayers.line, + type: ColorTypeName['line-color'], + color: style.color, + [key]: style[key], + }); } export default administrativeStyling; From a5461956f9eb95dc88a4db8b68db42dbf5a92514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 21:22:48 +0900 Subject: [PATCH 054/138] =?UTF-8?q?[feat]=20=EB=A0=88=EC=9D=B4=EC=96=B4=20?= =?UTF-8?q?=EB=B3=84=EB=A1=9C=20=EC=83=89=EC=83=81=EC=9D=84=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=ED=95=9C=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 맵박스와 네이버 레이어들의 색상입니다 - 일단 두가지 버전을 만들어놓고 채워나가면서 비교할 생각입니다. --- src/utils/rendering-data/layersColor.ts | 49 ++++++++++++++++ src/utils/rendering-data/layersColor2.ts | 72 ++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/utils/rendering-data/layersColor.ts create mode 100644 src/utils/rendering-data/layersColor2.ts diff --git a/src/utils/rendering-data/layersColor.ts b/src/utils/rendering-data/layersColor.ts new file mode 100644 index 0000000..e364c19 --- /dev/null +++ b/src/utils/rendering-data/layersColor.ts @@ -0,0 +1,49 @@ +export default { + poi: { + landmark: { + 'poi-attraction': 'hsl(26, 25%, 32%)', + 'poi-arts-label': 'hsl(26, 25%, 32%)', + 'poi-landmark-label': 'hsl(26, 25%, 32%)', + }, + business: { + 'poi-business': 'hsl(22, 55%, 55%)', + 'poi-food-label': 'hsl(22, 55%, 55%)', + 'poi-store-label': 'hsl(22, 55%, 55%)', + }, + government: { + 'poi-government': 'hsl(26, 25%, 32%)', + 'poi-public-label': 'hsl(26, 25%, 32%)', + 'poi-general-label': 'hsl(26, 25%, 32%)', + }, + medical: { + 'poi-medical': 'hsl(340, 39%, 42%)', + 'poi-medical-label': 'hsl(340, 39%, 42%)', + }, + park: { + 'poi-park': 'hsl(100, 45%, 37%)', + 'poi-park-label': 'hsl(100, 45%, 37%)', + }, + worship: { + 'poi-worship': 'hsl(26, 25%, 32%)', + 'poi-religion-label': 'hsl(26, 25%, 32%)', + }, + school: { + 'poi-school': 'hsl(51, 40%, 40%)', + 'poi-education-label': 'hsl(51, 40%, 40%)', + }, + sports: { + 'poi-sport-label': 'hsl(26, 25%, 32%)', + }, + etc: { + 'poi-etc': 'hsl(26, 25%, 32%)', + 'poi-industrial': 'hsl(26, 25%, 32%)', + 'poi-historic-label': 'hsl(26, 25%, 32%)', + 'poi-building-label': 'hsl(26, 25%, 32%)', + }, + }, + road: {}, + administrative: {}, + landscape: {}, + transit: {}, + water: {}, +}; diff --git a/src/utils/rendering-data/layersColor2.ts b/src/utils/rendering-data/layersColor2.ts new file mode 100644 index 0000000..3cd4fee --- /dev/null +++ b/src/utils/rendering-data/layersColor2.ts @@ -0,0 +1,72 @@ +export default { + poi: { + landmark: { + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, + business: { + labelText: { + fill: 'hsl(22, 55%, 55%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, + government: { + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, + medical: { + labelText: { + fill: 'hsl(340, 39%, 42%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, + park: { + labelText: { + fill: 'hsl(100, 45%, 37%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, + worship: { + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, + school: { + labelText: { + fill: 'hsl(51, 40%, 40%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, + sports: { + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, + etc: { + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, + }, + road: {}, + administrative: {}, + landscape: {}, + transit: {}, + water: {}, +}; From 564481710c5db3597f0ddf0462d11b99728b3f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 21:24:00 +0900 Subject: [PATCH 055/138] =?UTF-8?q?[refactor]=20poi=20/=20mapbox-poi?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=EB=93=A4=EC=97=90=EA=B2=8C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EC=A0=81=EC=9D=B8=20=EC=86=8D=EC=84=B1=20?= =?UTF-8?q?=EB=B6=80=EC=97=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - poi레이어에게 윤곽선값을 주었다 - mapbox-poi레이어에게 좀 더 다양한 정보를 주었다. --- src/store/map/layers/mapbox-poi.ts | 8 ++++-- src/store/map/layers/poi.ts | 46 +++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/store/map/layers/mapbox-poi.ts b/src/store/map/layers/mapbox-poi.ts index 5882534..bf0e0a5 100644 --- a/src/store/map/layers/mapbox-poi.ts +++ b/src/store/map/layers/mapbox-poi.ts @@ -8,6 +8,7 @@ const mapboxPoiNameAndColor = [ color: 'hsl(26, 25%, 32%)', layerName: 'arts', }, + { name: 'landmark', color: 'hsl(26, 25%, 32%)', layerName: 'landmark' }, { name: 'building', color: 'hsl(26, 25%, 32%)', layerName: 'building' }, { name: 'general', color: 'hsl(26, 25%, 32%)', layerName: 'general' }, { name: 'historic', color: 'hsl(26, 25%, 32%)', layerName: 'historic' }, @@ -15,14 +16,15 @@ const mapboxPoiNameAndColor = [ { name: 'public_facilities', color: 'hsl(26, 25%, 32%)', - layerName: 'public_facilities', + layerName: 'public', }, { name: 'religion', color: 'hsl(26, 25%, 32%)', layerName: 'religion' }, - { name: 'store_like', color: 'hsl(26, 25%, 32%)', layerName: 'store_like' }, + { name: 'store_like', color: 'hsl(22, 55%, 55%)', layerName: 'store' }, + { name: 'sport_and_leisure', color: 'hsl(26, 25%, 32%)', layerName: 'sport' }, ]; export default mapboxPoiNameAndColor.map((poi) => ({ - id: `poi-${poi.name}-label`, + id: `poi-${poi.layerName}-label`, type: 'symbol', source: 'composite', 'source-layer': 'poi_label', diff --git a/src/store/map/layers/poi.ts b/src/store/map/layers/poi.ts index 7afc001..a28026c 100644 --- a/src/store/map/layers/poi.ts +++ b/src/store/map/layers/poi.ts @@ -4,11 +4,15 @@ export default [ source: 'poi_source', 'source-layer': 'poi', layout: { + 'text-size': 12, 'text-field': ['get', 'name'], visibility: 'visible', }, paint: { - 'text-color': 'brown', + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 0.5, + 'text-halo-blur': 0.5, + 'text-color': 'hsl(26, 25%, 32%)', }, filter: [ 'match', @@ -24,11 +28,15 @@ export default [ source: 'poi_source', 'source-layer': 'poi', layout: { + 'text-size': 12, 'text-field': ['get', 'name'], visibility: 'visible', }, paint: { - 'text-color': 'blue', + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 0.5, + 'text-halo-blur': 0.5, + 'text-color': 'hsl(22, 55%, 55%)', }, filter: [ 'match', @@ -65,11 +73,15 @@ export default [ source: 'poi_source', 'source-layer': 'poi', layout: { + 'text-size': 12, 'text-field': ['get', 'name'], visibility: 'visible', }, paint: { - 'text-color': 'green', + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 0.5, + 'text-halo-blur': 0.5, + 'text-color': 'hsl(26, 25%, 32%)', }, filter: [ 'match', @@ -92,11 +104,15 @@ export default [ source: 'poi_source', 'source-layer': 'poi', layout: { + 'text-size': 12, 'text-field': ['get', 'name'], visibility: 'visible', }, paint: { - 'text-color': 'green', + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 0.5, + 'text-halo-blur': 0.5, + 'text-color': 'hsl(340, 39%, 42%)', }, filter: [ 'match', @@ -112,11 +128,15 @@ export default [ source: 'poi_source', 'source-layer': 'poi', layout: { + 'text-size': 12, 'text-field': ['get', 'name'], visibility: 'visible', }, paint: { - 'text-color': 'green', + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 0.5, + 'text-halo-blur': 0.5, + 'text-color': 'hsl(100, 45%, 37%)', }, filter: [ 'match', @@ -132,11 +152,15 @@ export default [ source: 'poi_source', 'source-layer': 'poi', layout: { + 'text-size': 12, 'text-field': ['get', 'name'], visibility: 'visible', }, paint: { - 'text-color': 'green', + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 0.5, + 'text-halo-blur': 0.5, + 'text-color': 'hsl(26, 25%, 32%)', }, filter: ['match', ['get', 'type'], ['place_of_worship'], true, false], id: 'poi-worship', @@ -146,11 +170,15 @@ export default [ source: 'poi_source', 'source-layer': 'poi', layout: { + 'text-size': 12, 'text-field': ['get', 'name'], visibility: 'visible', }, paint: { - 'text-color': 'green', + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 0.5, + 'text-halo-blur': 0.5, + 'text-color': 'hsl(51, 40%, 40%)', }, filter: [ 'match', @@ -166,10 +194,14 @@ export default [ source: 'poi_source', 'source-layer': 'poi', layout: { + 'text-size': 12, 'text-field': ['get', 'name'], visibility: 'visible', }, paint: { + 'text-halo-color': 'hsl(26, 25%, 32%)', + 'text-halo-width': 0.5, + 'text-halo-blur': 0.5, 'text-color': 'green', }, filter: [ From 47ad167af6547d5de0db9faef311084ce92b0032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 21:25:10 +0900 Subject: [PATCH 056/138] =?UTF-8?q?[refactor]=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=EC=9D=84=20=EB=B0=94=EA=BE=B8=EB=8A=94=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=A5=BC=20=ED=98=B8=EC=B6=9C=ED=95=A0=20=EB=96=84=20?= =?UTF-8?q?key=EA=B0=92=EB=8F=84=20=EA=B0=99=EC=9D=B4=20=EB=84=98=EA=B2=A8?= =?UTF-8?q?=EC=A4=80=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 코드에서 스타일을 바꾸는 함수를 호출할 때 key값을 같이 넘겨준다. - 어떤 값이 변경되었는지 알기 위함 --- src/hooks/sidebar/useStyleType.ts | 1 + src/utils/map-styling/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index cc76e8a..2227745 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -49,6 +49,7 @@ function useStyleType({ map, subFeatureName, detailName, + key, subDetailName: subDetailName as SubElementNameType, style: { ...styleElement, diff --git a/src/utils/map-styling/index.ts b/src/utils/map-styling/index.ts index 59c7af9..1975482 100644 --- a/src/utils/map-styling/index.ts +++ b/src/utils/map-styling/index.ts @@ -8,6 +8,7 @@ import { export interface stylingProps { map: mapboxgl.Map; subFeatureName: string; + key: string; detailName: ElementNameType; subDetailName: SubElementNameType; style: StyleType; From c1c15e4e33dbf70f988321d63c5d882447cde049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 21:26:28 +0900 Subject: [PATCH 057/138] =?UTF-8?q?[refactor]=20[#79]=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=9D=84=20=EC=A2=80=EB=8D=94=20=ED=9A=A8=EC=9C=A8=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EA=B5=AC=ED=98=84=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=EC=84=9C=20applyStyle=EC=9D=84=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - applyStyle의 함수들을 공통 인터페이스를 사용해서 비구조화로 값을 받아올 수 있게 하였다.- 필요한 여러 타입들과 enum을 지정해주었다 - 색상에 관련된 함수 세개를 하나로 합쳤다 --- src/utils/applyStyle.ts | 123 ++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 49 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index 940154e..e646b8c 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -1,62 +1,87 @@ +/* eslint-disable consistent-return */ import mapboxgl from 'mapbox-gl'; import { hexToHSL } from './colorFormat'; -type colorType = 'fill-color' | 'line-color' | 'text-color' | 'text-halo-color'; -type weightType = 'line-width' | 'text-halo-width' | 'text-size'; +export enum ColorTypeName { + 'fill-color' = 'fill-color', + 'line-color' = 'line-color', + 'text-color' = 'text-color', + 'text-halo-color' = 'text-halo-color', +} +export enum WeightTypeName { + 'line-width' = 'line-width', + 'text-halo-width' = 'text-halo-width', + 'icon-opacity' = 'icon-opacity', +} +export type colorType = keyof typeof ColorTypeName; +export type weightType = keyof typeof WeightTypeName; -export function applyVisibility( - map: mapboxgl.Map, - layerName: string, - visibility: string -): void { - map.setLayoutProperty(layerName, 'visibility', visibility); +interface ApplyColorProps { + map: mapboxgl.Map; + layerNames: string[]; + color?: string; + type?: colorType | weightType; + saturation?: number; + lightness?: number; + weight?: string; + visibility?: string; } -export function applyColor( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string -): void { +export function applyColor({ + map, + layerNames, + color = '#000', + type, + saturation, + lightness, +}: ApplyColorProps): void { + if (!type) return; const { h, s, l } = hexToHSL(color); - map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); -} -export function applyWeight( - map: mapboxgl.Map, - layerName: string, - type: weightType, - weight: number -): void { - map.setPaintProperty(layerName, type, weight * 2 + 1); + if (saturation) { + return layerNames.forEach((layerName) => { + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${50 + saturation / 2}%, ${l}%)` + ); + }); + } + + if (lightness) { + return layerNames.forEach((layerName) => { + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${s}%, ${50 + lightness / 2}%)` + ); + }); + } + + return layerNames.forEach((layerName) => { + map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); + }); } -export function applySaturation( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string, - saturation: number -): void { - const { h, l } = hexToHSL(color); - map.setPaintProperty( - layerName, - type, - `hsl(${h}, ${50 + saturation / 2}%, ${l}%)` - ); +export function applyVisibility({ + map, + layerNames, + visibility, +}: ApplyColorProps): void { + layerNames.forEach((layerName) => { + map.setLayoutProperty(layerName, 'visibility', visibility); + }); } -export function applyLightness( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string, - lightness: number -): void { - const { h, s } = hexToHSL(color); - map.setPaintProperty( - layerName, - type, - `hsl(${h}, ${s}%, ${50 + lightness / 2}%)` - ); +export function applyWeight({ + map, + layerNames, + type, + weight, +}: ApplyColorProps): void { + if (!type) return; + + layerNames.forEach((layerName) => { + map.setPaintProperty(layerName, type, Number(weight)); + }); } From 5ef0f4c9403a4e18698576907bab39825af3a0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Tue, 1 Dec 2020 21:29:31 +0900 Subject: [PATCH 058/138] =?UTF-8?q?[feat]=20[#79}=20poi=20feature=EB=A5=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - POI를 수정할 수 있는 함수를 구현하였다. - section(구역)이 들어오면 그냥 반환한다. - 객체를 활용해서 불필요한 if문을 최대한 줄이고, 예외가 발생한 경우들만 if문으로 처리를 해주었다. - 다른 분들과 코드가 엉킬 위험을 고려해 타입과 필요한 변수들을 한 파일에 작성하였다 --- src/utils/map-styling/poi.ts | 195 ++++++++++++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 1 deletion(-) diff --git a/src/utils/map-styling/poi.ts b/src/utils/map-styling/poi.ts index e16d251..11011c0 100644 --- a/src/utils/map-styling/poi.ts +++ b/src/utils/map-styling/poi.ts @@ -1,4 +1,157 @@ +/* eslint-disable consistent-return */ import { stylingProps } from './index'; +import { + applyColor, + applyWeight, + applyVisibility, + colorType, + weightType, + WeightTypeName, + ColorTypeName, +} from '../applyStyle'; +import { StyleKeyType, ElementName } from '../../store/common/type'; + +type PoiSubFeature = + | 'all' + | 'landmark' + | 'business' + | 'government' + | 'medical' + | 'park' + | 'worship' + | 'school' + | 'sports' + | 'etc'; + +type POI_LAYERS_TYPE = { + [name in PoiSubFeature]: string[]; +}; + +const VISIBLE = '1'; +const INVISIBLE = '0'; +const POI_LAYERS: POI_LAYERS_TYPE = { + all: [ + 'poi-attraction', + 'poi-arts-label', + 'poi-landmark-label', + 'poi-business', + 'poi-food-label', + 'poi-store-label', + 'poi-government', + 'poi-public-label', + 'poi-general-label', + 'poi-medical', + 'poi-medical-label', + 'poi-park', + 'poi-park-label', + 'poi-worship', + 'poi-religion-label', + 'poi-school', + 'poi-education-label', + 'poi-sport-label', + 'poi-etc', + 'poi-industrial-label', + 'poi-historic-label', + 'poi-building-label', + ], + landmark: ['poi-attraction', 'poi-arts-label', 'poi-landmark-label'], + business: ['poi-business', 'poi-food-label', 'poi-store-label'], + government: ['poi-government', 'poi-public-label', 'poi-general-label'], + medical: ['poi-medical', 'poi-medical-label'], + park: ['poi-park', 'poi-park-label'], + worship: ['poi-worship', 'poi-religion-label'], + school: ['poi-school', 'poi-education-label'], + sports: ['poi-sport-label'], + etc: [ + 'poi-etc', + 'poi-industrial-label', + 'poi-historic-label', + 'poi-building-label', + ], +}; + +const mappingDetailToFunc = { + labelText: { + fill: { + color: { + typeName: ColorTypeName['text-color'], + funcName: applyColor, + }, + saturation: { + typeName: ColorTypeName['text-color'], + funcName: applyColor, + }, + lightness: { + typeName: ColorTypeName['text-color'], + funcName: applyColor, + }, + weight: { + typeName: null, + funcName: () => null, + }, + visibility: { + typeName: 'what is my name?', + funcName: applyVisibility, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + stroke: { + color: { + typeName: ColorTypeName['text-halo-color'], + funcName: applyColor, + }, + saturation: { + typeName: ColorTypeName['text-halo-color'], + funcName: applyColor, + }, + lightness: { + typeName: ColorTypeName['text-halo-color'], + funcName: applyColor, + }, + weight: { + typeName: WeightTypeName['text-halo-width'], + funcName: applyWeight, + }, + visibility: { + typeName: WeightTypeName['text-halo-width'], + funcName: applyWeight, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + }, + labelIcon: { + color: { + typeName: null, + funcName: () => null, + }, + saturation: { + typeName: null, + funcName: () => null, + }, + lightness: { + typeName: null, + funcName: () => null, + }, + weight: { + typeName: null, + funcName: () => null, + }, + visibility: { + typeName: WeightTypeName['icon-opacity'], + funcName: applyWeight, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, +}; function poiStyling({ map, @@ -6,8 +159,48 @@ function poiStyling({ detailName, subDetailName, style, + key, }: stylingProps): void { - console.log(1); + let type = null; + let func = null; + + if (detailName === ElementName.section) return; + if (detailName === ElementName.labelText) { + const { typeName, funcName } = mappingDetailToFunc[detailName][ + subDetailName + ][key as StyleKeyType]; + type = typeName; + func = funcName; + } else { + const { typeName, funcName } = mappingDetailToFunc[detailName][ + key as StyleKeyType + ]; + type = typeName; + func = funcName; + } + + if (!type) return; + if ( + key === 'visibility' && + (type === WeightTypeName['icon-opacity'] || + type === WeightTypeName['text-halo-width']) + ) { + func({ + map, + layerNames: POI_LAYERS[subFeatureName as PoiSubFeature], + type, + weight: style.visibility === 'none' ? INVISIBLE : VISIBLE, + }); + return; + } + + func({ + map, + layerNames: POI_LAYERS[subFeatureName as PoiSubFeature], + type: type as weightType | colorType, + color: style.color, + [key]: style[key as StyleKeyType], + }); } export default poiStyling; From 5378371e98d4c030e816b6dd3be0ea3470676d1c Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Tue, 1 Dec 2020 22:52:30 +0900 Subject: [PATCH 059/138] =?UTF-8?q?[fix]=20string=20to=20number=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=EB=B2=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - parseInt 에서 Number로 직접적인 변환 - 진수를 신경써주지 않아도 되는 변환이라 생각되어 변경 --- src/utils/applyStyle.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index d362acf..f119703 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -52,7 +52,7 @@ export function applyColor({ map.setPaintProperty( layerName, type, - `hsl(${h}, ${50 + parseInt(saturation, 10) / 2}%, ${l}%)` + `hsl(${h}, ${50 + Number(saturation) / 2}%, ${l}%)` ); }); } @@ -61,7 +61,7 @@ export function applyColor({ map.setPaintProperty( layerName, type, - `hsl(${h}, ${s}%, ${50 + parseInt(lightness, 10) / 2}%)` + `hsl(${h}, ${s}%, ${50 + Number(lightness) / 2}%)` ); }); } @@ -77,6 +77,6 @@ export function applyWeight( weight: string ): void { layerNames.forEach((layerName) => { - map.setPaintProperty(layerName, type, parseInt(weight, 10) * 2 + 1); + map.setPaintProperty(layerName, type, Number(weight) * 2 + 1); }); } From c427262cac553abe3d26de1d38991199d1ede3c0 Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Tue, 1 Dec 2020 22:53:34 +0900 Subject: [PATCH 060/138] =?UTF-8?q?[fix]=20=EB=8F=84=EB=A1=9C=EC=9D=98=20?= =?UTF-8?q?=EC=9E=90=EC=A0=84=EA=B1=B0=20=EB=8F=84=EB=A1=9C=20=ED=95=AD?= =?UTF-8?q?=EB=AA=A9=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 자전거 도로에 해당하는 항목이 거의 존재하지 않아, 실효성이 없다고 판단되어 해당항목 삭제 --- src/utils/rendering-data/featureTypeData.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/rendering-data/featureTypeData.ts b/src/utils/rendering-data/featureTypeData.ts index 51893c5..059f15e 100644 --- a/src/utils/rendering-data/featureTypeData.ts +++ b/src/utils/rendering-data/featureTypeData.ts @@ -34,7 +34,6 @@ const data: DataType[] = [ { key: 'arterial', name: '주요도로' }, { key: 'local', name: '일반도로' }, { key: 'sidewalk', name: '인도' }, - { key: 'bicycle-road', name: '자전거도로' }, ], }, { From 5c5a03c273e4df82076c7c9706a8530f270906de Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Tue, 1 Dec 2020 22:55:02 +0900 Subject: [PATCH 061/138] =?UTF-8?q?[feat]=20[#81]=20=EB=8F=84=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=EB=8F=84=20=ED=95=AD=EB=AA=A9=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=A0=81=EC=9A=A9=20=EA=B0=80=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 도로 인도 항목 스타일 적용 가능 - 하지만 현재 인도에 관한 레이어가 네이버 데이터밖에 없어, 인도에 관한 추가 데이터 필요(mapbox) --- src/utils/map-styling/road.ts | 107 ++++++++++++++++------------------ 1 file changed, 51 insertions(+), 56 deletions(-) diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index 4c59070..7a40419 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -3,6 +3,29 @@ import { applyVisibility, applyColor, applyWeight } from '../applyStyle'; type KeyType = 'visibility' | 'color' | 'weight' | 'saturation' | 'lightness'; +/** + * visibility inherit 처리 applyStyle에서 + * color, saturation, lightness 한 함수로 처리 + * - section + * - fill : line 'line-color', symbol, polygon 'fill-color' + * - stroke : line 'case layer'의 색상, symbol 불변, polygon이면 불변 + * - label + * - text fill: 'text-color', 'text-width' + * - text stroke: 'text-halo-color', 'text-halo-width' + * + */ + +/** + * 20.12.01. + * highway : 분류 보류 + * aterial: line이라 채우기 윤곽선은 한번에 처리, 채우기 윤곽선 확인완료, 아이콘 없음 + * local: 지금 있는 것들은 section fill밖에 의미없음 + * sidewalk: 확인 완료 + * + * 색상을 변경하지 않은채로 채도나 밝기부터 선택하면 색상팔레트 색 기준으로 조정이 됨 + * fix 필요 + */ + function roadStyling({ map, subFeatureName, @@ -11,7 +34,7 @@ function roadStyling({ key, style, }: stylingProps): void { - const { visibility, color, weight, saturation, lightness } = style; + const { visibility, color, weight } = style; const arterialLayerNames = [ 'road-number-shield', 'road-exit-shield', @@ -21,39 +44,21 @@ function roadStyling({ const localLayerNames = ['ferry', 'ferry-auto', 'road-local']; const sidewalkLayerNames = ['road-footway']; - /** - * visibility 가 바뀌는 것 우선은 여기에서 처리, inherit (visible 처리) - * color, saturation, lightness 한 함수로 처리 - * - section - * - fill : line 'line-color', symbol, polygon 'fill-color' - * - stroke : line 'case layer'의 색상, symbol 불변, polygon이면 불변 - * - label - * - text fill: 'text-color', 'text-width' - * - text stroke: 'text-halo-color', 'text-halo-width' - * - * cf. 채도와 밝기가 크게 달라지는 것 같지 않다.. line인 탓일까..? - */ - - /** - * 20.12.01. - * highway : 분류 보류 - * aterial: line이라 채우기 윤곽선은 한번에 처리, 채우기 윤곽선 확인완료, 아이콘 없음 - * local: 지금 있는 것들은 section fill밖에 의미없음 - * sidewalk - */ - if (subFeatureName === 'arterial') { - if (detailName === 'section' && key === 'visibility') { + if ( + detailName === 'section' && + subDetailName === 'fill' && + key === 'visibility' + ) { applyVisibility(map, arterialLayerNames, visibility); - } else if (key === ('color' || 'saturation' || 'lightness')) { + } else if (key === 'color' || key === 'saturation' || key === 'lightness') { if (detailName === 'section' && subDetailName === 'fill') { applyColor({ map, layerNames: ['road-arterial'], color, type: 'line-color', - saturation, - lightness, + [key]: style[key as KeyType], }); } else if (detailName === 'labelText' && subDetailName === 'fill') { applyColor({ @@ -61,8 +66,7 @@ function roadStyling({ layerNames: ['road-number-shield', 'road-exit-shield', 'road-label'], color, type: 'text-color', - saturation, - lightness, + [key]: style[key as KeyType], }); } else if (detailName === 'labelText' && subDetailName === 'stroke') { applyColor({ @@ -70,8 +74,7 @@ function roadStyling({ layerNames: ['road-label'], color, type: 'text-halo-color', - saturation, - lightness, + [key]: style[key as KeyType], }); } } else if (key === 'weight') { @@ -83,15 +86,14 @@ function roadStyling({ } } else if (subFeatureName === 'local') { if (key === 'visibility') applyVisibility(map, localLayerNames, visibility); - else if (key === ('color' || 'saturation' || 'lightness')) { + else if (key === 'color' || key === 'saturation' || key === 'lightness') { if (detailName === 'section' && subDetailName === 'fill') { applyColor({ map, layerNames: ['ferry', 'ferry-auto', 'road-local'], color, type: 'line-color', - saturation, - lightness, + [key]: style[key as KeyType], }); } } else if (key === 'weight') { @@ -103,30 +105,23 @@ function roadStyling({ weight ); } + } else if (subFeatureName === 'sidewalk') { + if (key === 'visibility') + applyVisibility(map, sidewalkLayerNames, visibility); + else if (key === 'weight') { + if (detailName === 'section' && subDetailName === 'fill') + applyWeight(map, sidewalkLayerNames, 'line-width', weight); + } else if (key === 'color' || key === 'saturation' || key === 'lightness') { + if (detailName === 'section' && subDetailName === 'fill') + applyColor({ + map, + layerNames: sidewalkLayerNames, + color, + type: 'line-color', + [key]: style[key as KeyType], + }); + } } } -// if (subFeatureName === ('highway' || 'bicycle-road')) { -// /** -// * There is no highway layer and bicycle-road -// */ -// } else - -// else if (subFeatureName === 'local') { -// if (key === 'visibility') applyVisibility(map, localLayerNames, visibility); -// } else if (subFeatureName === 'sidewalk') { -// // weight 관련 속성 없음 -// if (key === 'visibility') -// applyVisibility(map, sidewalkLayerNames, visibility); -// if (key === ('color' || 'saturation' || 'lightness')) { -// applyColor({ -// map, -// layerNames: sidewalkLayerNames, -// color, -// type: 'line-color', -// saturation, -// lightness, -// }); -// } - export default roadStyling; From a7d67d4c09957b5bb60a9ab36ed73162324785c9 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Wed, 2 Dec 2020 00:20:21 +0900 Subject: [PATCH 062/138] =?UTF-8?q?[fix]=20neighborhood=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 수정을 빼먹어서 제거함 --- src/utils/map-styling/administrative.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/utils/map-styling/administrative.ts b/src/utils/map-styling/administrative.ts index 8bd318c..b41c3bc 100644 --- a/src/utils/map-styling/administrative.ts +++ b/src/utils/map-styling/administrative.ts @@ -102,11 +102,7 @@ function administrativeStyling({ } // section - fill - if ( - subFeatureName === 'locality' || - subFeatureName === 'neighborhood' || - subDetailName === SubElementName.fill - ) + if (subFeatureName === 'locality' || subDetailName === SubElementName.fill) return; // section - stroke From a8aa5b11616c82fa6b516a875fc64cacbfa73554 Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Wed, 2 Dec 2020 00:43:11 +0900 Subject: [PATCH 063/138] =?UTF-8?q?[fix]=20=EB=8F=84=EB=A1=9C=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=84=A4=EC=9D=B4=EB=B2=84=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EC=97=90=20=EB=8C=80=ED=95=9C=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EC=84=A4=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 스타일 설정을 위한 속성들 추가 --- src/store/map/layers/road.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/store/map/layers/road.ts b/src/store/map/layers/road.ts index 9219d2d..e1ea102 100644 --- a/src/store/map/layers/road.ts +++ b/src/store/map/layers/road.ts @@ -22,6 +22,7 @@ export default [ }, paint: { 'line-color': '#ff00ff', + 'line-width': 1, }, filter: [ 'match', @@ -60,6 +61,7 @@ export default [ }, paint: { 'line-color': 'red', + 'line-width': 1, }, filter: ['match', ['get', 'type'], ['pedestrian', 'footway'], true, false], id: 'road-footway', From e090ff8cde3a0f5c565df0080d9c7a1d634e9240 Mon Sep 17 00:00:00 2001 From: gitgitwi Date: Wed, 2 Dec 2020 01:39:02 +0900 Subject: [PATCH 064/138] =?UTF-8?q?[FEAT]=20[#86]=20=EA=B5=90=ED=86=B5(tra?= =?UTF-8?q?nsit)=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A1=B0=EC=A0=95=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 교통 전체/항공/버스/전철/지하철 스타일 조정 기본 기능 구현 - 샘플 데이터 레이어 + Mapbox 내장 레이어 변경 - TODO - 레이어에 없는 속성 변경 시 에러 관련 예외 처리 - 전반적인 리팩토링 시급 --- src/hooks/sidebar/useStyleType.ts | 1 + src/store/map/layers/transit.ts | 26 ++++-- src/utils/applyStyle.ts | 119 ++++++++++++++++--------- src/utils/map-styling/index.ts | 1 + src/utils/map-styling/transit.ts | 140 +++++++++++++++++++++++++++++- 5 files changed, 240 insertions(+), 47 deletions(-) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index cc76e8a..4dc1fe9 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -48,6 +48,7 @@ function useStyleType({ mapStyling[featureName]({ map, subFeatureName, + key, detailName, subDetailName: subDetailName as SubElementNameType, style: { diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts index 7828095..18a3ae4 100644 --- a/src/store/map/layers/transit.ts +++ b/src/store/map/layers/transit.ts @@ -1,4 +1,17 @@ export default [ + { + type: 'fill', + source: 'composite', + 'source-layer': 'landuse', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': 'hsl(230, 100%, 44%)', + }, + filter: ['in', 'class', 'airport'], + id: 'transit-airport', + }, { type: 'line', source: 'line_source', @@ -9,7 +22,7 @@ export default [ paint: { 'line-color': 'yellow', }, - filter: ['==', ['get', 'type'], 'subway'], + filter: ['in', 'type', 'subway'], id: 'transit-subway', }, { @@ -22,7 +35,7 @@ export default [ paint: { 'line-color': 'black', }, - filter: ['==', ['get', 'type'], 'rail'], + filter: ['in', 'type', 'rail'], id: 'transit-rail', }, { @@ -31,12 +44,15 @@ export default [ 'source-layer': 'poi', layout: { 'text-field': ['get', 'name'], + 'text-size': 12, visibility: 'visible', }, paint: { - 'text-color': 'green', + 'text-halo-color': 'green', + 'text-halo-width': 0.5, + 'text-color': 'red', }, - filter: ['==', ['get', 'type'], 'taxi'], - id: 'transit-bus', + filter: ['in', 'type', 'bus_stop'], + id: 'transit-bus-label', }, ]; diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index 940154e..384e8e3 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -1,62 +1,99 @@ +/* eslint-disable no-debugger */ +/* eslint-disable no-case-declarations */ import mapboxgl from 'mapbox-gl'; +import { StyleKeyName } from '../store/common/type'; import { hexToHSL } from './colorFormat'; -type colorType = 'fill-color' | 'line-color' | 'text-color' | 'text-halo-color'; -type weightType = 'line-width' | 'text-halo-width' | 'text-size'; +export enum VisibilityType { + visibility = 'visibility', +} + +export enum ColorTypeName { + 'fill-color' = 'fill-color', + 'line-color' = 'line-color', + 'text-color' = 'text-color', + 'text-halo-color' = 'text-halo-color', +} +export enum WeightTypeName { + 'line-width' = 'line-width', + 'text-size' = 'text-size', + 'text-halo-width' = 'text-halo-width', +} +type ColorType = keyof typeof ColorTypeName; +type WeightType = keyof typeof WeightTypeName; +export type styleTypes = VisibilityType | ColorType | WeightType; + +interface ApplyColorProps { + map: mapboxgl.Map; + layerNames: string[]; + color: string; + type: styleTypes; + saturation?: number; + lightness?: number; +} export function applyVisibility( map: mapboxgl.Map, - layerName: string, + layerNames: string[], visibility: string ): void { - map.setLayoutProperty(layerName, 'visibility', visibility); + return layerNames.forEach((layerName) => { + map.setLayoutProperty(layerName, VisibilityType.visibility, visibility); + }); } -export function applyColor( - map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string -): void { +export function applyColor({ + map, + layerNames, + type, + color, + saturation, + lightness, +}: ApplyColorProps): void { const { h, s, l } = hexToHSL(color); - map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); -} -export function applyWeight( - map: mapboxgl.Map, - layerName: string, - type: weightType, - weight: number -): void { - map.setPaintProperty(layerName, type, weight * 2 + 1); + if (saturation) { + return layerNames.forEach((layerName) => { + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${50 + saturation / 2}%, ${l}%)` + ); + }); + } + if (lightness) { + return layerNames.forEach((layerName) => { + map.setPaintProperty( + layerName, + type, + `hsl(${h}, ${s}%, ${50 + lightness / 2}%)` + ); + }); + } + + return layerNames.forEach((layerName) => { + map.setPaintProperty(layerName, type, `hsl(${h}, ${s}%, ${l}%)`); + }); } -export function applySaturation( +export function applyWeight( map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string, - saturation: number + layerNames: string[], + type: WeightType, + weight: string ): void { - const { h, l } = hexToHSL(color); - map.setPaintProperty( - layerName, - type, - `hsl(${h}, ${50 + saturation / 2}%, ${l}%)` - ); + return layerNames.forEach((layerName) => { + map.setPaintProperty(layerName, type, +weight * 3 + 1); + }); } -export function applyLightness( +export function applyTextSize( map: mapboxgl.Map, - layerName: string, - type: colorType, - color: string, - lightness: number + layerNames: string[], + type: WeightType, + size: string ): void { - const { h, s } = hexToHSL(color); - map.setPaintProperty( - layerName, - type, - `hsl(${h}, ${s}%, ${50 + lightness / 2}%)` - ); + return layerNames.forEach((layerName) => { + map.setLayoutProperty(layerName, type, +size * 3); + }); } diff --git a/src/utils/map-styling/index.ts b/src/utils/map-styling/index.ts index 59c7af9..1975482 100644 --- a/src/utils/map-styling/index.ts +++ b/src/utils/map-styling/index.ts @@ -8,6 +8,7 @@ import { export interface stylingProps { map: mapboxgl.Map; subFeatureName: string; + key: string; detailName: ElementNameType; subDetailName: SubElementNameType; style: StyleType; diff --git a/src/utils/map-styling/transit.ts b/src/utils/map-styling/transit.ts index e66698b..ef6d703 100644 --- a/src/utils/map-styling/transit.ts +++ b/src/utils/map-styling/transit.ts @@ -1,13 +1,151 @@ +/* eslint-disable no-nested-ternary */ +/* eslint-disable no-debugger */ +/* eslint-disable no-case-declarations */ import { stylingProps } from '.'; +import { StyleKeyName, StyleType } from '../../store/common/type'; +import { + applyVisibility, + applyColor, + applyWeight, + applyTextSize, + ColorTypeName, + styleTypes, + WeightTypeName, +} from '../../utils/applyStyle'; + +const AirportLayerNames = [ + 'airport-label', + + 'aeroway-polygon', + 'transit-airport', + 'aeroway-line', +]; +const BusLayerNames = ['transit-bus-label']; +const RailLayerNames = ['transit-rail']; +const SubwayLayerNames = ['transit-subway', 'transit-subway-label']; + +const AllLayerTypeKeys = [ + ...AirportLayerNames, + ...BusLayerNames, + ...RailLayerNames, + ...SubwayLayerNames, + 'transit-label', +]; function transitStyling({ map, subFeatureName, + key, detailName, subDetailName, style, }: stylingProps): void { - console.log(1); + const layers = + subFeatureName === 'all' + ? [...AllLayerTypeKeys] + : subFeatureName === 'subway' + ? [...SubwayLayerNames] + : subFeatureName === 'bus' + ? [...BusLayerNames] + : subFeatureName === 'airport' + ? [...AirportLayerNames] + : [...RailLayerNames]; + + let layersByDetailName: string[]; + let styleType: styleTypes; + debugger; + switch (detailName) { + case 'section': + layersByDetailName = layers.filter((layer) => !layer.includes('label')); + styleType = + subDetailName === 'stroke' + ? key === 'weight' + ? WeightTypeName['line-width'] + : ColorTypeName['line-color'] + : ColorTypeName['fill-color']; + + break; + case 'labelText': + layersByDetailName = layers.filter((layer) => layer.includes('label')); + styleType = + subDetailName === 'stroke' + ? key === 'weight' + ? WeightTypeName['text-halo-width'] + : ColorTypeName['text-halo-color'] + : key === 'weight' + ? WeightTypeName['text-size'] + : ColorTypeName['text-color']; + debugger; + break; + case 'labelIcon': + layersByDetailName = layers.filter((layer) => !layer.includes('label')); + styleType = + subDetailName === 'stroke' + ? ColorTypeName['line-color'] + : ColorTypeName['fill-color']; + break; + + default: + return; + } + + const styleKey: StyleKeyName = key as StyleKeyName; + const { [styleKey]: value } = style; + let stylerFunction: (obj: any) => void; + + switch (styleKey) { + case StyleKeyName.visibility: + stylerFunction = ({ layerNames }: { layerNames: string[] }) => + applyVisibility(map, layerNames, value as string); + break; + + case StyleKeyName.color: + case StyleKeyName.saturation: + case StyleKeyName.lightness: + const colorKey = StyleKeyName.color; + const satKey = StyleKeyName.saturation; + const ligKey = StyleKeyName.lightness; + + const satureOrLight = + key === 'saturation' + ? { saturation: +style[satKey] } + : key === 'lightness' + ? { lightness: +style[ligKey] } + : {}; + stylerFunction = ({ layerNames }: { layerNames: string[] }) => + applyColor({ + map, + layerNames, + type: styleType, + color: style[colorKey], + ...satureOrLight, + }); + break; + + case StyleKeyName.weight: + stylerFunction = + subDetailName !== 'fill' + ? ({ layerNames }: { layerNames: string[] }) => + applyWeight( + map, + layerNames, + styleType as WeightTypeName, + value as string + ) + : ({ layerNames }: { layerNames: string[] }) => + applyTextSize( + map, + layerNames, + styleType as WeightTypeName, + value as string + ); + break; + + default: + return; + } + + stylerFunction({ layerNames: layersByDetailName }); } export default transitStyling; From a214348355e36f013aa310b6c1e2664c66347388 Mon Sep 17 00:00:00 2001 From: tejava Date: Wed, 2 Dec 2020 02:17:56 +0900 Subject: [PATCH 065/138] =?UTF-8?q?[fix]=20subFeatureName=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20=EB=A0=88=EC=9D=B4=EC=96=B4=EB=A5=BC=20?= =?UTF-8?q?=EC=9E=AC=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit subFeatureName에 따라서 applyStyle이 일대일로 대응될 수 있도록 레이어를 변경 landscape-humanmade를 landscape-human-made로 변경 landscape-commercial을 landscape-building과 병합 --- src/store/map/layers/landscape.ts | 50 ++++++++++++------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/src/store/map/layers/landscape.ts b/src/store/map/layers/landscape.ts index f9a9724..9ef4661 100644 --- a/src/store/map/layers/landscape.ts +++ b/src/store/map/layers/landscape.ts @@ -1,6 +1,6 @@ export default [ { - id: 'landscape-humanmade', + id: 'landscape-human-made', type: 'fill', source: 'polygon_source', 'source-layer': 'polygon', @@ -49,37 +49,17 @@ export default [ visibility: 'visible', }, paint: { - 'fill-color': 'skyblue', - 'fill-opacity': 0.5, - }, - filter: [ - 'match', - ['get', 'type'], - [ - 'restaurant', - 'cafe', - 'marketplace', - 'fast_food', - 'bar', - 'pub', - 'casino', - 'cinema', + 'fill-color': [ + 'interpolate', + ['linear'], + ['zoom'], + 15, + 'hsl(35, 11%, 86%)', + 16, + 'hsl(35, 7%, 85%)', ], - true, - false, - ], - id: 'landscape-commercial', - }, - { - type: 'fill', - source: 'polygon_source', - 'source-layer': 'polygon', - layout: { - visibility: 'visible', - }, - paint: { - 'fill-color': 'black', - 'fill-opacity': 0.5, + 'fill-opacity': ['interpolate', ['linear'], ['zoom'], 15, 0, 16, 1], + 'fill-outline-color': 'hsl(35, 8%, 80%)', }, filter: [ 'in', @@ -126,6 +106,14 @@ export default [ 'palace', 'toilets', 'social_facility', + 'restaurant', + 'cafe', + 'marketplace', + 'fast_food', + 'bar', + 'pub', + 'casino', + 'cinema', '', ], id: 'landscape-building', From b39fd7dc6d62fb64ea913bd4c55c02dc4d675baf Mon Sep 17 00:00:00 2001 From: tejava Date: Wed, 2 Dec 2020 05:01:26 +0900 Subject: [PATCH 066/138] =?UTF-8?q?[feat]=20all=EC=9D=84=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8=ED=95=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 각 레이어 이름에 landscape-를 더하여 속성을 지정, 이에 대한 설정을 각자 진행한다. --- src/utils/map-styling/landscape.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index 9282499..4e49395 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -1,4 +1,10 @@ import { stylingProps } from './index'; +import { + applyColor, + applyLightness, + applyVisibility, + applySaturation, +} from '../applyStyle'; function landscapeStyling({ map, @@ -7,7 +13,26 @@ function landscapeStyling({ subDetailName, style, }: stylingProps): void { - console.log(1); + const layerName = `landscape-${subFeatureName}`; + + if (detailName === 'section' && subDetailName === 'fill') { + applyColor(map, layerName, 'fill-color', style.color); + applyVisibility(map, layerName, style.visibility); + applyLightness( + map, + layerName, + 'fill-color', + style.color, + Number(style.lightness) + ); + applySaturation( + map, + layerName, + 'fill-color', + style.color, + Number(style.saturation) + ); + } } export default landscapeStyling; From e0eb491492fbffe93e18209d3a5f8799d1b0ca77 Mon Sep 17 00:00:00 2001 From: tejava Date: Wed, 2 Dec 2020 06:43:14 +0900 Subject: [PATCH 067/138] =?UTF-8?q?[feat]=20[#83]=20all=EC=9D=84=20?= =?UTF-8?q?=ED=8F=AC=ED=95=A8=ED=95=9C=20=EA=B2=BD=EA=B4=80=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=A1=B0=EC=A0=95=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit all의 경우 모든 항목을 forEach로 순회하며 setPaintProperty를 한다 이를 위해서 setProperty를 하기 위한 구현 과정을 함수로 분리하였다 #93 에 따르면 변경사항이 전달되도록 하였다는데, 이를 이후에 리팩토링에 적용할 예정 현재는 모든 항목을 한번에 업데이트하고있다 --- src/utils/map-styling/landscape.ts | 59 +++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index 4e49395..dec32f1 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -5,6 +5,40 @@ import { applyVisibility, applySaturation, } from '../applyStyle'; +import { StyleType } from '../../store/common/type'; + +const SECTION = 'section'; +const FILL = 'fill'; +const ALL = 'all'; + +const layers = ['human-made', 'building', 'natural', 'landcover']; + +interface applyStyleProps { + map: mapboxgl.Map; + subFeatureName: string; + style: StyleType; +} + +function applyLandscapeStyle({ map, subFeatureName, style }: applyStyleProps) { + const layerName = `landscape-${subFeatureName}`; + + applyColor(map, layerName, 'fill-color', style.color); + applyVisibility(map, layerName, style.visibility); + applyLightness( + map, + layerName, + 'fill-color', + style.color, + Number(style.lightness) + ); + applySaturation( + map, + layerName, + 'fill-color', + style.color, + Number(style.saturation) + ); +} function landscapeStyling({ map, @@ -13,25 +47,16 @@ function landscapeStyling({ subDetailName, style, }: stylingProps): void { - const layerName = `landscape-${subFeatureName}`; + if (detailName !== SECTION || subDetailName !== FILL) { + return; + } - if (detailName === 'section' && subDetailName === 'fill') { - applyColor(map, layerName, 'fill-color', style.color); - applyVisibility(map, layerName, style.visibility); - applyLightness( - map, - layerName, - 'fill-color', - style.color, - Number(style.lightness) - ); - applySaturation( - map, - layerName, - 'fill-color', - style.color, - Number(style.saturation) + if (subFeatureName === ALL) { + layers.forEach((item) => + applyLandscapeStyle({ map, subFeatureName: item, style }) ); + } else { + applyLandscapeStyle({ map, subFeatureName, style }); } } From a02a50072f3d91f4cac364f23db62b2336878fb7 Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Wed, 2 Dec 2020 14:03:03 +0900 Subject: [PATCH 068/138] =?UTF-8?q?[refactor]=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=ED=83=80=EC=9E=85=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이름이 유사하고 쓰임이 같은 타입 제거 --- src/store/common/type.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index d2e169d..9ba1caa 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,17 +1,17 @@ import { init, setStyle } from '../style/action'; -export enum ElementName { +export enum ElementNameType { section = 'section', labelText = 'labelText', labelIcon = 'labelIcon', } -export enum SubElementName { +export enum SubElementNameType { fill = 'fill', stroke = 'stroke', } -export enum StyleKeyName { +export enum StyleKeyType { visibility = 'visibility', color = 'color', weight = 'weight', @@ -20,7 +20,7 @@ export enum StyleKeyName { isChanged = 'isChanged', } -export enum FeatureName { +export enum FeatureNameType { poi = 'poi', administrative = 'administrative', landscape = 'landscape', @@ -30,11 +30,6 @@ export enum FeatureName { marker = 'marker', } -export type ElementNameType = keyof typeof ElementName; -export type SubElementNameType = keyof typeof SubElementName; -export type FeatureNameType = keyof typeof FeatureName; -export type StyleKeyType = keyof typeof StyleKeyName; - export interface objType { // eslint-disable-next-line @typescript-eslint/no-explicit-any [name: string]: any; @@ -44,9 +39,9 @@ export interface StyleType { isChanged: boolean; visibility: string; color: string; - weight: string; - saturation: string; - lightness: string; + weight: number; + saturation: number; + lightness: number; } export interface ElementType { fill: StyleType; From faadd1892132843e7b75756fe26bf56d4b51d50c Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Wed, 2 Dec 2020 14:03:55 +0900 Subject: [PATCH 069/138] =?UTF-8?q?[refactor]=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=ED=83=80=EC=9E=85=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20import=ED=95=9C=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EB=93=A4=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이전 commit의 중복되는 타입제거에 따라, 다른 파일에서 import 해주었던 type 변경 --- .../Sidebar/SidebarContentMore/ColorStyle.tsx | 6 +- .../Sidebar/SidebarContentMore/DetailType.tsx | 42 +++-- .../SidebarContentMore/LightnessStyle.tsx | 8 +- .../SidebarContentMore/SaturationStyle.tsx | 8 +- .../SidebarContentMore/VisibilityStyle.tsx | 6 +- .../SidebarContentMore/WeightStyle.tsx | 8 +- src/hooks/common/useInputRange.ts | 8 +- src/hooks/sidebar/useStyleType.ts | 11 +- src/store/style/compareStyle.ts | 4 +- src/store/style/getReducer.ts | 4 +- src/store/style/properties.ts | 6 +- src/utils/applyStyle.ts | 46 +++--- src/utils/map-styling/administrative.ts | 58 +++---- src/utils/map-styling/index.ts | 6 +- src/utils/map-styling/landscape.ts | 37 ++--- src/utils/map-styling/poi.ts | 40 +++-- src/utils/map-styling/road.ts | 92 ++++++----- src/utils/map-styling/transit.ts | 152 +++++++++--------- src/utils/map-styling/water.ts | 57 ++++--- src/utils/rendering-data/featureTypeData.ts | 14 +- 20 files changed, 311 insertions(+), 302 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx b/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx index 6bba068..f114141 100644 --- a/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/ColorStyle.tsx @@ -1,7 +1,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import useInputRange from '../../../hooks/common/useInputRange'; -import { StyleKeyName } from '../../../store/common/type'; +import { StyleKeyType } from '../../../store/common/type'; const ColorWrapper = styled.div` display: flex; @@ -30,7 +30,7 @@ const ColorPalette = styled.input` interface ColorStyleProps { color: string; - onStyleChange: (key: StyleKeyName, value: string | number) => void; + onStyleChange: (key: StyleKeyType, value: string | number) => void; } function ColorStyle({ @@ -50,7 +50,7 @@ function ColorStyle({ type="color" id="styler__color" onChange={rangeChangeHandler} - onBlur={() => rangeMouseUpHandler(StyleKeyName.color)} + onBlur={() => rangeMouseUpHandler(StyleKeyType.color)} value={curRange} /> diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 2168c5f..133d776 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -12,8 +12,6 @@ import { FeatureNameType, ElementNameType, SubElementNameType, - ElementName, - SubElementName, } from '../../../store/common/type'; interface PaddingProp { @@ -106,24 +104,30 @@ function DetailType({ {section?.fill.isChanged ? : <>} { - styleClickHandler(ElementName.section, SubElementName.fill); + styleClickHandler( + ElementNameType.section, + SubElementNameType.fill + ); }} name="채우기" /> {section?.stroke.isChanged ? : <>} { - styleClickHandler(ElementName.section, SubElementName.stroke); + styleClickHandler( + ElementNameType.section, + SubElementNameType.stroke + ); }} name="윤곽선" /> @@ -135,33 +139,39 @@ function DetailType({ {labelText?.fill.isChanged ? : <>} { - styleClickHandler(ElementName.labelText, SubElementName.fill); + styleClickHandler( + ElementNameType.labelText, + SubElementNameType.fill + ); }} name="채우기" /> {labelText?.stroke.isChanged ? : <>} { - styleClickHandler(ElementName.labelText, SubElementName.stroke); + styleClickHandler( + ElementNameType.labelText, + SubElementNameType.stroke + ); }} name="윤곽선" /> {labelIcon?.isChanged ? : <>} styleClickHandler(ElementName.labelIcon)} + clickHandler={() => styleClickHandler(ElementNameType.labelIcon)} name="아이콘" /> diff --git a/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx b/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx index 28bc1d1..11b96bd 100644 --- a/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/LightnessStyle.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; import useInputRange from '../../../hooks/common/useInputRange'; -import { StyleKeyName } from '../../../store/common/type'; +import { StyleKeyType } from '../../../store/common/type'; const LightnessWrapper = styled.div` display: flex; @@ -23,8 +23,8 @@ const LightnessControlBar = styled(Range)` `; interface LightnessPropsInterface { - lightness: string; - onStyleChange: (key: StyleKeyName, value: string | number) => void; + lightness: number; + onStyleChange: (key: StyleKeyType, value: string | number) => void; } function LightnessStyle({ @@ -48,7 +48,7 @@ function LightnessStyle({ id="styler__lightness" value={curRange} onChange={(e) => rangeChangeHandler(e)} - onMouseUp={() => rangeMouseUpHandler(StyleKeyName.lightness)} + onMouseUp={() => rangeMouseUpHandler(StyleKeyType.lightness)} /> diff --git a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx index 1f75552..605a4cf 100644 --- a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; import useInputRange from '../../../hooks/common/useInputRange'; -import { StyleKeyName } from '../../../store/common/type'; +import { StyleKeyType } from '../../../store/common/type'; const SaturationWrapper = styled.div` display: flex; @@ -23,8 +23,8 @@ const SaturationControlBar = styled(Range)` `; interface SaturationStyleProps { - saturation: string; - onStyleChange: (key: StyleKeyName, value: string | number) => void; + saturation: number; + onStyleChange: (key: StyleKeyType, value: string | number) => void; } function SaturationStyle({ @@ -47,7 +47,7 @@ function SaturationStyle({ id="styler__saturation" value={curRange} onChange={(e) => rangeChangeHandler(e)} - onMouseUp={() => rangeMouseUpHandler(StyleKeyName.saturation)} + onMouseUp={() => rangeMouseUpHandler(StyleKeyType.saturation)} /> ); diff --git a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx index 9a5a186..d0fc9a0 100644 --- a/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/VisibilityStyle.tsx @@ -1,6 +1,6 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; -import { StyleKeyName } from '../../../store/common/type'; +import { StyleKeyType } from '../../../store/common/type'; interface CheckedProp { checked: boolean; @@ -47,7 +47,7 @@ const Circle = styled.div` interface VisibilityStyleProps { visibility: string; - onStyleChange: (key: StyleKeyName, value: string | number) => void; + onStyleChange: (key: StyleKeyType, value: string | number) => void; } function VisibilityStyle({ @@ -66,7 +66,7 @@ function VisibilityStyle({ {list.map((item) => ( onStyleChange(StyleKeyName.visibility, item.value)} + onClick={() => onStyleChange(StyleKeyType.visibility, item.value)} > diff --git a/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx b/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx index b69c10d..b345dff 100644 --- a/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/WeightStyle.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; import useInputRange from '../../../hooks/common/useInputRange'; -import { StyleKeyName } from '../../../store/common/type'; +import { StyleKeyType } from '../../../store/common/type'; const WeightWrapper = styled.div` display: flex; @@ -23,8 +23,8 @@ const WeightControlBar = styled(Range)` `; interface WeightStyleProps { - weight: string; - onStyleChange: (key: StyleKeyName, value: string | number) => void; + weight: number; + onStyleChange: (key: StyleKeyType, value: string | number) => void; } function WeightStyle({ @@ -47,7 +47,7 @@ function WeightStyle({ id="styler__weight" value={curRange} onChange={rangeChangeHandler} - onMouseUp={() => rangeMouseUpHandler(StyleKeyName.weight)} + onMouseUp={() => rangeMouseUpHandler(StyleKeyType.weight)} /> ); diff --git a/src/hooks/common/useInputRange.ts b/src/hooks/common/useInputRange.ts index b3150e7..1b1bfc8 100644 --- a/src/hooks/common/useInputRange.ts +++ b/src/hooks/common/useInputRange.ts @@ -1,15 +1,15 @@ import React, { useState, useEffect } from 'react'; -import { StyleKeyName } from '../../store/common/type'; +import { StyleKeyType } from '../../store/common/type'; interface UseInputRangeProps { range: string | number; - onStyleChange: (key: StyleKeyName, value: string | number) => void; + onStyleChange: (key: StyleKeyType, value: string | number) => void; } interface InputRangeHookType { curRange: string | number; rangeChangeHandler: (e: React.ChangeEvent) => void; - rangeMouseUpHandler: (key: StyleKeyName) => void; + rangeMouseUpHandler: (key: StyleKeyType) => void; } function useInputRange({ @@ -26,7 +26,7 @@ function useInputRange({ setCurRange(e.target.value); }; - const rangeMouseUpHandler = (key: StyleKeyName) => { + const rangeMouseUpHandler = (key: StyleKeyType) => { onStyleChange(key, curRange); }; diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index 9f68346..b4929d4 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -5,10 +5,9 @@ import { RootState } from '../../store'; import { FeatureNameType, StyleType, + StyleKeyType, ElementNameType, SubElementNameType, - ElementName, - StyleKeyName, } from '../../store/common/type'; import { setStyle } from '../../store/style/action'; import { getDefaultStyle } from '../../store/style/properties'; @@ -23,7 +22,7 @@ interface UseStyleTypeProps { export interface UseStyleHookType { styleElement: StyleType; - onStyleChange: (key: StyleKeyName, value: string | number) => void; + onStyleChange: (key: StyleKeyType, value: string | number) => void; } function useStyleType({ @@ -40,20 +39,18 @@ function useStyleType({ return getDefaultStyle(); } const feature = state[featureName][subFeatureName]; - if (detailName === ElementName.labelIcon) return feature[detailName]; + if (detailName === ElementNameType.labelIcon) return feature[detailName]; return feature[detailName][subDetailName as SubElementNameType]; }) as StyleType; const onStyleChange = useCallback( - (key: StyleKeyName, value: string | number) => { + (key: StyleKeyType, value: string | number) => { mapStyling[featureName]({ map, subFeatureName, key, detailName, - key, subDetailName: subDetailName as SubElementNameType, - key, style: { ...styleElement, [key]: value, diff --git a/src/store/style/compareStyle.ts b/src/store/style/compareStyle.ts index 88459d7..7f8c506 100644 --- a/src/store/style/compareStyle.ts +++ b/src/store/style/compareStyle.ts @@ -1,5 +1,5 @@ import { getDefaultStyle } from './properties'; -import { StyleType, StyleKeyType, objType, StyleKeyName } from '../common/type'; +import { StyleType, StyleKeyType, objType } from '../common/type'; export function checkStyleIsChanged(targetStyle: StyleType): boolean { const defaultStyle: StyleType = getDefaultStyle(); @@ -7,7 +7,7 @@ export function checkStyleIsChanged(targetStyle: StyleType): boolean { const filteredKeys = keys.filter( (key) => - key === StyleKeyName.isChanged || defaultStyle[key] === targetStyle[key] + key === StyleKeyType.isChanged || defaultStyle[key] === targetStyle[key] ); return keys.length !== filteredKeys.length; } diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts index e28724a..d32f05c 100644 --- a/src/store/style/getReducer.ts +++ b/src/store/style/getReducer.ts @@ -6,7 +6,7 @@ import { ActionType, SubElementNameType, objType, - ElementName, + ElementNameType, } from '../common/type'; import { getDefaultFeature } from './properties'; import { INIT, SET } from './action'; @@ -50,7 +50,7 @@ export default function getReducer(IDX: number): ReducerType { const newFeature: FeatureType = newState[subFeature as string]; let prevIsChanged; - if (element === ElementName.labelIcon) { + if (element === ElementNameType.labelIcon) { prevIsChanged = newFeature[element].isChanged; newFeature[element] = style; } else { diff --git a/src/store/style/properties.ts b/src/store/style/properties.ts index b1b3661..83b0051 100644 --- a/src/store/style/properties.ts +++ b/src/store/style/properties.ts @@ -4,9 +4,9 @@ const style: StyleType = { isChanged: false, visibility: 'inherit', color: '#55bf40', - weight: '0', - saturation: '0', - lightness: '0', + weight: 0, + saturation: 0, + lightness: 0, }; export const getDefaultStyle = (): StyleType => { diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index bbcd645..42da7d0 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -2,49 +2,47 @@ /* eslint-disable no-debugger */ /* eslint-disable no-case-declarations */ import mapboxgl from 'mapbox-gl'; -import { StyleKeyName } from '../store/common/type'; import { hexToHSL } from './colorFormat'; -export enum ColorTypeName { - 'fill-color' = 'fill-color', - 'line-color' = 'line-color', - 'text-color' = 'text-color', - 'text-halo-color' = 'text-halo-color', +export enum ColorType { + fill = 'fill-color', + line = 'line-color', + text = 'text-color', + textHalo = 'text-halo-color', + icon = 'icon-opacity', } -export enum WeightTypeName { - 'line-width' = 'line-width', - 'text-halo-width' = 'text-halo-width', - 'icon-opacity' = 'icon-opacity', +export enum WeightType { + line = 'line-width', + textHalo = 'text-halo-width', } -export type colorType = keyof typeof ColorTypeName; -export type weightType = keyof typeof WeightTypeName; -interface ApplyColorProps { +interface ApplyProps { map: mapboxgl.Map; layerNames: string[]; color?: string; - type?: colorType | weightType; + type?: StyleTypes; saturation?: number; lightness?: number; - weight?: string; + weight?: number; visibility?: string; +} -export enum visibilityType { +export enum VisibilityType { visibility = 'visibility', } -export type styleTypes = visibilityType | colorType | weightType; +export type StyleTypes = VisibilityType | ColorType | WeightType; export function applyColor({ map, layerNames, - color = '#000', + color, type, saturation, lightness, -}: ApplyColorProps): void { - if (!type) return; +}: ApplyProps): void { + if (!type || !color) return; const { h, s, l } = hexToHSL(color); if (saturation) { @@ -76,8 +74,8 @@ export function applyVisibility({ map, layerNames, visibility, -}: ApplyColorProps): void { - layerNames.forEach((layerName) => { +}: ApplyProps): void { + layerNames.forEach((layerName) => { if (visibility === 'inherit') { map.setLayoutProperty(layerName, 'visibility', 'visible'); } else map.setLayoutProperty(layerName, 'visibility', visibility); @@ -89,8 +87,8 @@ export function applyWeight({ layerNames, type, weight, -}: ApplyColorProps): void { - if (!type) return; +}: ApplyProps): void { + if (!type || !weight) return; const weightValue = weight === 0 ? 0 : weight * 2 + 1; layerNames.forEach((layerName) => { diff --git a/src/utils/map-styling/administrative.ts b/src/utils/map-styling/administrative.ts index 8bd318c..459b846 100644 --- a/src/utils/map-styling/administrative.ts +++ b/src/utils/map-styling/administrative.ts @@ -1,12 +1,12 @@ import { stylingProps } from '.'; import { - ElementName, - SubElementName, - StyleKeyName, + ElementNameType, + SubElementNameType, + StyleKeyType, } from '../../store/common/type'; import { - ColorTypeName, - WeightTypeName, + ColorType, + WeightType, applyVisibility, applyColor, applyWeight, @@ -58,21 +58,25 @@ function administrativeStyling({ }: stylingProps): void { const mappingLayers = layers[subFeatureName as subFeatureNameType]; - if (detailName === ElementName.labelIcon) return; - if (detailName === ElementName.labelText) { + if (detailName === ElementNameType.labelIcon) return; + if (detailName === ElementNameType.labelText) { // labelText - fill - if (subDetailName === SubElementName.fill) { - if (key === StyleKeyName.weight) return; - if (key === StyleKeyName.visibility) { + if (subDetailName === SubElementNameType.fill) { + if (key === StyleKeyType.weight) return; + if (key === StyleKeyType.visibility) { const styleValue = style[key] === 'none' ? 'none' : 'visible'; - applyVisibility(map, mappingLayers.symbol, styleValue); + applyVisibility({ + map, + layerNames: mappingLayers.symbol, + visibility: styleValue, + }); return; } applyColor({ map, layerNames: mappingLayers.symbol, - type: ColorTypeName['text-color'], + type: ColorType.text, color: style.color, [key]: style[key], }); @@ -80,21 +84,21 @@ function administrativeStyling({ } // labelText - stroke - if (key === StyleKeyName.weight || key === StyleKeyName.visibility) { + if (key === StyleKeyType.weight || key === StyleKeyType.visibility) { const styleValue = style.visibility === 'none' ? 0 : Number(style.weight); - applyWeight( + applyWeight({ map, - mappingLayers.symbol, - WeightTypeName['text-halo-width'], - styleValue - ); + layerNames: mappingLayers.symbol, + type: WeightType.textHalo, + weight: styleValue, + }); return; } applyColor({ map, layerNames: mappingLayers.symbol, - type: ColorTypeName['text-halo-color'], + type: ColorType.textHalo, color: style.color, [key]: style[key], }); @@ -105,26 +109,26 @@ function administrativeStyling({ if ( subFeatureName === 'locality' || subFeatureName === 'neighborhood' || - subDetailName === SubElementName.fill + subDetailName === SubElementNameType.fill ) return; // section - stroke - if (key === StyleKeyName.weight || key === StyleKeyName.visibility) { + if (key === StyleKeyType.weight || key === StyleKeyType.visibility) { const styleValue = style.visibility === 'none' ? 0 : Number(style.weight); - applyWeight( + applyWeight({ map, - mappingLayers.line, - WeightTypeName['line-width'], - styleValue - ); + layerNames: mappingLayers.line, + type: WeightType.line, + weight: styleValue, + }); return; } applyColor({ map, layerNames: mappingLayers.line, - type: ColorTypeName['line-color'], + type: ColorType.line, color: style.color, [key]: style[key], }); diff --git a/src/utils/map-styling/index.ts b/src/utils/map-styling/index.ts index d45bbc0..1b259cf 100644 --- a/src/utils/map-styling/index.ts +++ b/src/utils/map-styling/index.ts @@ -2,18 +2,16 @@ import mapboxgl from 'mapbox-gl'; import { ElementNameType, SubElementNameType, - StyleKeyName, + StyleKeyType, StyleType, } from '../../store/common/type'; export interface stylingProps { map: mapboxgl.Map; subFeatureName: string; - key: string; detailName: ElementNameType; - key: string; subDetailName: SubElementNameType; - key: StyleKeyName; + key: StyleKeyType; style: StyleType; } diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index dec32f1..48ee551 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -1,10 +1,5 @@ import { stylingProps } from './index'; -import { - applyColor, - applyLightness, - applyVisibility, - applySaturation, -} from '../applyStyle'; +import { applyColor, applyVisibility, ColorType } from '../applyStyle'; import { StyleType } from '../../store/common/type'; const SECTION = 'section'; @@ -20,24 +15,24 @@ interface applyStyleProps { } function applyLandscapeStyle({ map, subFeatureName, style }: applyStyleProps) { - const layerName = `landscape-${subFeatureName}`; + const layerNames = [`landscape-${subFeatureName}`]; - applyColor(map, layerName, 'fill-color', style.color); - applyVisibility(map, layerName, style.visibility); - applyLightness( + applyColor({ map, layerNames, type: ColorType.fill, color: style.color }); + applyVisibility({ map, layerNames, visibility: style.visibility }); + applyColor({ map, - layerName, - 'fill-color', - style.color, - Number(style.lightness) - ); - applySaturation( + layerNames, + type: ColorType.fill, + color: style.color, + lightness: Number(style.lightness), + }); + applyColor({ map, - layerName, - 'fill-color', - style.color, - Number(style.saturation) - ); + layerNames, + type: ColorType.fill, + color: style.color, + saturation: Number(style.saturation), + }); } function landscapeStyling({ diff --git a/src/utils/map-styling/poi.ts b/src/utils/map-styling/poi.ts index 11011c0..a7acf1f 100644 --- a/src/utils/map-styling/poi.ts +++ b/src/utils/map-styling/poi.ts @@ -4,12 +4,11 @@ import { applyColor, applyWeight, applyVisibility, - colorType, - weightType, - WeightTypeName, - ColorTypeName, + WeightType, + ColorType, + StyleTypes, } from '../applyStyle'; -import { StyleKeyType, ElementName } from '../../store/common/type'; +import { StyleKeyType, ElementNameType } from '../../store/common/type'; type PoiSubFeature = | 'all' @@ -27,8 +26,8 @@ type POI_LAYERS_TYPE = { [name in PoiSubFeature]: string[]; }; -const VISIBLE = '1'; -const INVISIBLE = '0'; +const VISIBLE = 1; +const INVISIBLE = 0; const POI_LAYERS: POI_LAYERS_TYPE = { all: [ 'poi-attraction', @@ -74,15 +73,15 @@ const mappingDetailToFunc = { labelText: { fill: { color: { - typeName: ColorTypeName['text-color'], + typeName: ColorType.text, funcName: applyColor, }, saturation: { - typeName: ColorTypeName['text-color'], + typeName: ColorType.text, funcName: applyColor, }, lightness: { - typeName: ColorTypeName['text-color'], + typeName: ColorType.text, funcName: applyColor, }, weight: { @@ -100,23 +99,23 @@ const mappingDetailToFunc = { }, stroke: { color: { - typeName: ColorTypeName['text-halo-color'], + typeName: ColorType.textHalo, funcName: applyColor, }, saturation: { - typeName: ColorTypeName['text-halo-color'], + typeName: ColorType.textHalo, funcName: applyColor, }, lightness: { - typeName: ColorTypeName['text-halo-color'], + typeName: ColorType.textHalo, funcName: applyColor, }, weight: { - typeName: WeightTypeName['text-halo-width'], + typeName: WeightType.textHalo, funcName: applyWeight, }, visibility: { - typeName: WeightTypeName['text-halo-width'], + typeName: WeightType.textHalo, funcName: applyWeight, }, isChanged: { @@ -143,7 +142,7 @@ const mappingDetailToFunc = { funcName: () => null, }, visibility: { - typeName: WeightTypeName['icon-opacity'], + typeName: ColorType.icon, funcName: applyWeight, }, isChanged: { @@ -164,8 +163,8 @@ function poiStyling({ let type = null; let func = null; - if (detailName === ElementName.section) return; - if (detailName === ElementName.labelText) { + if (detailName === ElementNameType.section) return; + if (detailName === ElementNameType.labelText) { const { typeName, funcName } = mappingDetailToFunc[detailName][ subDetailName ][key as StyleKeyType]; @@ -182,8 +181,7 @@ function poiStyling({ if (!type) return; if ( key === 'visibility' && - (type === WeightTypeName['icon-opacity'] || - type === WeightTypeName['text-halo-width']) + (type === ColorType.icon || type === WeightType.textHalo) ) { func({ map, @@ -197,7 +195,7 @@ function poiStyling({ func({ map, layerNames: POI_LAYERS[subFeatureName as PoiSubFeature], - type: type as weightType | colorType, + type: type as StyleTypes, color: style.color, [key]: style[key as StyleKeyType], }); diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index 7a40419..aaf310a 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -1,30 +1,12 @@ import { stylingProps } from '.'; -import { applyVisibility, applyColor, applyWeight } from '../applyStyle'; - -type KeyType = 'visibility' | 'color' | 'weight' | 'saturation' | 'lightness'; - -/** - * visibility inherit 처리 applyStyle에서 - * color, saturation, lightness 한 함수로 처리 - * - section - * - fill : line 'line-color', symbol, polygon 'fill-color' - * - stroke : line 'case layer'의 색상, symbol 불변, polygon이면 불변 - * - label - * - text fill: 'text-color', 'text-width' - * - text stroke: 'text-halo-color', 'text-halo-width' - * - */ - -/** - * 20.12.01. - * highway : 분류 보류 - * aterial: line이라 채우기 윤곽선은 한번에 처리, 채우기 윤곽선 확인완료, 아이콘 없음 - * local: 지금 있는 것들은 section fill밖에 의미없음 - * sidewalk: 확인 완료 - * - * 색상을 변경하지 않은채로 채도나 밝기부터 선택하면 색상팔레트 색 기준으로 조정이 됨 - * fix 필요 - */ +import { + applyVisibility, + applyColor, + applyWeight, + ColorType, + WeightType, +} from '../applyStyle'; +import { StyleKeyType } from '../../store/common/type'; function roadStyling({ map, @@ -50,75 +32,91 @@ function roadStyling({ subDetailName === 'fill' && key === 'visibility' ) { - applyVisibility(map, arterialLayerNames, visibility); + applyVisibility({ map, layerNames: arterialLayerNames, visibility }); } else if (key === 'color' || key === 'saturation' || key === 'lightness') { if (detailName === 'section' && subDetailName === 'fill') { applyColor({ map, layerNames: ['road-arterial'], color, - type: 'line-color', - [key]: style[key as KeyType], + type: ColorType.line, + [key]: style[key as StyleKeyType], }); } else if (detailName === 'labelText' && subDetailName === 'fill') { applyColor({ map, layerNames: ['road-number-shield', 'road-exit-shield', 'road-label'], color, - type: 'text-color', - [key]: style[key as KeyType], + type: ColorType.text, + [key]: style[key as StyleKeyType], }); } else if (detailName === 'labelText' && subDetailName === 'stroke') { applyColor({ map, layerNames: ['road-label'], color, - type: 'text-halo-color', - [key]: style[key as KeyType], + type: ColorType.textHalo, + [key]: style[key as StyleKeyType], }); } } else if (key === 'weight') { if (detailName === 'section' && subDetailName === 'fill') { - applyWeight(map, ['road-arterial'], 'line-width', weight); + applyWeight({ + map, + layerNames: ['road-arterial'], + type: WeightType.line, + weight, + }); } if (detailName === 'labelText' && subDetailName === 'stroke') - applyWeight(map, ['road-label'], 'text-halo-width', weight); + applyWeight({ + map, + layerNames: ['road-label'], + type: ColorType.textHalo, + weight, + }); } } else if (subFeatureName === 'local') { - if (key === 'visibility') applyVisibility(map, localLayerNames, visibility); + if (key === 'visibility') + applyVisibility({ map, layerNames: localLayerNames, visibility }); else if (key === 'color' || key === 'saturation' || key === 'lightness') { if (detailName === 'section' && subDetailName === 'fill') { applyColor({ map, layerNames: ['ferry', 'ferry-auto', 'road-local'], color, - type: 'line-color', - [key]: style[key as KeyType], + type: ColorType.line, + [key]: style[key as StyleKeyType], }); } } else if (key === 'weight') { if (detailName === 'section' && subDetailName === 'fill') - applyWeight( + applyWeight({ map, - ['ferry', 'ferry-auto', 'road-local'], - 'line-width', - weight - ); + layerNames: ['ferry', 'ferry-auto', 'road-local'], + type: WeightType.line, + weight, + }); } } else if (subFeatureName === 'sidewalk') { if (key === 'visibility') - applyVisibility(map, sidewalkLayerNames, visibility); + applyVisibility({ map, layerNames: sidewalkLayerNames, visibility }); else if (key === 'weight') { if (detailName === 'section' && subDetailName === 'fill') - applyWeight(map, sidewalkLayerNames, 'line-width', weight); + applyWeight({ + map, + layerNames: sidewalkLayerNames, + type: WeightType.line, + weight, + }); } else if (key === 'color' || key === 'saturation' || key === 'lightness') { if (detailName === 'section' && subDetailName === 'fill') applyColor({ map, layerNames: sidewalkLayerNames, color, - type: 'line-color', - [key]: style[key as KeyType], + type: ColorType.line, + [key]: style[key as StyleKeyType], }); } } diff --git a/src/utils/map-styling/transit.ts b/src/utils/map-styling/transit.ts index ef6d703..6f84c12 100644 --- a/src/utils/map-styling/transit.ts +++ b/src/utils/map-styling/transit.ts @@ -1,21 +1,22 @@ /* eslint-disable no-nested-ternary */ -/* eslint-disable no-debugger */ /* eslint-disable no-case-declarations */ import { stylingProps } from '.'; -import { StyleKeyName, StyleType } from '../../store/common/type'; +import { + StyleKeyType, + ElementNameType, + SubElementNameType, +} from '../../store/common/type'; import { applyVisibility, applyColor, applyWeight, - applyTextSize, - ColorTypeName, - styleTypes, - WeightTypeName, + ColorType, + WeightType, + StyleTypes, } from '../../utils/applyStyle'; const AirportLayerNames = [ 'airport-label', - 'aeroway-polygon', 'transit-airport', 'aeroway-line', @@ -40,71 +41,80 @@ function transitStyling({ subDetailName, style, }: stylingProps): void { - const layers = - subFeatureName === 'all' - ? [...AllLayerTypeKeys] - : subFeatureName === 'subway' - ? [...SubwayLayerNames] - : subFeatureName === 'bus' - ? [...BusLayerNames] - : subFeatureName === 'airport' - ? [...AirportLayerNames] - : [...RailLayerNames]; + let layers: string[]; + switch (subFeatureName) { + case 'all': + layers = [...AllLayerTypeKeys]; + break; + case 'subway': + layers = [...SubwayLayerNames]; + break; + case 'bus': + layers = [...BusLayerNames]; + break; + case 'airport': + layers = [...AirportLayerNames]; + break; + case 'rail': + layers = [...RailLayerNames]; + break; + default: + return; + } let layersByDetailName: string[]; - let styleType: styleTypes; - debugger; + let styleType: StyleTypes; + switch (detailName) { - case 'section': + case ElementNameType.section: layersByDetailName = layers.filter((layer) => !layer.includes('label')); styleType = - subDetailName === 'stroke' - ? key === 'weight' - ? WeightTypeName['line-width'] - : ColorTypeName['line-color'] - : ColorTypeName['fill-color']; + subDetailName === SubElementNameType.stroke + ? key === StyleKeyType.weight + ? WeightType.line + : ColorType.line + : ColorType.fill; break; - case 'labelText': + case ElementNameType.labelText: layersByDetailName = layers.filter((layer) => layer.includes('label')); styleType = - subDetailName === 'stroke' - ? key === 'weight' - ? WeightTypeName['text-halo-width'] - : ColorTypeName['text-halo-color'] - : key === 'weight' - ? WeightTypeName['text-size'] - : ColorTypeName['text-color']; - debugger; + subDetailName === SubElementNameType.stroke + ? key === StyleKeyType.weight + ? WeightType.textHalo + : ColorType.textHalo + : ColorType.text; break; - case 'labelIcon': + case ElementNameType.labelIcon: layersByDetailName = layers.filter((layer) => !layer.includes('label')); styleType = - subDetailName === 'stroke' - ? ColorTypeName['line-color'] - : ColorTypeName['fill-color']; + subDetailName === SubElementNameType.stroke + ? ColorType.line + : ColorType.fill; break; default: return; } - const styleKey: StyleKeyName = key as StyleKeyName; + const styleKey: StyleKeyType = key as StyleKeyType; const { [styleKey]: value } = style; - let stylerFunction: (obj: any) => void; switch (styleKey) { - case StyleKeyName.visibility: - stylerFunction = ({ layerNames }: { layerNames: string[] }) => - applyVisibility(map, layerNames, value as string); + case StyleKeyType.visibility: + applyVisibility({ + map, + layerNames: layersByDetailName, + visibility: value as string, + }); break; - case StyleKeyName.color: - case StyleKeyName.saturation: - case StyleKeyName.lightness: - const colorKey = StyleKeyName.color; - const satKey = StyleKeyName.saturation; - const ligKey = StyleKeyName.lightness; + case StyleKeyType.color: + case StyleKeyType.saturation: + case StyleKeyType.lightness: + const colorKey = StyleKeyType.color; + const satKey = StyleKeyType.saturation; + const ligKey = StyleKeyType.lightness; const satureOrLight = key === 'saturation' @@ -112,40 +122,30 @@ function transitStyling({ : key === 'lightness' ? { lightness: +style[ligKey] } : {}; - stylerFunction = ({ layerNames }: { layerNames: string[] }) => - applyColor({ + applyColor({ + map, + layerNames: layersByDetailName, + type: styleType, + color: style[colorKey], + ...satureOrLight, + }); + break; + + case StyleKeyType.weight: + if (subDetailName !== 'fill') { + applyWeight({ map, - layerNames, - type: styleType, - color: style[colorKey], - ...satureOrLight, + layerNames: layersByDetailName, + type: styleType as WeightType, + weight: value as number, }); - break; + } - case StyleKeyName.weight: - stylerFunction = - subDetailName !== 'fill' - ? ({ layerNames }: { layerNames: string[] }) => - applyWeight( - map, - layerNames, - styleType as WeightTypeName, - value as string - ) - : ({ layerNames }: { layerNames: string[] }) => - applyTextSize( - map, - layerNames, - styleType as WeightTypeName, - value as string - ); break; default: - return; + break; } - - stylerFunction({ layerNames: layersByDetailName }); } export default transitStyling; diff --git a/src/utils/map-styling/water.ts b/src/utils/map-styling/water.ts index 2261cb5..a87215d 100644 --- a/src/utils/map-styling/water.ts +++ b/src/utils/map-styling/water.ts @@ -1,12 +1,12 @@ import { stylingProps } from '.'; import { - ElementName, - SubElementName, - StyleKeyName, + ElementNameType, + SubElementNameType, + StyleKeyType, } from '../../store/common/type'; import { - ColorTypeName, - WeightTypeName, + ColorType, + WeightType, applyVisibility, applyColor, applyWeight, @@ -24,22 +24,26 @@ function waterStyling({ key, style, }: stylingProps): void { - if (detailName === ElementName.labelIcon) return; + if (detailName === ElementNameType.labelIcon) return; - if (detailName === ElementName.labelText) { + if (detailName === ElementNameType.labelText) { // labelText - fill - if (subDetailName === SubElementName.fill) { - if (key === StyleKeyName.weight) return; - if (key === StyleKeyName.visibility) { + if (subDetailName === SubElementNameType.fill) { + if (key === StyleKeyType.weight) return; + if (key === StyleKeyType.visibility) { const styleValue = style[key] === 'none' ? 'none' : 'visible'; - applyVisibility(map, layers.symbol, styleValue); + applyVisibility({ + map, + layerNames: layers.symbol, + visibility: styleValue, + }); return; } applyColor({ map, layerNames: layers.symbol, - type: ColorTypeName['text-color'], + type: ColorType.text, color: style.color, [key]: style[key], }); @@ -47,21 +51,21 @@ function waterStyling({ } // labelText - stroke - if (key === StyleKeyName.weight || key === StyleKeyName.visibility) { + if (key === StyleKeyType.weight || key === StyleKeyType.visibility) { const styleValue = style.visibility === 'none' ? 0 : Number(style.weight); - applyWeight( + applyWeight({ map, - layers.symbol, - WeightTypeName['text-halo-width'], - styleValue - ); + layerNames: layers.symbol, + type: WeightType.textHalo, + weight: styleValue, + }); return; } applyColor({ map, layerNames: layers.symbol, - type: ColorTypeName['text-halo-color'], + type: ColorType.textHalo, color: style.color, [key]: style[key], }); @@ -69,20 +73,27 @@ function waterStyling({ } // section - stroke - if (subDetailName === SubElementName.stroke || key === StyleKeyName.weight) + if ( + subDetailName === SubElementNameType.stroke || + key === StyleKeyType.weight + ) return; // section - fill - if (key === StyleKeyName.visibility) { + if (key === StyleKeyType.visibility) { const styleValue = style[key] === 'none' ? 'none' : 'visible'; - applyVisibility(map, layers.polygon, styleValue); + applyVisibility({ + map, + layerNames: layers.polygon, + visibility: styleValue, + }); return; } applyColor({ map, layerNames: layers.polygon, - type: ColorTypeName['fill-color'], + type: ColorType.fill, color: style.color, [key]: style[key], }); diff --git a/src/utils/rendering-data/featureTypeData.ts b/src/utils/rendering-data/featureTypeData.ts index b9c2380..fb7a5d5 100644 --- a/src/utils/rendering-data/featureTypeData.ts +++ b/src/utils/rendering-data/featureTypeData.ts @@ -12,7 +12,7 @@ export interface DataType { const data: DataType[] = [ { - typeKey: 'poi', + typeKey: FeatureNameType.poi, typeName: 'POI', features: [ { key: 'landmark', name: '랜드마크' }, @@ -27,7 +27,7 @@ const data: DataType[] = [ ], }, { - typeKey: 'road', + typeKey: FeatureNameType.road, typeName: '도로', features: [ { key: 'highway', name: '고속도로' }, @@ -37,7 +37,7 @@ const data: DataType[] = [ ], }, { - typeKey: 'administrative', + typeKey: FeatureNameType.administrative, typeName: '행정구역', features: [ { key: 'country', name: '국가' }, @@ -46,7 +46,7 @@ const data: DataType[] = [ ], }, { - typeKey: 'landscape', + typeKey: FeatureNameType.landscape, typeName: '경관', features: [ { key: 'human-made', name: '인공물' }, @@ -57,7 +57,7 @@ const data: DataType[] = [ ], }, { - typeKey: 'transit', + typeKey: FeatureNameType.transit, typeName: '교통', features: [ { key: 'airport', name: '공항' }, @@ -66,8 +66,8 @@ const data: DataType[] = [ { key: 'subway', name: '지하철' }, ], }, - { typeKey: 'water', typeName: '물', features: [] }, - { typeKey: 'marker', typeName: '마크', features: [] }, + { typeKey: FeatureNameType.water, typeName: '물', features: [] }, + { typeKey: FeatureNameType.marker, typeName: '마크', features: [] }, ]; export default data; From d569ba31dc94faae6ff7929cbc3d4191eacc9cfa Mon Sep 17 00:00:00 2001 From: tejava Date: Wed, 2 Dec 2020 14:49:45 +0900 Subject: [PATCH 070/138] =?UTF-8?q?[fix]=20key=EA=B0=92=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20=EC=83=88=EB=A1=9C=20=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81=EB=90=98=EB=8A=94=20=EB=B6=80=EB=B6=84=EC=9D=84=20?= =?UTF-8?q?=EC=B5=9C=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존의 구현은 key를 인자로 받지 못해 모든 요소를 재설정했다. 이를 막고자 PR에서 추가된 key를 기반으로 분기를 나누어 업데이트하도록 했다. --- src/utils/map-styling/landscape.ts | 64 ++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index 48ee551..1964333 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -1,6 +1,6 @@ import { stylingProps } from './index'; import { applyColor, applyVisibility, ColorType } from '../applyStyle'; -import { StyleType } from '../../store/common/type'; +import { StyleKeyType, StyleType } from '../../store/common/type'; const SECTION = 'section'; const FILL = 'fill'; @@ -11,28 +11,51 @@ const layers = ['human-made', 'building', 'natural', 'landcover']; interface applyStyleProps { map: mapboxgl.Map; subFeatureName: string; + key: StyleKeyType; style: StyleType; } -function applyLandscapeStyle({ map, subFeatureName, style }: applyStyleProps) { +function applyLandscapeStyle({ + map, + subFeatureName, + key, + style, +}: applyStyleProps): void { const layerNames = [`landscape-${subFeatureName}`]; - applyColor({ map, layerNames, type: ColorType.fill, color: style.color }); - applyVisibility({ map, layerNames, visibility: style.visibility }); - applyColor({ - map, - layerNames, - type: ColorType.fill, - color: style.color, - lightness: Number(style.lightness), - }); - applyColor({ - map, - layerNames, - type: ColorType.fill, - color: style.color, - saturation: Number(style.saturation), - }); + switch (key) { + case 'color': + applyColor({ + map, + layerNames, + type: ColorType.fill, + color: style.color, + }); + break; + case 'visibility': + applyVisibility({ map, layerNames, visibility: style.visibility }); + break; + case 'lightness': + applyColor({ + map, + layerNames, + type: ColorType.fill, + color: style.color, + lightness: Number(style.lightness), + }); + break; + case 'saturation': + applyColor({ + map, + layerNames, + type: ColorType.fill, + color: style.color, + saturation: Number(style.saturation), + }); + break; + default: + break; + } } function landscapeStyling({ @@ -40,6 +63,7 @@ function landscapeStyling({ subFeatureName, detailName, subDetailName, + key, style, }: stylingProps): void { if (detailName !== SECTION || subDetailName !== FILL) { @@ -48,10 +72,10 @@ function landscapeStyling({ if (subFeatureName === ALL) { layers.forEach((item) => - applyLandscapeStyle({ map, subFeatureName: item, style }) + applyLandscapeStyle({ map, subFeatureName: item, key, style }) ); } else { - applyLandscapeStyle({ map, subFeatureName, style }); + applyLandscapeStyle({ map, subFeatureName, key, style }); } } From dfa4f7b33e7d08dbcd59fd1be1175b5a3035f471 Mon Sep 17 00:00:00 2001 From: gitgitwi Date: Wed, 2 Dec 2020 15:10:08 +0900 Subject: [PATCH 071/138] =?UTF-8?q?[FIX]=20[#86]=20=EC=9D=BC=EB=B6=80=20ma?= =?UTF-8?q?pbox=20=EB=A0=88=EC=9D=B4=EC=96=B4=20=EC=95=88=EB=B3=B4?= =?UTF-8?q?=EC=9D=B4=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 관련 항목 transit layer data에 직접 추가 - mapbox 의 관련 layer는 초기 실행시 삭제할 필요 - mapbox transit-label은 여전히 안되어서 삭제 --- src/store/map/layers/transit.ts | 111 ++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts index 18a3ae4..b15482d 100644 --- a/src/store/map/layers/transit.ts +++ b/src/store/map/layers/transit.ts @@ -12,6 +12,96 @@ export default [ filter: ['in', 'class', 'airport'], id: 'transit-airport', }, + { + id: 'mapbox-aeroway-polygon', + type: 'fill', + metadata: {}, + source: 'composite', + 'source-layer': 'aeroway', + minzoom: 11, + filter: [ + 'all', + ['==', ['geometry-type'], 'Polygon'], + ['match', ['get', 'type'], ['runway', 'taxiway', 'helipad'], true, false], + ], + layout: {}, + paint: { + 'fill-color': [ + 'interpolate', + ['linear'], + ['zoom'], + 15, + 'hsl(230, 23%, 82%)', + 16, + 'hsl(230, 37%, 84%)', + ], + 'fill-opacity': ['interpolate', ['linear'], ['zoom'], 11, 0, 11.5, 1], + }, + }, + { + id: 'mapbox-aeroway-line', + type: 'line', + metadata: {}, + source: 'composite', + 'source-layer': 'aeroway', + minzoom: 9, + filter: ['==', ['geometry-type'], 'LineString'], + layout: {}, + paint: { + 'line-color': [ + 'interpolate', + ['linear'], + ['zoom'], + 15, + 'hsl(230, 23%, 82%)', + 16, + 'hsl(230, 37%, 84%)', + ], + 'line-width': [ + 'interpolate', + ['exponential', 1.5], + ['zoom'], + 9, + ['match', ['get', 'type'], 'runway', 1, 'taxiway', 0.5, 0.5], + 18, + ['match', ['get', 'type'], 'runway', 80, 'taxiway', 20, 20], + ], + }, + }, + { + id: 'mapbox-airport-label', + type: 'symbol', + source: 'composite', + 'source-layer': 'airport_label', + minzoom: 8, + layout: { + 'text-size': 12, + 'icon-image': [ + 'step', + ['get', 'sizerank'], + ['concat', ['get', 'maki'], '-15'], + 9, + ['concat', ['get', 'maki'], '-11'], + ], + 'text-offset': [0, 0.75], + 'text-rotation-alignment': 'viewport', + 'text-anchor': 'top', + 'text-field': [ + 'step', + ['get', 'sizerank'], + ['coalesce', ['get', 'name_en'], ['get', 'name']], + 15, + ['get', 'ref'], + ], + 'text-max-width': 9, + }, + paint: { + 'text-color': 'hsl(0, 69%, 50%)', + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 1, + }, + }, + { type: 'line', source: 'line_source', @@ -38,6 +128,27 @@ export default [ filter: ['in', 'type', 'rail'], id: 'transit-rail', }, + { + type: 'line', + source: 'composite', + 'source-layer': 'road', + layout: { + visibility: 'visible', + 'line-join': 'round', + }, + minzoom: 13, + + paint: { + 'line-color': 'hsl(234, 20%, 30%)', + 'line-width': 6, + }, + filter: [ + 'all', + ['match', ['get', 'class'], ['major_rail', 'minor_rail'], true, false], + ['match', ['get', 'structure'], ['none', 'ford'], true, false], + ], + id: 'mapbox-rail-road', + }, { type: 'symbol', source: 'poi_source', From b98c0b5a87795f1cce3c6a48a7e1931be5af23ab Mon Sep 17 00:00:00 2001 From: gitgitwi Date: Wed, 2 Dec 2020 15:26:20 +0900 Subject: [PATCH 072/138] =?UTF-8?q?[PERF]=20[#86]=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EC=83=89=EC=83=81=20=EC=9D=BC=EB=B6=80=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/map/layers/transit.ts | 37 ++++++++++----------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts index b15482d..aa0fe80 100644 --- a/src/store/map/layers/transit.ts +++ b/src/store/map/layers/transit.ts @@ -7,7 +7,7 @@ export default [ visibility: 'visible', }, paint: { - 'fill-color': 'hsl(230, 100%, 44%)', + 'fill-color': 'hsl(234, 20%, 30%)', }, filter: ['in', 'class', 'airport'], id: 'transit-airport', @@ -26,16 +26,7 @@ export default [ ], layout: {}, paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 15, - 'hsl(230, 23%, 82%)', - 16, - 'hsl(230, 37%, 84%)', - ], - 'fill-opacity': ['interpolate', ['linear'], ['zoom'], 11, 0, 11.5, 1], + 'fill-color': 'hsl(40, 97%, 64%)', }, }, { @@ -48,15 +39,7 @@ export default [ filter: ['==', ['geometry-type'], 'LineString'], layout: {}, paint: { - 'line-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 15, - 'hsl(230, 23%, 82%)', - 16, - 'hsl(230, 37%, 84%)', - ], + 'line-color': 'hsl(230, 23%, 82%)', 'line-width': [ 'interpolate', ['exponential', 1.5], @@ -80,7 +63,7 @@ export default [ 'step', ['get', 'sizerank'], ['concat', ['get', 'maki'], '-15'], - 9, + 12, ['concat', ['get', 'maki'], '-11'], ], 'text-offset': [0, 0.75], @@ -110,7 +93,8 @@ export default [ visibility: 'visible', }, paint: { - 'line-color': 'yellow', + 'line-color': 'hsl(192, 70%, 43%)', + 'line-width': 2, }, filter: ['in', 'type', 'subway'], id: 'transit-subway', @@ -123,7 +107,8 @@ export default [ visibility: 'visible', }, paint: { - 'line-color': 'black', + 'line-color': 'hsl(201, 100%, 14%)', + 'line-width': 2, }, filter: ['in', 'type', 'rail'], id: 'transit-rail', @@ -159,9 +144,9 @@ export default [ visibility: 'visible', }, paint: { - 'text-halo-color': 'green', - 'text-halo-width': 0.5, - 'text-color': 'red', + 'text-halo-color': 'hsl(151, 24%, 60%)', + 'text-halo-width': 1, + 'text-color': 'hsl(13, 68%, 63%)', }, filter: ['in', 'type', 'bus_stop'], id: 'transit-bus-label', From 04bd78912f6e56f77817368289c936e7031fd15b Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Wed, 2 Dec 2020 16:18:07 +0900 Subject: [PATCH 073/138] =?UTF-8?q?[fix]=20mapbox=20road-label=20layer=20?= =?UTF-8?q?=EB=82=98=EB=88=84=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각 요소들에 대하여 합쳐져있던 road-label layer를 각 카테고리별로 나누기 --- src/store/map/initializeMap.ts | 2 +- src/store/map/layers/road.ts | 202 +++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 1 deletion(-) diff --git a/src/store/map/initializeMap.ts b/src/store/map/initializeMap.ts index fcdbcfa..108d186 100644 --- a/src/store/map/initializeMap.ts +++ b/src/store/map/initializeMap.ts @@ -16,7 +16,6 @@ const ZOOM = 15.5; const LABEL_LAYERS: string[] = [ 'country-label', 'settlement-label', - 'road-label', 'state-label', 'settlement-subdivision-label', 'airport-label', @@ -68,6 +67,7 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { map.on('load', () => { translate(map); map.removeLayer('poi-label'); + map.removeLayer('road-label'); map.addSource(Sources.polygon, { type: 'vector', diff --git a/src/store/map/layers/road.ts b/src/store/map/layers/road.ts index e1ea102..805aaa0 100644 --- a/src/store/map/layers/road.ts +++ b/src/store/map/layers/road.ts @@ -66,4 +66,206 @@ export default [ filter: ['match', ['get', 'type'], ['pedestrian', 'footway'], true, false], id: 'road-footway', }, + { + id: 'road-arterial-label', + type: 'symbol', + source: 'composite', + 'source-layer': 'road', + metadata: {}, + minzoom: 10, + filter: [ + 'step', + ['zoom'], + [ + 'match', + ['get', 'class'], + ['motorway', 'trunk', 'primary', 'secondary'], + true, + false, + ], + 12, + [ + 'match', + ['get', 'class'], + ['motorway', 'trunk', 'primary', 'secondary'], + true, + false, + ], + ], + layout: { + 'text-size': [ + 'interpolate', + ['linear'], + ['zoom'], + 10, + [ + 'match', + ['get', 'class'], + ['motorway', 'trunk', 'primary', 'secondary'], + 10, + ['motorway_link', 'trunk_link', 'primary_link', 'secondary_link'], + 9, + 6.5, + ], + 18, + [ + 'match', + ['get', 'class'], + ['motorway', 'trunk', 'primary', 'secondary'], + 16, + ['motorway_link', 'trunk_link', 'primary_link', 'secondary_link'], + 14, + 13, + ], + ], + 'text-max-angle': 30, + 'text-font': ['DIN Offc Pro Regular', 'Arial Unicode MS Regular'], + 'symbol-placement': 'line', + 'text-padding': 1, + 'text-rotation-alignment': 'map', + 'text-pitch-alignment': 'viewport', + 'text-field': ['coalesce', ['get', 'name_en'], ['get', 'name']], + 'text-letter-spacing': 0.01, + }, + paint: { + 'text-color': [ + 'match', + ['get', 'class'], + 'ferry', + 'hsl(230, 48%, 44%)', + 'hsl(0, 0%, 0%)', + ], + 'text-halo-color': [ + 'match', + ['get', 'class'], + ['motorway', 'trunk'], + 'hsla(0, 0%, 100%, 0.75)', + 'hsl(0, 0%, 100%)', + ], + 'text-halo-width': 1, + 'text-halo-blur': 1, + }, + }, + { + id: 'road-local-label', + type: 'symbol', + source: 'composite', + 'source-layer': 'road', + metadata: {}, + minzoom: 10, + filter: [ + 'step', + ['zoom'], + ['match', ['get', 'class'], ['tertiary'], true, false], + 12, + ['match', ['get', 'class'], ['tertiary', 'street'], true, false], + ], + layout: { + 'text-size': [ + 'interpolate', + ['linear'], + ['zoom'], + 10, + [ + 'match', + ['get', 'class'], + ['tertiary'], + 10, + ['tertiary_link', 'street'], + 9, + 6.5, + ], + 18, + [ + 'match', + ['get', 'class'], + ['tertiary'], + 16, + ['tertiary_link', 'street'], + 14, + 13, + ], + ], + 'text-max-angle': 30, + 'text-font': ['DIN Offc Pro Regular', 'Arial Unicode MS Regular'], + 'symbol-placement': 'line', + 'text-padding': 1, + 'text-rotation-alignment': 'map', + 'text-pitch-alignment': 'viewport', + 'text-field': ['coalesce', ['get', 'name_en'], ['get', 'name']], + 'text-letter-spacing': 0.01, + }, + paint: { + 'text-color': [ + 'match', + ['get', 'class'], + 'ferry', + 'hsl(230, 48%, 44%)', + 'hsl(0, 0%, 0%)', + ], + 'text-halo-color': [ + 'match', + ['get', 'class'], + ['ferry'], + 'hsla(0, 0%, 100%, 0.75)', + 'hsl(0, 0%, 100%)', + ], + 'text-halo-width': 1, + 'text-halo-blur': 1, + }, + }, + { + id: 'road-sidewalk-label', + type: 'symbol', + source: 'composite', + 'source-layer': 'road', + metadata: {}, + minzoom: 10, + filter: [ + 'match', + ['get', 'class'], + ['pedestrian', 'street_limited'], + true, + false, + ], + layout: { + 'text-size': [ + 'interpolate', + ['linear'], + ['zoom'], + 10, + ['match', ['get', 'class'], ['pedestrian', 'street_limited'], 9, 6.5], + 18, + ['match', ['get', 'class'], ['pedestrian', 'street_limited'], 14, 13], + ], + 'text-max-angle': 30, + 'text-font': ['DIN Offc Pro Regular', 'Arial Unicode MS Regular'], + 'symbol-placement': 'line', + 'text-padding': 1, + 'text-rotation-alignment': 'map', + 'text-pitch-alignment': 'viewport', + 'text-field': ['coalesce', ['get', 'name_en'], ['get', 'name']], + 'text-letter-spacing': 0.01, + }, + paint: { + 'text-color': [ + 'match', + ['get', 'class'], + 'ferry', + 'hsl(230, 48%, 44%)', + 'hsl(0, 0%, 0%)', + ], + 'text-halo-color': [ + 'match', + ['get', 'class'], + ['motorway', 'trunk'], + 'hsla(0, 0%, 100%, 0.75)', + 'ferry', + 'hsl(196, 80%, 70%)', + 'hsl(0, 0%, 100%)', + ], + 'text-halo-width': 1, + 'text-halo-blur': 1, + }, + }, ]; From 3cedc7f730515a0423b2bcc5eb9b830569f9cb7a Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Wed, 2 Dec 2020 16:19:10 +0900 Subject: [PATCH 074/138] =?UTF-8?q?[fix]=20=EA=B8=B0=EC=A1=B4=EC=9D=98=20h?= =?UTF-8?q?ighway=EB=A5=BC=20arterial=EB=A1=9C=20=EB=B3=91=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - highway를 arterial로 병합 --- src/utils/rendering-data/featureTypeData.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/rendering-data/featureTypeData.ts b/src/utils/rendering-data/featureTypeData.ts index fb7a5d5..7e6a4f7 100644 --- a/src/utils/rendering-data/featureTypeData.ts +++ b/src/utils/rendering-data/featureTypeData.ts @@ -30,7 +30,6 @@ const data: DataType[] = [ typeKey: FeatureNameType.road, typeName: '도로', features: [ - { key: 'highway', name: '고속도로' }, { key: 'arterial', name: '주요도로' }, { key: 'local', name: '일반도로' }, { key: 'sidewalk', name: '인도' }, From f11b3419f638e151cf5de700ab6f3d87aaf270bd Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Wed, 2 Dec 2020 16:34:58 +0900 Subject: [PATCH 075/138] =?UTF-8?q?[feat]=20[#81]=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=EB=B3=84=20=EB=82=98=EB=88=84=EC=96=B4?= =?UTF-8?q?=EC=A7=84=20road-label=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 수정된 road-label layer에 대한 스타일 적용 --- src/utils/map-styling/road.ts | 129 +++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index aaf310a..c8ac3e4 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -6,7 +6,11 @@ import { ColorType, WeightType, } from '../applyStyle'; -import { StyleKeyType } from '../../store/common/type'; +import { + StyleKeyType, + ElementNameType, + SubElementNameType, +} from '../../store/common/type'; function roadStyling({ map, @@ -20,19 +24,28 @@ function roadStyling({ const arterialLayerNames = [ 'road-number-shield', 'road-exit-shield', - 'road-label', 'road-arterial', + 'road-arterial-label', + ]; + const localLayerNames = [ + 'ferry', + 'ferry-auto', + 'road-local', + 'road-local-label', ]; - const localLayerNames = ['ferry', 'ferry-auto', 'road-local']; - const sidewalkLayerNames = ['road-footway']; + const sidewalkLayerNames = ['road-footway', 'road-sidewalk-label']; if (subFeatureName === 'arterial') { - if ( - detailName === 'section' && - subDetailName === 'fill' && - key === 'visibility' - ) { - applyVisibility({ map, layerNames: arterialLayerNames, visibility }); + if (key === 'visibility') { + if ( + detailName === ElementNameType.section && + subDetailName === SubElementNameType.fill + ) + applyVisibility({ + map, + layerNames: localLayerNames, + visibility, + }); } else if (key === 'color' || key === 'saturation' || key === 'lightness') { if (detailName === 'section' && subDetailName === 'fill') { applyColor({ @@ -45,15 +58,22 @@ function roadStyling({ } else if (detailName === 'labelText' && subDetailName === 'fill') { applyColor({ map, - layerNames: ['road-number-shield', 'road-exit-shield', 'road-label'], + layerNames: [ + 'road-number-shield', + 'road-exit-shield', + 'road-arterial-label', + ], color, type: ColorType.text, [key]: style[key as StyleKeyType], }); - } else if (detailName === 'labelText' && subDetailName === 'stroke') { + } else if ( + detailName === 'labelText' && + subDetailName === SubElementNameType.stroke + ) { applyColor({ map, - layerNames: ['road-label'], + layerNames: ['road-arterial-label'], color, type: ColorType.textHalo, [key]: style[key as StyleKeyType], @@ -71,8 +91,8 @@ function roadStyling({ if (detailName === 'labelText' && subDetailName === 'stroke') applyWeight({ map, - layerNames: ['road-label'], - type: ColorType.textHalo, + layerNames: ['road-arterial-label'], + type: WeightType.textHalo, weight, }); } @@ -80,7 +100,7 @@ function roadStyling({ if (key === 'visibility') applyVisibility({ map, layerNames: localLayerNames, visibility }); else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === 'section' && subDetailName === 'fill') { + if (detailName === 'section' && subDetailName === 'fill') applyColor({ map, layerNames: ['ferry', 'ferry-auto', 'road-local'], @@ -88,6 +108,23 @@ function roadStyling({ type: ColorType.line, [key]: style[key as StyleKeyType], }); + else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) + applyColor({ + map, + layerNames: ['road-local-label'], + color, + type: ColorType.text, + [key]: style[key as StyleKeyType], + }); + else if (subDetailName === SubElementNameType.stroke) + applyColor({ + map, + layerNames: ['road-local-label'], + color, + type: ColorType.textHalo, + [key]: style[key as StyleKeyType], + }); } } else if (key === 'weight') { if (detailName === 'section' && subDetailName === 'fill') @@ -97,18 +134,20 @@ function roadStyling({ type: WeightType.line, weight, }); + else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.stroke) + applyWeight({ + map, + layerNames: ['road-local-label'], + type: WeightType.textHalo, + weight, + }); + } } } else if (subFeatureName === 'sidewalk') { - if (key === 'visibility') - applyVisibility({ map, layerNames: sidewalkLayerNames, visibility }); - else if (key === 'weight') { - if (detailName === 'section' && subDetailName === 'fill') - applyWeight({ - map, - layerNames: sidewalkLayerNames, - type: WeightType.line, - weight, - }); + if (key === 'visibility') { + if (detailName === ElementNameType.labelText) + applyVisibility({ map, layerNames: sidewalkLayerNames, visibility }); } else if (key === 'color' || key === 'saturation' || key === 'lightness') { if (detailName === 'section' && subDetailName === 'fill') applyColor({ @@ -118,6 +157,44 @@ function roadStyling({ type: ColorType.line, [key]: style[key as StyleKeyType], }); + else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) + applyColor({ + map, + layerNames: ['road-sidewalk-label'], + color, + type: ColorType.text, + [key]: style[key as StyleKeyType], + }); + else if (subDetailName === SubElementNameType.stroke) + applyColor({ + map, + layerNames: ['road-sidewalk-label'], + color, + type: ColorType.textHalo, + [key]: style[key as StyleKeyType], + }); + } + } else if (key === 'weight') { + if ( + detailName === ElementNameType.section && + subDetailName === SubElementNameType.fill + ) + applyWeight({ + map, + layerNames: sidewalkLayerNames, + type: WeightType.line, + weight, + }); + else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.stroke) + applyWeight({ + map, + layerNames: ['road-sidewalk-label'], + type: WeightType.textHalo, + weight, + }); + } } } } From 2fce5e84426c70178bd4e64bbeb977e2ee57fded Mon Sep 17 00:00:00 2001 From: gitgitwi Date: Wed, 2 Dec 2020 17:23:01 +0900 Subject: [PATCH 076/138] =?UTF-8?q?[FIX]=20[#86]=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=EC=97=90=20=EC=97=86=EB=8A=94=20=EC=86=8D=EC=84=B1=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95=20=EC=8B=9C=20=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 레이어에 없는 속성 조정 할 경우 mapbox 내부 에러로 아예 동작 멈추는 현상 발생 - layers array filtering으로 아예 해당하는 layera만 잘 넘겨주는 방식으로 변경하여 오류 수정 - 좀더 쉬운 layer filtering 위해 layer id 변경 및 지도 유형 별 array에서 속성별 array로 변경 --- src/store/map/layers/transit.ts | 15 ++-- src/utils/map-styling/transit.ts | 133 ++++++++++++------------------- 2 files changed, 59 insertions(+), 89 deletions(-) diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts index aa0fe80..9e6fde7 100644 --- a/src/store/map/layers/transit.ts +++ b/src/store/map/layers/transit.ts @@ -1,5 +1,6 @@ export default [ { + id: 'mapbox-airport-polygon', type: 'fill', source: 'composite', 'source-layer': 'landuse', @@ -10,10 +11,9 @@ export default [ 'fill-color': 'hsl(234, 20%, 30%)', }, filter: ['in', 'class', 'airport'], - id: 'transit-airport', }, { - id: 'mapbox-aeroway-polygon', + id: 'mapbox-airport-aeroway-polygon', type: 'fill', metadata: {}, source: 'composite', @@ -30,7 +30,7 @@ export default [ }, }, { - id: 'mapbox-aeroway-line', + id: 'mapbox-airport-aeroway-line', type: 'line', metadata: {}, source: 'composite', @@ -84,8 +84,8 @@ export default [ 'text-halo-width': 1, }, }, - { + id: 'transit-subway-line', type: 'line', source: 'line_source', 'source-layer': 'line', @@ -97,9 +97,9 @@ export default [ 'line-width': 2, }, filter: ['in', 'type', 'subway'], - id: 'transit-subway', }, { + id: 'transit-rail-line', type: 'line', source: 'line_source', 'source-layer': 'line', @@ -111,9 +111,9 @@ export default [ 'line-width': 2, }, filter: ['in', 'type', 'rail'], - id: 'transit-rail', }, { + id: 'mapbox-rail-road-line', type: 'line', source: 'composite', 'source-layer': 'road', @@ -132,9 +132,9 @@ export default [ ['match', ['get', 'class'], ['major_rail', 'minor_rail'], true, false], ['match', ['get', 'structure'], ['none', 'ford'], true, false], ], - id: 'mapbox-rail-road', }, { + id: 'transit-bus-label', type: 'symbol', source: 'poi_source', 'source-layer': 'poi', @@ -149,6 +149,5 @@ export default [ 'text-color': 'hsl(13, 68%, 63%)', }, filter: ['in', 'type', 'bus_stop'], - id: 'transit-bus-label', }, ]; diff --git a/src/utils/map-styling/transit.ts b/src/utils/map-styling/transit.ts index 6f84c12..4dae1a5 100644 --- a/src/utils/map-styling/transit.ts +++ b/src/utils/map-styling/transit.ts @@ -1,6 +1,8 @@ +/* eslint-disable no-debugger */ /* eslint-disable no-nested-ternary */ /* eslint-disable no-case-declarations */ import { stylingProps } from '.'; + import { StyleKeyType, ElementNameType, @@ -15,23 +17,17 @@ import { StyleTypes, } from '../../utils/applyStyle'; -const AirportLayerNames = [ - 'airport-label', - 'aeroway-polygon', - 'transit-airport', - 'aeroway-line', +const PolygonLayers = [ + 'mapbox-airport-aeroway-polygon', + 'mapbox-airport-polygon', ]; -const BusLayerNames = ['transit-bus-label']; -const RailLayerNames = ['transit-rail']; -const SubwayLayerNames = ['transit-subway', 'transit-subway-label']; - -const AllLayerTypeKeys = [ - ...AirportLayerNames, - ...BusLayerNames, - ...RailLayerNames, - ...SubwayLayerNames, - 'transit-label', +const LineLayers = [ + 'mapbox-airport-aeroway-line', + 'transit-subway-line', + 'transit-rail-line', + 'mapbox-rail-road-line', ]; +const LabelLayers = ['mapbox-airport-label', 'transit-bus-label']; function transitStyling({ map, @@ -41,71 +37,34 @@ function transitStyling({ subDetailName, style, }: stylingProps): void { - let layers: string[]; - switch (subFeatureName) { - case 'all': - layers = [...AllLayerTypeKeys]; - break; - case 'subway': - layers = [...SubwayLayerNames]; - break; - case 'bus': - layers = [...BusLayerNames]; - break; - case 'airport': - layers = [...AirportLayerNames]; - break; - case 'rail': - layers = [...RailLayerNames]; - break; - default: - return; - } + let layerNames: string[] = + subDetailName === SubElementNameType.fill + ? key === StyleKeyType.weight + ? [] + : detailName === ElementNameType.labelText + ? [...LabelLayers] + : [...PolygonLayers] + : detailName === ElementNameType.labelText + ? [...LabelLayers] + : [...LineLayers]; - let layersByDetailName: string[]; - let styleType: StyleTypes; + layerNames = + subFeatureName === 'all' + ? layerNames + : layerNames.filter((layer) => layer.includes(subFeatureName)); - switch (detailName) { - case ElementNameType.section: - layersByDetailName = layers.filter((layer) => !layer.includes('label')); - styleType = - subDetailName === SubElementNameType.stroke - ? key === StyleKeyType.weight - ? WeightType.line - : ColorType.line - : ColorType.fill; + if (layerNames.length === 0) return; - break; - case ElementNameType.labelText: - layersByDetailName = layers.filter((layer) => layer.includes('label')); - styleType = - subDetailName === SubElementNameType.stroke - ? key === StyleKeyType.weight - ? WeightType.textHalo - : ColorType.textHalo - : ColorType.text; - break; - case ElementNameType.labelIcon: - layersByDetailName = layers.filter((layer) => !layer.includes('label')); - styleType = - subDetailName === SubElementNameType.stroke - ? ColorType.line - : ColorType.fill; - break; - - default: - return; - } - - const styleKey: StyleKeyType = key as StyleKeyType; - const { [styleKey]: value } = style; + const styleKey = key as StyleKeyType; + const { [styleKey]: styleValue } = style; + let styleType: StyleTypes; switch (styleKey) { case StyleKeyType.visibility: applyVisibility({ map, - layerNames: layersByDetailName, - visibility: value as string, + layerNames, + visibility: styleValue as string, }); break; @@ -116,6 +75,15 @@ function transitStyling({ const satKey = StyleKeyType.saturation; const ligKey = StyleKeyType.lightness; + styleType = + detailName === ElementNameType.labelText + ? subDetailName === SubElementNameType.fill + ? ColorType.text + : ColorType.textHalo + : subDetailName === SubElementNameType.fill + ? ColorType.fill + : ColorType.line; + const satureOrLight = key === 'saturation' ? { saturation: +style[satKey] } @@ -124,7 +92,7 @@ function transitStyling({ : {}; applyColor({ map, - layerNames: layersByDetailName, + layerNames, type: styleType, color: style[colorKey], ...satureOrLight, @@ -132,20 +100,23 @@ function transitStyling({ break; case StyleKeyType.weight: - if (subDetailName !== 'fill') { - applyWeight({ - map, - layerNames: layersByDetailName, - type: styleType as WeightType, - weight: value as number, - }); - } + styleType = + detailName === ElementNameType.labelText + ? WeightType.textHalo + : WeightType.line; + applyWeight({ + map, + layerNames, + type: styleType, + weight: styleValue as number, + }); break; default: break; } + debugger; } export default transitStyling; From 96656d0ea5a2def3af24ca243823181609de1540 Mon Sep 17 00:00:00 2001 From: gitgitwi Date: Wed, 2 Dec 2020 17:48:28 +0900 Subject: [PATCH 077/138] =?UTF-8?q?[REFACTOR]=20[#86]=20debugger=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C,=20=EB=A0=88=EC=9D=B4=EC=96=B4=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20filter=20=EB=B0=A9=EC=8B=9D=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - debugger 코드 삭제 - layer data 가져오는 방식 `in..type` -> `==/match..get..` 으로 변경 --- src/store/map/layers/transit.ts | 8 ++++---- src/utils/map-styling/transit.ts | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts index 9e6fde7..93d66ee 100644 --- a/src/store/map/layers/transit.ts +++ b/src/store/map/layers/transit.ts @@ -10,7 +10,7 @@ export default [ paint: { 'fill-color': 'hsl(234, 20%, 30%)', }, - filter: ['in', 'class', 'airport'], + filter: ['==', ['get', 'class'], 'airport'], }, { id: 'mapbox-airport-aeroway-polygon', @@ -96,7 +96,7 @@ export default [ 'line-color': 'hsl(192, 70%, 43%)', 'line-width': 2, }, - filter: ['in', 'type', 'subway'], + filter: ['==', ['get', 'type'], 'subway'], }, { id: 'transit-rail-line', @@ -110,7 +110,7 @@ export default [ 'line-color': 'hsl(201, 100%, 14%)', 'line-width': 2, }, - filter: ['in', 'type', 'rail'], + filter: ['match', ['get', 'type'], 'rail'], }, { id: 'mapbox-rail-road-line', @@ -148,6 +148,6 @@ export default [ 'text-halo-width': 1, 'text-color': 'hsl(13, 68%, 63%)', }, - filter: ['in', 'type', 'bus_stop'], + filter: ['==', ['get', 'type'], 'bus_stop'], }, ]; diff --git a/src/utils/map-styling/transit.ts b/src/utils/map-styling/transit.ts index 4dae1a5..7051bd4 100644 --- a/src/utils/map-styling/transit.ts +++ b/src/utils/map-styling/transit.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-debugger */ /* eslint-disable no-nested-ternary */ /* eslint-disable no-case-declarations */ import { stylingProps } from '.'; @@ -116,7 +115,6 @@ function transitStyling({ default: break; } - debugger; } export default transitStyling; From d00f5521953a92a44adb1e36724f2fa9bb8356b6 Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Wed, 2 Dec 2020 17:50:36 +0900 Subject: [PATCH 078/138] =?UTF-8?q?[refactor]=20road=20styling=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존에 한 파일에서 작업하고 있던 road styling 작업 카테고리별로 분류 --- .../map-styling/road-categories/arterial.ts | 117 ++++++++++ .../map-styling/road-categories/local.ts | 112 +++++++++ .../map-styling/road-categories/sidewalk.ts | 112 +++++++++ src/utils/map-styling/road.ts | 212 +++--------------- 4 files changed, 368 insertions(+), 185 deletions(-) create mode 100644 src/utils/map-styling/road-categories/arterial.ts create mode 100644 src/utils/map-styling/road-categories/local.ts create mode 100644 src/utils/map-styling/road-categories/sidewalk.ts diff --git a/src/utils/map-styling/road-categories/arterial.ts b/src/utils/map-styling/road-categories/arterial.ts new file mode 100644 index 0000000..36ca1f9 --- /dev/null +++ b/src/utils/map-styling/road-categories/arterial.ts @@ -0,0 +1,117 @@ +import { stylingProps } from '../index'; +import { + StyleKeyType, + ElementNameType, + SubElementNameType, +} from '../../../store/common/type'; +import { + applyVisibility, + applyColor, + applyWeight, + ColorType, + WeightType, +} from '../../applyStyle'; + +export const arterialLayerNames = { + all: [ + 'road-arterial', + 'road-number-shield', + 'road-exit-shield', + 'road-arterial-label', + ], + polygon: [], + line: ['road-arterial'], + text: { + all: ['road-number-shield', 'road-exit-shield', 'road-arterial-label'], + hasStroke: ['road-arterial-label'], + noStroke: ['road-number-shield', 'road-exit-shield'], + }, + icon: [], +}; + +function arterialStyling({ + map, + subFeatureName, + detailName, + subDetailName, + key, + style, +}: stylingProps): void { + const { visibility, color, weight } = style; + + if (key === 'visibility') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) + applyVisibility({ + map, + layerNames: arterialLayerNames.line, + visibility, + }); + } else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) + applyVisibility({ + map, + layerNames: arterialLayerNames.text.noStroke, + visibility, + }); + else if (subDetailName === SubElementNameType.stroke) + applyVisibility({ + map, + layerNames: arterialLayerNames.text.hasStroke, + visibility, + }); + } + } else if (key === 'color' || key === 'saturation' || key === 'lightness') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyColor({ + map, + layerNames: arterialLayerNames.line, + color, + type: ColorType.line, + [key]: style[key as StyleKeyType], + }); + } + } else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) { + applyColor({ + map, + layerNames: arterialLayerNames.text.all, + color, + type: ColorType.text, + [key]: style[key as StyleKeyType], + }); + } else if (subDetailName === SubElementNameType.stroke) { + applyColor({ + map, + layerNames: arterialLayerNames.text.hasStroke, + color, + type: ColorType.textHalo, + [key]: style[key as StyleKeyType], + }); + } + } + } else if (key === 'weight') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyWeight({ + map, + layerNames: arterialLayerNames.line, + type: WeightType.line, + weight, + }); + } + } + if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.stroke) + applyWeight({ + map, + layerNames: arterialLayerNames.text.hasStroke, + type: WeightType.textHalo, + weight, + }); + } + } +} + +export default arterialStyling; diff --git a/src/utils/map-styling/road-categories/local.ts b/src/utils/map-styling/road-categories/local.ts new file mode 100644 index 0000000..c83aa15 --- /dev/null +++ b/src/utils/map-styling/road-categories/local.ts @@ -0,0 +1,112 @@ +import { stylingProps } from '../index'; +import { + StyleKeyType, + ElementNameType, + SubElementNameType, +} from '../../../store/common/type'; +import { + applyVisibility, + applyColor, + applyWeight, + ColorType, + WeightType, +} from '../../applyStyle'; + +const localLayerNames = { + all: ['ferry', 'ferry-auto', 'road-local', 'road-local-label'], + polygon: [], + line: ['ferry', 'ferry-auto', 'road-local'], + text: { + all: ['road-local-label'], + hasStroke: ['road-local-label'], + noStroke: [], + }, + icon: [], +}; + +function localStyling({ + map, + subFeatureName, + detailName, + subDetailName, + key, + style, +}: stylingProps): void { + const { visibility, color, weight } = style; + + if (key === 'visibility') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) + applyVisibility({ + map, + layerNames: localLayerNames.line, + visibility, + }); + } else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) + applyVisibility({ + map, + layerNames: localLayerNames.text.noStroke, + visibility, + }); + else if (subDetailName === SubElementNameType.stroke) + applyVisibility({ + map, + layerNames: localLayerNames.text.hasStroke, + visibility, + }); + } + } else if (key === 'color' || key === 'saturation' || key === 'lightness') { + if (detailName === 'section') { + if (subDetailName === 'fill') { + applyColor({ + map, + layerNames: localLayerNames.line, + color, + type: ColorType.line, + [key]: style[key as StyleKeyType], + }); + } + } else if (detailName === 'labelText') { + if (subDetailName === SubElementNameType.fill) { + applyColor({ + map, + layerNames: localLayerNames.text.all, + color, + type: ColorType.text, + [key]: style[key as StyleKeyType], + }); + } else if (subDetailName === SubElementNameType.stroke) { + applyColor({ + map, + layerNames: localLayerNames.text.hasStroke, + color, + type: ColorType.textHalo, + [key]: style[key as StyleKeyType], + }); + } + } + } else if (key === 'weight') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyWeight({ + map, + layerNames: localLayerNames.line, + type: WeightType.line, + weight, + }); + } + } + if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.stroke) + applyWeight({ + map, + layerNames: localLayerNames.text.hasStroke, + type: WeightType.textHalo, + weight, + }); + } + } +} + +export default localStyling; diff --git a/src/utils/map-styling/road-categories/sidewalk.ts b/src/utils/map-styling/road-categories/sidewalk.ts new file mode 100644 index 0000000..d3e5030 --- /dev/null +++ b/src/utils/map-styling/road-categories/sidewalk.ts @@ -0,0 +1,112 @@ +import { stylingProps } from '../index'; +import { + StyleKeyType, + ElementNameType, + SubElementNameType, +} from '../../../store/common/type'; +import { + applyVisibility, + applyColor, + applyWeight, + ColorType, + WeightType, +} from '../../applyStyle'; + +const sidewalkLayerNames = { + all: ['road-footway', 'road-sidewalk-label'], + polygon: [], + line: ['road-footway'], + text: { + all: ['road-sidewalk-label'], + hasStroke: ['road-sidewalk-label'], + noStroke: [], + }, + icon: [], +}; + +function sidewalkStyling({ + map, + subFeatureName, + detailName, + subDetailName, + key, + style, +}: stylingProps): void { + const { visibility, color, weight } = style; + + if (key === 'visibility') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) + applyVisibility({ + map, + layerNames: sidewalkLayerNames.line, + visibility, + }); + } else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) + applyVisibility({ + map, + layerNames: sidewalkLayerNames.text.noStroke, + visibility, + }); + else if (subDetailName === SubElementNameType.stroke) + applyVisibility({ + map, + layerNames: sidewalkLayerNames.text.hasStroke, + visibility, + }); + } + } else if (key === 'color' || key === 'saturation' || key === 'lightness') { + if (detailName === 'section') { + if (subDetailName === 'fill') { + applyColor({ + map, + layerNames: sidewalkLayerNames.line, + color, + type: ColorType.line, + [key]: style[key as StyleKeyType], + }); + } + } else if (detailName === 'labelText') { + if (subDetailName === SubElementNameType.fill) { + applyColor({ + map, + layerNames: sidewalkLayerNames.text.all, + color, + type: ColorType.text, + [key]: style[key as StyleKeyType], + }); + } else if (subDetailName === SubElementNameType.stroke) { + applyColor({ + map, + layerNames: sidewalkLayerNames.text.hasStroke, + color, + type: ColorType.textHalo, + [key]: style[key as StyleKeyType], + }); + } + } + } else if (key === 'weight') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyWeight({ + map, + layerNames: sidewalkLayerNames.line, + type: WeightType.line, + weight, + }); + } + } + if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.stroke) + applyWeight({ + map, + layerNames: sidewalkLayerNames.text.hasStroke, + type: WeightType.textHalo, + weight, + }); + } + } +} + +export default sidewalkStyling; diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index c8ac3e4..016678f 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -1,16 +1,7 @@ import { stylingProps } from '.'; -import { - applyVisibility, - applyColor, - applyWeight, - ColorType, - WeightType, -} from '../applyStyle'; -import { - StyleKeyType, - ElementNameType, - SubElementNameType, -} from '../../store/common/type'; +import arterialStyling from './road-categories/arterial'; +import localStyling from './road-categories/local'; +import sidewalkStyling from './road-categories/sidewalk'; function roadStyling({ map, @@ -20,182 +11,33 @@ function roadStyling({ key, style, }: stylingProps): void { - const { visibility, color, weight } = style; - const arterialLayerNames = [ - 'road-number-shield', - 'road-exit-shield', - 'road-arterial', - 'road-arterial-label', - ]; - const localLayerNames = [ - 'ferry', - 'ferry-auto', - 'road-local', - 'road-local-label', - ]; - const sidewalkLayerNames = ['road-footway', 'road-sidewalk-label']; - if (subFeatureName === 'arterial') { - if (key === 'visibility') { - if ( - detailName === ElementNameType.section && - subDetailName === SubElementNameType.fill - ) - applyVisibility({ - map, - layerNames: localLayerNames, - visibility, - }); - } else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === 'section' && subDetailName === 'fill') { - applyColor({ - map, - layerNames: ['road-arterial'], - color, - type: ColorType.line, - [key]: style[key as StyleKeyType], - }); - } else if (detailName === 'labelText' && subDetailName === 'fill') { - applyColor({ - map, - layerNames: [ - 'road-number-shield', - 'road-exit-shield', - 'road-arterial-label', - ], - color, - type: ColorType.text, - [key]: style[key as StyleKeyType], - }); - } else if ( - detailName === 'labelText' && - subDetailName === SubElementNameType.stroke - ) { - applyColor({ - map, - layerNames: ['road-arterial-label'], - color, - type: ColorType.textHalo, - [key]: style[key as StyleKeyType], - }); - } - } else if (key === 'weight') { - if (detailName === 'section' && subDetailName === 'fill') { - applyWeight({ - map, - layerNames: ['road-arterial'], - type: WeightType.line, - weight, - }); - } - if (detailName === 'labelText' && subDetailName === 'stroke') - applyWeight({ - map, - layerNames: ['road-arterial-label'], - type: WeightType.textHalo, - weight, - }); - } + arterialStyling({ + map, + subFeatureName, + detailName, + subDetailName, + key, + style, + }); } else if (subFeatureName === 'local') { - if (key === 'visibility') - applyVisibility({ map, layerNames: localLayerNames, visibility }); - else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === 'section' && subDetailName === 'fill') - applyColor({ - map, - layerNames: ['ferry', 'ferry-auto', 'road-local'], - color, - type: ColorType.line, - [key]: style[key as StyleKeyType], - }); - else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.fill) - applyColor({ - map, - layerNames: ['road-local-label'], - color, - type: ColorType.text, - [key]: style[key as StyleKeyType], - }); - else if (subDetailName === SubElementNameType.stroke) - applyColor({ - map, - layerNames: ['road-local-label'], - color, - type: ColorType.textHalo, - [key]: style[key as StyleKeyType], - }); - } - } else if (key === 'weight') { - if (detailName === 'section' && subDetailName === 'fill') - applyWeight({ - map, - layerNames: ['ferry', 'ferry-auto', 'road-local'], - type: WeightType.line, - weight, - }); - else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.stroke) - applyWeight({ - map, - layerNames: ['road-local-label'], - type: WeightType.textHalo, - weight, - }); - } - } + localStyling({ + map, + subFeatureName, + detailName, + subDetailName, + key, + style, + }); } else if (subFeatureName === 'sidewalk') { - if (key === 'visibility') { - if (detailName === ElementNameType.labelText) - applyVisibility({ map, layerNames: sidewalkLayerNames, visibility }); - } else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === 'section' && subDetailName === 'fill') - applyColor({ - map, - layerNames: sidewalkLayerNames, - color, - type: ColorType.line, - [key]: style[key as StyleKeyType], - }); - else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.fill) - applyColor({ - map, - layerNames: ['road-sidewalk-label'], - color, - type: ColorType.text, - [key]: style[key as StyleKeyType], - }); - else if (subDetailName === SubElementNameType.stroke) - applyColor({ - map, - layerNames: ['road-sidewalk-label'], - color, - type: ColorType.textHalo, - [key]: style[key as StyleKeyType], - }); - } - } else if (key === 'weight') { - if ( - detailName === ElementNameType.section && - subDetailName === SubElementNameType.fill - ) - applyWeight({ - map, - layerNames: sidewalkLayerNames, - type: WeightType.line, - weight, - }); - else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.stroke) - applyWeight({ - map, - layerNames: ['road-sidewalk-label'], - type: WeightType.textHalo, - weight, - }); - } - } + sidewalkStyling({ + map, + subFeatureName, + detailName, + subDetailName, + key, + style, + }); } } From 7a8b6c90d8c521fcab97c4e27fda6c34e03842ab Mon Sep 17 00:00:00 2001 From: gitgitwi Date: Wed, 2 Dec 2020 17:57:23 +0900 Subject: [PATCH 079/138] =?UTF-8?q?[FIX]=20[#86]=20=EB=AF=B8=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=83=81=ED=83=9C=EC=9D=B8=20labelIcon=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=EB=A7=81=20early=20return?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/map-styling/transit.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/map-styling/transit.ts b/src/utils/map-styling/transit.ts index 7051bd4..c0e9cd9 100644 --- a/src/utils/map-styling/transit.ts +++ b/src/utils/map-styling/transit.ts @@ -36,6 +36,9 @@ function transitStyling({ subDetailName, style, }: stylingProps): void { + // TODO: labelIcon 관련 구현 + if (detailName === ElementNameType.labelIcon) return; + let layerNames: string[] = subDetailName === SubElementNameType.fill ? key === StyleKeyType.weight From 0c3ca513ca8e789da42ba044aab799eaa1ab72a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Wed, 2 Dec 2020 13:34:54 +0900 Subject: [PATCH 080/138] =?UTF-8?q?[feat]=20=EB=A0=88=EC=9D=B4=EC=96=B4=20?= =?UTF-8?q?=EB=B3=84=EB=A1=9C=20=EC=83=89=EC=83=81=20=EA=B0=92=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/rendering-data/layerColor3.ts | 38 +++++++++++++++++ src/utils/rendering-data/layersColor.ts | 52 +++++++++++++++++++++--- src/utils/rendering-data/layersColor2.ts | 40 +++++++++++++++++- 3 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 src/utils/rendering-data/layerColor3.ts diff --git a/src/utils/rendering-data/layerColor3.ts b/src/utils/rendering-data/layerColor3.ts new file mode 100644 index 0000000..556eda7 --- /dev/null +++ b/src/utils/rendering-data/layerColor3.ts @@ -0,0 +1,38 @@ +export default { + 'poi-attraction': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-arts-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-landmark-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-business': { color: 'hsl(22, 55%, 55%)', type: 'text' }, + 'poi-food-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, + 'poi-store-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, + 'poi-government': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-public-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-general-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-medical': { color: 'hsl(340, 39%, 42%)', type: 'text' }, + 'poi-medical-label': { color: 'hsl(340, 39%, 42%)', type: 'text' }, + 'poi-park': { color: 'hsl(100, 45%, 37%)', type: 'text' }, + 'poi-park-label': { color: 'hsl(100, 45%, 37%)', type: 'text' }, + 'poi-worship': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-religion-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-school': { color: 'hsl(51, 40%, 40%)', type: 'text' }, + 'poi-education-label': { color: 'hsl(51, 40%, 40%)', type: 'text' }, + 'poi-sport-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-etc': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-industrial-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-historic-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-building-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'admin-0-boundary': { color: 'hsl(230, 8%, 51%)', type: 'line' }, + 'admin-0-boundary-bg': { color: 'hsl(230, 8%, 51%)', type: 'line' }, + 'admin-0-boundary-disputed': { color: 'hsl(230, 8%, 51%)', type: 'line' }, + 'country-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + 'admin-1-boundary-bg': { color: 'hsl(230, 14%, 77%)', type: 'line' }, + 'admin-1-boundary': { color: 'hsl(230, 14%, 77%)', type: 'line' }, + 'state-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + 'settlement-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + 'settlement-subdivision-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + water: { color: 'hsl(205, 87%, 76%)', type: 'fill' }, + 'water-polygon': { color: 'hsl(205, 87%, 76%)', type: 'fill' }, + 'water-line-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'water-point-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'waterway-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, +}; diff --git a/src/utils/rendering-data/layersColor.ts b/src/utils/rendering-data/layersColor.ts index e364c19..85a29ec 100644 --- a/src/utils/rendering-data/layersColor.ts +++ b/src/utils/rendering-data/layersColor.ts @@ -1,3 +1,4 @@ +// 어차피 레이어 하나 바꾸면 다 바뀔 건데 체크박스로 해둘 필요가 있을까?? export default { poi: { landmark: { @@ -36,14 +37,53 @@ export default { }, etc: { 'poi-etc': 'hsl(26, 25%, 32%)', - 'poi-industrial': 'hsl(26, 25%, 32%)', + 'poi-industrial-label': 'hsl(26, 25%, 32%)', 'poi-historic-label': 'hsl(26, 25%, 32%)', 'poi-building-label': 'hsl(26, 25%, 32%)', }, }, - road: {}, - administrative: {}, - landscape: {}, - transit: {}, - water: {}, + road: { + highway: {}, + arterial: {}, + local: {}, + sidewalk: {}, + 'bicycle-road': {}, + }, + administrative: { + country: { + 'admin-0-boundary': 'hsl(230, 8%, 51%)', + 'admin-0-boundary-bg': 'hsl(35, 12%, 89%)', + 'admin-0-boundary-disputed': 'hsl(230, 8%, 51%)', + 'country-label': 'hsl(0, 0%, 0%)', + }, + state: { + 'admin-1-boundary-bg': 'hsl(35, 12%, 89%)', + 'admin-1-boundary': 'hsl(230, 14%, 77%)', + 'state-label': 'hsl(0, 0%, 0%)', + }, + locality: { + 'settlement-label': 'hsl(0, 0%, 0%)', + 'settlement-subdivision-label': 'hsl(230, 29%, 35%)', + }, + }, + landscape: { + 'human-made': {}, + building: {}, + natural: {}, + landcover: {}, + mountain: {}, + }, + transit: { + airport: {}, + bus: {}, + rail: {}, + subway: {}, + }, + water: { + water: 'hsl(205, 87%, 76%)', + 'water-polygon': 'hsl(205, 87%, 76%)', + 'water-line-label': 'hsl(230, 48%, 44%)', + 'water-point-label': 'hsl(230, 48%, 44%)', + 'waterway-label': 'hsl(230, 48%, 44%)', + }, }; diff --git a/src/utils/rendering-data/layersColor2.ts b/src/utils/rendering-data/layersColor2.ts index 3cd4fee..5014988 100644 --- a/src/utils/rendering-data/layersColor2.ts +++ b/src/utils/rendering-data/layersColor2.ts @@ -65,8 +65,44 @@ export default { }, }, road: {}, - administrative: {}, + administrative: { + country: { + section: { + fill: 'transparent', + stroke: 'hsl(230, 8%, 51%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + state: { + section: { + fill: 'transparent', + stroke: 'hsl(230, 14%, 77%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + locality: { + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + }, landscape: {}, transit: {}, - water: {}, + water: { + section: { + fill: 'hsl(205, 87%, 76%)', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(230, 48%, 44%)', + stroke: 'transparent', + }, + }, }; From 53ebeaa76af835b86e138f31ef7bbafb65ee8f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Wed, 2 Dec 2020 13:36:00 +0900 Subject: [PATCH 081/138] =?UTF-8?q?[feat]=20range=EB=A5=BC=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=88=ED=95=98=EB=8A=94=20=EA=B2=83=EC=9D=84=20canvas?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=9C=EC=9E=91=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - range를 조절하는 것을 현재 canvas로 제작중이다 --- .../Sidebar/SidebarContentMore/AAA.tsx | 100 ++++++++++++++++++ .../SidebarContentMore/SaturationStyle.tsx | 2 + 2 files changed, 102 insertions(+) create mode 100644 src/components/Sidebar/SidebarContentMore/AAA.tsx diff --git a/src/components/Sidebar/SidebarContentMore/AAA.tsx b/src/components/Sidebar/SidebarContentMore/AAA.tsx new file mode 100644 index 0000000..ff89423 --- /dev/null +++ b/src/components/Sidebar/SidebarContentMore/AAA.tsx @@ -0,0 +1,100 @@ +import React, { useRef, useEffect, useState } from 'react'; +import styled from '../../../utils/styles/styled'; + +const Canvas = styled.canvas` + position: relative; + + background-color: #eee; +`; + +interface DrawRangeToCanvasProps { + x?: number; + y?: number; +} + +interface AAAProps { + value: string | number; + onStyleChange: (key: string, value: string | number) => void; +} + +function AAA({ value, onStyleChange }: AAAProps): React.ReactElement { + const canvasRef = useRef(document.createElement('canvas')); + const [foreRange, setForeRange] = useState(0); + const [backRange, setBackRange] = useState(0); + const [down, setDown] = useState(false); + + function getCursorPosition(event: React.MouseEvent) { + const rect = canvasRef.current.getBoundingClientRect(); + const x = event.clientX - rect.left; + return x; + } + + const mouseDownHandler = (e: React.MouseEvent) => { + const x = getCursorPosition(e); + setDown(true); + drawRangeToCanvas({ x }); + }; + + const mouseMoveHandler = (e: React.MouseEvent) => { + if (!down) return; + + const x = getCursorPosition(e); + drawRangeToCanvas({ x }); + }; + + const mouseUpHandler = (e: React.MouseEvent) => { + setDown(false); + const { width } = canvasRef.current; + const x = getCursorPosition(e); + if (x < width / 2) { + const a = Number(value) - (width / 2 - x) * foreRange; + console.log((width / 2 - x) * foreRange, width / 2 - x, foreRange); + onStyleChange('saturation', a); + } else { + const b = Number(value) + (x - width / 2) * backRange; + onStyleChange('saturation', b); + } + }; + + const drawRangeToCanvas = ({ x }: DrawRangeToCanvasProps) => { + const ctx = canvasRef.current.getContext('2d'); + const { width, height } = canvasRef.current; + + if (!ctx) return; + ctx?.clearRect(0, 0, width, height); + + ctx.fillStyle = 'darkgray'; + ctx.beginPath(); + ctx.rect(0, height / 2, width * 2, 2); + ctx.fill(); + + ctx.fillStyle = '#3ECF5C'; + ctx.beginPath(); + ctx.arc(x || width / 2, height / 2, 10, 0, Math.PI * 2); + ctx.fill(); + }; + + const setRange = () => { + const { width } = canvasRef.current; + setForeRange(width / 2 / (100 - Number(value))); + setBackRange(width / 2 / Number(value)); + }; + + useEffect(() => { + setRange(); + drawRangeToCanvas({}); + }, []); + + return ( + + ); +} + +export default AAA; diff --git a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx index 605a4cf..961ee3c 100644 --- a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx @@ -3,6 +3,7 @@ import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; import useInputRange from '../../../hooks/common/useInputRange'; import { StyleKeyType } from '../../../store/common/type'; +import AAA from './AAA'; const SaturationWrapper = styled.div` display: flex; @@ -49,6 +50,7 @@ function SaturationStyle({ onChange={(e) => rangeChangeHandler(e)} onMouseUp={() => rangeMouseUpHandler(StyleKeyType.saturation)} /> + {/* */} ); } From f9f6c0ef2fb02707da5794ffdff498eeca79d94c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Wed, 2 Dec 2020 19:52:32 +0900 Subject: [PATCH 082/138] =?UTF-8?q?[fix]=20range=EB=A5=BC=20=EC=A0=88?= =?UTF-8?q?=EB=8C=93=EA=B0=92=EC=9C=BC=EB=A1=9C=20=ED=95=98=EA=B8=B0?= =?UTF-8?q?=EB=A1=9C=20=ED=95=B4=EC=84=9C=20range=20canvas=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - range의 변화를 절댓값으로 주기로 해서 range canvas를 없애주었다 --- .../Sidebar/SidebarContentMore/AAA.tsx | 100 ------------------ .../SidebarContentMore/SaturationStyle.tsx | 2 - 2 files changed, 102 deletions(-) delete mode 100644 src/components/Sidebar/SidebarContentMore/AAA.tsx diff --git a/src/components/Sidebar/SidebarContentMore/AAA.tsx b/src/components/Sidebar/SidebarContentMore/AAA.tsx deleted file mode 100644 index ff89423..0000000 --- a/src/components/Sidebar/SidebarContentMore/AAA.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import React, { useRef, useEffect, useState } from 'react'; -import styled from '../../../utils/styles/styled'; - -const Canvas = styled.canvas` - position: relative; - - background-color: #eee; -`; - -interface DrawRangeToCanvasProps { - x?: number; - y?: number; -} - -interface AAAProps { - value: string | number; - onStyleChange: (key: string, value: string | number) => void; -} - -function AAA({ value, onStyleChange }: AAAProps): React.ReactElement { - const canvasRef = useRef(document.createElement('canvas')); - const [foreRange, setForeRange] = useState(0); - const [backRange, setBackRange] = useState(0); - const [down, setDown] = useState(false); - - function getCursorPosition(event: React.MouseEvent) { - const rect = canvasRef.current.getBoundingClientRect(); - const x = event.clientX - rect.left; - return x; - } - - const mouseDownHandler = (e: React.MouseEvent) => { - const x = getCursorPosition(e); - setDown(true); - drawRangeToCanvas({ x }); - }; - - const mouseMoveHandler = (e: React.MouseEvent) => { - if (!down) return; - - const x = getCursorPosition(e); - drawRangeToCanvas({ x }); - }; - - const mouseUpHandler = (e: React.MouseEvent) => { - setDown(false); - const { width } = canvasRef.current; - const x = getCursorPosition(e); - if (x < width / 2) { - const a = Number(value) - (width / 2 - x) * foreRange; - console.log((width / 2 - x) * foreRange, width / 2 - x, foreRange); - onStyleChange('saturation', a); - } else { - const b = Number(value) + (x - width / 2) * backRange; - onStyleChange('saturation', b); - } - }; - - const drawRangeToCanvas = ({ x }: DrawRangeToCanvasProps) => { - const ctx = canvasRef.current.getContext('2d'); - const { width, height } = canvasRef.current; - - if (!ctx) return; - ctx?.clearRect(0, 0, width, height); - - ctx.fillStyle = 'darkgray'; - ctx.beginPath(); - ctx.rect(0, height / 2, width * 2, 2); - ctx.fill(); - - ctx.fillStyle = '#3ECF5C'; - ctx.beginPath(); - ctx.arc(x || width / 2, height / 2, 10, 0, Math.PI * 2); - ctx.fill(); - }; - - const setRange = () => { - const { width } = canvasRef.current; - setForeRange(width / 2 / (100 - Number(value))); - setBackRange(width / 2 / Number(value)); - }; - - useEffect(() => { - setRange(); - drawRangeToCanvas({}); - }, []); - - return ( - - ); -} - -export default AAA; diff --git a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx index 961ee3c..605a4cf 100644 --- a/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx +++ b/src/components/Sidebar/SidebarContentMore/SaturationStyle.tsx @@ -3,7 +3,6 @@ import styled from '../../../utils/styles/styled'; import { Range } from '../SidebarContentFewer/DepthItem'; import useInputRange from '../../../hooks/common/useInputRange'; import { StyleKeyType } from '../../../store/common/type'; -import AAA from './AAA'; const SaturationWrapper = styled.div` display: flex; @@ -50,7 +49,6 @@ function SaturationStyle({ onChange={(e) => rangeChangeHandler(e)} onMouseUp={() => rangeMouseUpHandler(StyleKeyType.saturation)} /> - {/* */} ); } From 7ed5926f61b45f59789b309ba9a8040dc69e04a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Wed, 2 Dec 2020 19:53:21 +0900 Subject: [PATCH 083/138] =?UTF-8?q?[refactor]=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=EC=97=90=20=EA=B0=92=EC=9C=BC=EB=A1=9C=20defaultColor=EB=A5=BC?= =?UTF-8?q?=20=EC=A3=BC=EA=B8=B0=20=EC=9C=84=ED=95=B4=EC=84=9C=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9D=84=20=EC=A7=80=EC=A0=95=EC=A4=91=EC=9D=B4?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 초기에 값으로 defaultColor를 주기로 결정 - 타입에러가 많이나서 공유 후 개별적으로 해결해볼 예정 --- src/store/common/type.ts | 51 ++++++- src/store/style/getReducer.ts | 17 ++- src/store/style/properties.ts | 112 +++++++++++++-- src/utils/rendering-data/layersColor2.ts | 168 ++++++++++++++--------- 4 files changed, 263 insertions(+), 85 deletions(-) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 9ba1caa..d19e689 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,5 +1,27 @@ import { init, setStyle } from '../style/action'; +export interface nice { + poi: + | 'all' + | 'landmark' + | 'business' + | 'government' + | 'medical' + | 'park' + | 'worship' + | 'school' + | 'sports' + | 'etc'; + road: 'all'; + administrative: 'all' | 'country' | 'state' | 'locality'; + landscape: 'all'; + transit: 'all'; + water: 'all'; + marker: 'all'; +} + +export type hello = 'landmark'; + export enum ElementNameType { section = 'section', labelText = 'labelText', @@ -43,15 +65,15 @@ export interface StyleType { saturation: number; lightness: number; } -export interface ElementType { +export interface SubElementType { fill: StyleType; stroke: StyleType; } export interface FeatureType { isChanged: boolean; - section: ElementType; - labelText: ElementType; - labelIcon: StyleType; + section: SubElementType | null; + labelText: SubElementType | null; + labelIcon: StyleType | null; } export interface FeatureState { [name: string]: FeatureType; @@ -61,8 +83,27 @@ export type ActionType = ReturnType | ReturnType; export type ActionPayload = { feature: FeatureNameType; - subFeature?: string; + subFeature: string; element: ElementNameType; subElement?: SubElementNameType; style: StyleType; }; + +export interface HasPropertiesType { + featureType: FeatureNameType; + subFeatureType: string; +} + +export interface PropertyType { + [subFeatureType: string]: { + [ElementNameType.section]?: { + fill: string; + stroke: string; + } | null; + [ElementNameType.labelText]?: { + fill: string; + stroke: string; + } | null; + [ElementNameType.labelIcon]?: string | null; + }; +} diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts index d32f05c..f56d689 100644 --- a/src/store/style/getReducer.ts +++ b/src/store/style/getReducer.ts @@ -7,6 +7,7 @@ import { SubElementNameType, objType, ElementNameType, + SubElementType, } from '../common/type'; import { getDefaultFeature } from './properties'; import { INIT, SET } from './action'; @@ -23,7 +24,10 @@ export default function getReducer(IDX: number): ReducerType { ]; const initialState = subFeatures.reduce((acc: FeatureState, cur: string) => { - acc[cur] = getDefaultFeature(); + acc[cur] = getDefaultFeature({ + featureType: renderingData[IDX].typeKey, + subFeatureType: cur, + }); return acc; }, {}); @@ -51,12 +55,15 @@ export default function getReducer(IDX: number): ReducerType { let prevIsChanged; if (element === ElementNameType.labelIcon) { - prevIsChanged = newFeature[element].isChanged; + prevIsChanged = newFeature[element]?.isChanged; newFeature[element] = style; } else { - prevIsChanged = - newFeature[element][subElement as SubElementNameType].isChanged; - newFeature[element][subElement as SubElementNameType] = style; + prevIsChanged = (newFeature[element] as SubElementType)[ + subElement as SubElementNameType + ].isChanged; + (newFeature[element] as SubElementType)[ + subElement as SubElementNameType + ] = style; } if (prevIsChanged !== style.isChanged) { diff --git a/src/store/style/properties.ts b/src/store/style/properties.ts index 83b0051..d7e2e87 100644 --- a/src/store/style/properties.ts +++ b/src/store/style/properties.ts @@ -1,4 +1,46 @@ -import { StyleType, ElementType, FeatureType } from '../common/type'; +// import { StyleType, ElementType, FeatureType } from '../common/type'; + +// const style: StyleType = { +// isChanged: false, +// visibility: 'inherit', +// color: '#55bf40', +// weight: 0, +// saturation: 0, +// lightness: 0, +// }; + +// export const getDefaultStyle = (): StyleType => { +// return JSON.parse(JSON.stringify(style)); +// }; + +// const getDefaultElement = (): ElementType => { +// return { +// fill: getDefaultStyle(), +// stroke: getDefaultStyle(), +// }; +// }; + +// export const getDefaultFeature = (): FeatureType => { +// return { +// isChanged: false, +// section: getDefaultElement(), +// labelText: getDefaultElement(), +// labelIcon: getDefaultStyle(), +// }; +// }; + +import { + StyleType, + ElementNameType, + FeatureType, + FeatureNameType, + HasPropertiesType, + SubElementNameType, + PropertyType, + nice, +} from '../common/type'; + +import defaultStyle from '../../utils/rendering-data/layersColor2'; const style: StyleType = { isChanged: false, @@ -9,22 +51,70 @@ const style: StyleType = { lightness: 0, }; -export const getDefaultStyle = (): StyleType => { - return JSON.parse(JSON.stringify(style)); -}; +interface getDefaultStyleProps { + featureType: FeatureNameType; + subFeatureType: T; + elementType: ElementNameType; + subElementType: SubElementNameType; +} -const getDefaultElement = (): ElementType => { +export const getDefaultStyle = ({ + featureType, + subFeatureType, + elementType, + subElementType, +}: getDefaultStyleProps): StyleType => { return { - fill: getDefaultStyle(), - stroke: getDefaultStyle(), + ...JSON.parse(JSON.stringify(style)), + color: defaultStyle[featureType][subFeatureType], + // defaultStyle[featureType][subFeatureType][elementType][subElementType], }; }; -export const getDefaultFeature = (): FeatureType => { +export const getDefaultFeature = ({ + featureType, + subFeatureType, +}: HasPropertiesType): FeatureType => { return { isChanged: false, - section: getDefaultElement(), - labelText: getDefaultElement(), - labelIcon: getDefaultStyle(), + section: defaultStyle[featureType][subFeatureType][ElementNameType.section] + ? { + [SubElementNameType.fill]: getDefaultStyle({ + featureType, + subFeatureType, + elementType: ElementNameType.section, + subElementType: SubElementNameType.fill, + }), + [SubElementNameType.stroke]: getDefaultStyle({ + featureType, + subFeatureType, + elementType: ElementNameType.section, + subElementType: SubElementNameType.stroke, + }), + } + : null, + labelText: defaultStyle[featureType][subFeatureType][ + ElementNameType.labelText + ] + ? { + [SubElementNameType.fill]: getDefaultStyle({ + featureType, + subFeatureType, + elementType: ElementNameType.labelText, + subElementType: SubElementNameType.fill, + }), + [SubElementNameType.stroke]: getDefaultStyle({ + featureType, + subFeatureType, + elementType: ElementNameType.labelText, + subElementType: SubElementNameType.stroke, + }), + } + : null, + labelIcon: defaultStyle[featureType][subFeatureType][ + ElementNameType.labelIcon + ] + ? getDefaultStyle() + : null, }; }; diff --git a/src/utils/rendering-data/layersColor2.ts b/src/utils/rendering-data/layersColor2.ts index 5014988..ea12695 100644 --- a/src/utils/rendering-data/layersColor2.ts +++ b/src/utils/rendering-data/layersColor2.ts @@ -1,108 +1,148 @@ +import { + SubElementNameType, + ElementNameType, + FeatureNameType, + PropertyType, +} from '../../store/common/type'; + export default { - poi: { + [FeatureNameType.poi]: { + all: {}, landmark: { - labelText: { - fill: 'hsl(26, 25%, 32%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, - labelIcon: 'transparent', + [ElementNameType.labelIcon]: 'transparent', }, business: { - labelText: { - fill: 'hsl(22, 55%, 55%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(22, 55%, 55%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, - labelIcon: 'transparent', + [ElementNameType.labelIcon]: 'transparent', }, government: { - labelText: { - fill: 'hsl(26, 25%, 32%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, - labelIcon: 'transparent', + [ElementNameType.labelIcon]: 'transparent', }, medical: { - labelText: { - fill: 'hsl(340, 39%, 42%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(340, 39%, 42%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, - labelIcon: 'transparent', + [ElementNameType.labelIcon]: 'transparent', }, park: { - labelText: { - fill: 'hsl(100, 45%, 37%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(100, 45%, 37%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, - labelIcon: 'transparent', + [ElementNameType.labelIcon]: 'transparent', }, worship: { - labelText: { - fill: 'hsl(26, 25%, 32%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, - labelIcon: 'transparent', + [ElementNameType.labelIcon]: 'transparent', }, school: { - labelText: { - fill: 'hsl(51, 40%, 40%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(51, 40%, 40%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, - labelIcon: 'transparent', + [ElementNameType.labelIcon]: 'transparent', }, sports: { - labelText: { - fill: 'hsl(26, 25%, 32%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, - labelIcon: 'transparent', + [ElementNameType.labelIcon]: 'transparent', }, etc: { - labelText: { - fill: 'hsl(26, 25%, 32%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, - labelIcon: 'transparent', + [ElementNameType.labelIcon]: 'transparent', }, }, - road: {}, - administrative: { + [FeatureNameType.road]: { + all: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + }, + }, + [FeatureNameType.administrative]: { + all: {}, country: { - section: { - fill: 'transparent', - stroke: 'hsl(230, 8%, 51%)', + [ElementNameType.section]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'hsl(230, 8%, 51%)', }, - labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(0, 0%, 0%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, }, state: { - section: { - fill: 'transparent', - stroke: 'hsl(230, 14%, 77%)', + [ElementNameType.section]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'hsl(230, 14%, 77%)', }, - labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(0, 0%, 0%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', }, }, locality: { - labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(0, 0%, 0%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', + }, + }, + }, + [FeatureNameType.landscape]: { + all: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + }, + }, + [FeatureNameType.transit]: { + all: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', }, }, }, - landscape: {}, - transit: {}, - water: { - section: { - fill: 'hsl(205, 87%, 76%)', - stroke: 'transparent', - }, - labelText: { - fill: 'hsl(230, 48%, 44%)', - stroke: 'transparent', + [FeatureNameType.water]: { + all: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(205, 87%, 76%)', + [SubElementNameType.stroke]: 'transparent', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(230, 48%, 44%)', + [SubElementNameType.stroke]: 'transparent', + }, + }, + }, + [FeatureNameType.marker]: { + all: { + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, }, }, }; From 347c72d8d5095c80f89ab162fc219a251ea3f6bb Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Wed, 2 Dec 2020 22:29:32 +0900 Subject: [PATCH 084/138] =?UTF-8?q?[fix]=20road-polygon=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=EB=B3=84=20=EB=82=98=EB=88=84?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 mapbox의 road-polygon layer 카테고리별 나누기 - 기존 local로 분류하였던 tertiary 관련 class, arterial로 변경 --- src/store/map/initializeMap.ts | 1 + src/store/map/layers/road.ts | 117 ++++++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 31 deletions(-) diff --git a/src/store/map/initializeMap.ts b/src/store/map/initializeMap.ts index 108d186..265a122 100644 --- a/src/store/map/initializeMap.ts +++ b/src/store/map/initializeMap.ts @@ -68,6 +68,7 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { translate(map); map.removeLayer('poi-label'); map.removeLayer('road-label'); + map.removeLayer('road-polygon'); map.addSource(Sources.polygon, { type: 'vector', diff --git a/src/store/map/layers/road.ts b/src/store/map/layers/road.ts index 805aaa0..bc4d19a 100644 --- a/src/store/map/layers/road.ts +++ b/src/store/map/layers/road.ts @@ -79,7 +79,7 @@ export default [ [ 'match', ['get', 'class'], - ['motorway', 'trunk', 'primary', 'secondary'], + ['motorway', 'trunk', 'primary', 'secondary', 'tertiary'], true, false, ], @@ -87,7 +87,7 @@ export default [ [ 'match', ['get', 'class'], - ['motorway', 'trunk', 'primary', 'secondary'], + ['motorway', 'trunk', 'primary', 'secondary', 'tertiary'], true, false, ], @@ -101,9 +101,15 @@ export default [ [ 'match', ['get', 'class'], - ['motorway', 'trunk', 'primary', 'secondary'], + ['motorway', 'trunk', 'primary', 'secondary', 'tertiary'], 10, - ['motorway_link', 'trunk_link', 'primary_link', 'secondary_link'], + [ + 'motorway_link', + 'trunk_link', + 'primary_link', + 'secondary_link', + 'tertiary_link', + ], 9, 6.5, ], @@ -111,9 +117,15 @@ export default [ [ 'match', ['get', 'class'], - ['motorway', 'trunk', 'primary', 'secondary'], + ['motorway', 'trunk', 'primary', 'secondary', 'tertiary'], 16, - ['motorway_link', 'trunk_link', 'primary_link', 'secondary_link'], + [ + 'motorway_link', + 'trunk_link', + 'primary_link', + 'secondary_link', + 'tertiary_link', + ], 14, 13, ], @@ -153,38 +165,16 @@ export default [ 'source-layer': 'road', metadata: {}, minzoom: 10, - filter: [ - 'step', - ['zoom'], - ['match', ['get', 'class'], ['tertiary'], true, false], - 12, - ['match', ['get', 'class'], ['tertiary', 'street'], true, false], - ], + filter: ['match', ['get', 'class'], ['street'], true, false], layout: { 'text-size': [ 'interpolate', ['linear'], ['zoom'], 10, - [ - 'match', - ['get', 'class'], - ['tertiary'], - 10, - ['tertiary_link', 'street'], - 9, - 6.5, - ], + ['match', ['get', 'class'], ['street'], 9, 6.5], 18, - [ - 'match', - ['get', 'class'], - ['tertiary'], - 16, - ['tertiary_link', 'street'], - 14, - 13, - ], + ['match', ['get', 'class'], ['street'], 14, 13], ], 'text-max-angle': 30, 'text-font': ['DIN Offc Pro Regular', 'Arial Unicode MS Regular'], @@ -268,4 +258,69 @@ export default [ 'text-halo-blur': 1, }, }, + { + id: 'road-arterial-polygon', + type: 'fill', + source: 'composite', + 'source-layer': 'road', + metadata: { 'mapbox:group': '1444855786460.0557' }, + minzoom: 12, + filter: [ + 'all', + ['==', ['geometry-type'], 'Polygon'], + [ + 'match', + ['get', 'class'], + [ + 'primary', + 'secondary', + 'tertiary', + 'primary_link', + 'secondary_link', + 'tertiary_link', + ], + true, + false, + ], + ['match', ['get', 'structure'], ['none', 'ford'], true, false], + ], + paint: { + 'fill-color': 'hsl(0, 0%, 100%)', + 'fill-outline-color': '#d6d9e6', + }, + }, + { + id: 'road-local-polygon', + type: 'fill', + source: 'composite', + 'source-layer': 'road', + metadata: { 'mapbox:group': '1444855786460.0557' }, + minzoom: 12, + filter: [ + 'all', + ['==', ['geometry-type'], 'Polygon'], + ['match', ['get', 'class'], ['street'], true, false], + ], + paint: { + 'fill-color': 'hsl(0, 0%, 100%)', + 'fill-outline-color': '#d6d9e6', + }, + }, + { + id: 'road-sidewalk-polygon', + type: 'fill', + source: 'composite', + 'source-layer': 'road', + metadata: { 'mapbox:group': '1444855786460.0557' }, + minzoom: 12, + filter: [ + 'all', + ['==', ['geometry-type'], 'Polygon'], + ['match', ['get', 'class'], ['street_limited'], true, false], + ], + paint: { + 'fill-color': 'hsl(0, 0%, 100%)', + 'fill-outline-color': '#d6d9e6', + }, + }, ]; From 30d25f4bfc0629d7b0e19035389cbaa05affac1f Mon Sep 17 00:00:00 2001 From: yyjjjj Date: Wed, 2 Dec 2020 22:32:42 +0900 Subject: [PATCH 085/138] =?UTF-8?q?[refactor]=20[#81]=20road=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=A0=81=EC=9A=A9=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - layerNames 형식 세분화 및 통일 - src/utils/map-styling/road.ts에서 roadStyling 함수로 들어오는 subFeatureName과 스타일링 관련 함수로 전달해줄 layerNames를 mapping 해준다. - 그 결과 같은 흐름을 공유하고 있는 스타일 적용 부분을 stylingCategory 함수를 재활용하여 처리할 수 있다. --- .../map-styling/road-categories/arterial.ts | 119 ++----------- .../map-styling/road-categories/local.ts | 108 +----------- .../map-styling/road-categories/sidewalk.ts | 112 +------------ .../road-categories/stylingCategory.ts | 158 ++++++++++++++++++ src/utils/map-styling/road.ts | 53 +++--- 5 files changed, 208 insertions(+), 342 deletions(-) create mode 100644 src/utils/map-styling/road-categories/stylingCategory.ts diff --git a/src/utils/map-styling/road-categories/arterial.ts b/src/utils/map-styling/road-categories/arterial.ts index 36ca1f9..bf9f685 100644 --- a/src/utils/map-styling/road-categories/arterial.ts +++ b/src/utils/map-styling/road-categories/arterial.ts @@ -1,17 +1,3 @@ -import { stylingProps } from '../index'; -import { - StyleKeyType, - ElementNameType, - SubElementNameType, -} from '../../../store/common/type'; -import { - applyVisibility, - applyColor, - applyWeight, - ColorType, - WeightType, -} from '../../applyStyle'; - export const arterialLayerNames = { all: [ 'road-arterial', @@ -19,8 +5,22 @@ export const arterialLayerNames = { 'road-exit-shield', 'road-arterial-label', ], - polygon: [], - line: ['road-arterial'], + polygon: ['road-arterial-polygon'], + line: [ + 'road-arterial', + 'road-primary', + 'road-secondary-tertiary', + 'road-motorway-trunk', + 'road-minor', + 'road-minor-low', + ], + stroke: [ + 'road-primary-case', + 'road-secondary-tertiary-case', + 'road-motorway-trunk-case', + 'road-minor-case', + 'road-minor-low', + ], text: { all: ['road-number-shield', 'road-exit-shield', 'road-arterial-label'], hasStroke: ['road-arterial-label'], @@ -28,90 +28,3 @@ export const arterialLayerNames = { }, icon: [], }; - -function arterialStyling({ - map, - subFeatureName, - detailName, - subDetailName, - key, - style, -}: stylingProps): void { - const { visibility, color, weight } = style; - - if (key === 'visibility') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) - applyVisibility({ - map, - layerNames: arterialLayerNames.line, - visibility, - }); - } else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.fill) - applyVisibility({ - map, - layerNames: arterialLayerNames.text.noStroke, - visibility, - }); - else if (subDetailName === SubElementNameType.stroke) - applyVisibility({ - map, - layerNames: arterialLayerNames.text.hasStroke, - visibility, - }); - } - } else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) { - applyColor({ - map, - layerNames: arterialLayerNames.line, - color, - type: ColorType.line, - [key]: style[key as StyleKeyType], - }); - } - } else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.fill) { - applyColor({ - map, - layerNames: arterialLayerNames.text.all, - color, - type: ColorType.text, - [key]: style[key as StyleKeyType], - }); - } else if (subDetailName === SubElementNameType.stroke) { - applyColor({ - map, - layerNames: arterialLayerNames.text.hasStroke, - color, - type: ColorType.textHalo, - [key]: style[key as StyleKeyType], - }); - } - } - } else if (key === 'weight') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) { - applyWeight({ - map, - layerNames: arterialLayerNames.line, - type: WeightType.line, - weight, - }); - } - } - if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.stroke) - applyWeight({ - map, - layerNames: arterialLayerNames.text.hasStroke, - type: WeightType.textHalo, - weight, - }); - } - } -} - -export default arterialStyling; diff --git a/src/utils/map-styling/road-categories/local.ts b/src/utils/map-styling/road-categories/local.ts index c83aa15..617961f 100644 --- a/src/utils/map-styling/road-categories/local.ts +++ b/src/utils/map-styling/road-categories/local.ts @@ -1,21 +1,8 @@ -import { stylingProps } from '../index'; -import { - StyleKeyType, - ElementNameType, - SubElementNameType, -} from '../../../store/common/type'; -import { - applyVisibility, - applyColor, - applyWeight, - ColorType, - WeightType, -} from '../../applyStyle'; - -const localLayerNames = { +export const localLayerNames = { all: ['ferry', 'ferry-auto', 'road-local', 'road-local-label'], - polygon: [], - line: ['ferry', 'ferry-auto', 'road-local'], + polygon: ['road-local-polygon'], + line: ['ferry', 'ferry-auto', 'road-local', 'road-street'], + stroke: ['road-street-case'], text: { all: ['road-local-label'], hasStroke: ['road-local-label'], @@ -23,90 +10,3 @@ const localLayerNames = { }, icon: [], }; - -function localStyling({ - map, - subFeatureName, - detailName, - subDetailName, - key, - style, -}: stylingProps): void { - const { visibility, color, weight } = style; - - if (key === 'visibility') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) - applyVisibility({ - map, - layerNames: localLayerNames.line, - visibility, - }); - } else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.fill) - applyVisibility({ - map, - layerNames: localLayerNames.text.noStroke, - visibility, - }); - else if (subDetailName === SubElementNameType.stroke) - applyVisibility({ - map, - layerNames: localLayerNames.text.hasStroke, - visibility, - }); - } - } else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === 'section') { - if (subDetailName === 'fill') { - applyColor({ - map, - layerNames: localLayerNames.line, - color, - type: ColorType.line, - [key]: style[key as StyleKeyType], - }); - } - } else if (detailName === 'labelText') { - if (subDetailName === SubElementNameType.fill) { - applyColor({ - map, - layerNames: localLayerNames.text.all, - color, - type: ColorType.text, - [key]: style[key as StyleKeyType], - }); - } else if (subDetailName === SubElementNameType.stroke) { - applyColor({ - map, - layerNames: localLayerNames.text.hasStroke, - color, - type: ColorType.textHalo, - [key]: style[key as StyleKeyType], - }); - } - } - } else if (key === 'weight') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) { - applyWeight({ - map, - layerNames: localLayerNames.line, - type: WeightType.line, - weight, - }); - } - } - if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.stroke) - applyWeight({ - map, - layerNames: localLayerNames.text.hasStroke, - type: WeightType.textHalo, - weight, - }); - } - } -} - -export default localStyling; diff --git a/src/utils/map-styling/road-categories/sidewalk.ts b/src/utils/map-styling/road-categories/sidewalk.ts index d3e5030..6137a5a 100644 --- a/src/utils/map-styling/road-categories/sidewalk.ts +++ b/src/utils/map-styling/road-categories/sidewalk.ts @@ -1,21 +1,12 @@ -import { stylingProps } from '../index'; -import { - StyleKeyType, - ElementNameType, - SubElementNameType, -} from '../../../store/common/type'; -import { - applyVisibility, - applyColor, - applyWeight, - ColorType, - WeightType, -} from '../../applyStyle'; - -const sidewalkLayerNames = { +export const sidewalkLayerNames = { all: ['road-footway', 'road-sidewalk-label'], - polygon: [], - line: ['road-footway'], + polygon: [ + 'road-pedestrian-polygon-fill', + 'road-pedestrian-polygon-pattern', + 'road-sidewalk-polygon', + ], + line: ['road-footway', 'road-pedestrian', 'road-steps', 'road-path'], + stroke: ['road-pedestrian-case'], text: { all: ['road-sidewalk-label'], hasStroke: ['road-sidewalk-label'], @@ -23,90 +14,3 @@ const sidewalkLayerNames = { }, icon: [], }; - -function sidewalkStyling({ - map, - subFeatureName, - detailName, - subDetailName, - key, - style, -}: stylingProps): void { - const { visibility, color, weight } = style; - - if (key === 'visibility') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) - applyVisibility({ - map, - layerNames: sidewalkLayerNames.line, - visibility, - }); - } else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.fill) - applyVisibility({ - map, - layerNames: sidewalkLayerNames.text.noStroke, - visibility, - }); - else if (subDetailName === SubElementNameType.stroke) - applyVisibility({ - map, - layerNames: sidewalkLayerNames.text.hasStroke, - visibility, - }); - } - } else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === 'section') { - if (subDetailName === 'fill') { - applyColor({ - map, - layerNames: sidewalkLayerNames.line, - color, - type: ColorType.line, - [key]: style[key as StyleKeyType], - }); - } - } else if (detailName === 'labelText') { - if (subDetailName === SubElementNameType.fill) { - applyColor({ - map, - layerNames: sidewalkLayerNames.text.all, - color, - type: ColorType.text, - [key]: style[key as StyleKeyType], - }); - } else if (subDetailName === SubElementNameType.stroke) { - applyColor({ - map, - layerNames: sidewalkLayerNames.text.hasStroke, - color, - type: ColorType.textHalo, - [key]: style[key as StyleKeyType], - }); - } - } - } else if (key === 'weight') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) { - applyWeight({ - map, - layerNames: sidewalkLayerNames.line, - type: WeightType.line, - weight, - }); - } - } - if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.stroke) - applyWeight({ - map, - layerNames: sidewalkLayerNames.text.hasStroke, - type: WeightType.textHalo, - weight, - }); - } - } -} - -export default sidewalkStyling; diff --git a/src/utils/map-styling/road-categories/stylingCategory.ts b/src/utils/map-styling/road-categories/stylingCategory.ts new file mode 100644 index 0000000..5e6b4f0 --- /dev/null +++ b/src/utils/map-styling/road-categories/stylingCategory.ts @@ -0,0 +1,158 @@ +import { + applyVisibility, + applyColor, + applyWeight, + ColorType, + WeightType, +} from '../../applyStyle'; +import mapboxgl from 'mapbox-gl'; +import { + ElementNameType, + SubElementNameType, + StyleKeyType, + StyleType, +} from '../../../store/common/type'; + +export interface layerProps { + all: string[]; + polygon: string[]; + line: string[]; + stroke: string[]; + text: { + all: string[]; + hasStroke: string[]; + noStroke: string[]; + }; + icon: string[]; +} + +export interface catergoryStylingProps { + map: mapboxgl.Map; + subFeatureName: string; + detailName: ElementNameType; + subDetailName: SubElementNameType; + key: StyleKeyType; + style: StyleType; + layerNames: layerProps; +} + +function stylingCategory({ + layerNames, + map, + subFeatureName, + detailName, + subDetailName, + key, + style, +}: catergoryStylingProps): void { + const { visibility, color, weight } = style; + + if (key === 'visibility') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyVisibility({ + map, + layerNames: layerNames.line, + visibility, + }); + applyVisibility({ + map, + layerNames: layerNames.polygon, + visibility, + }); + } else if (subDetailName === SubElementNameType.stroke) + applyVisibility({ + map, + layerNames: layerNames.stroke, + visibility, + }); + } else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) + applyVisibility({ + map, + layerNames: layerNames.text.noStroke, + visibility, + }); + else if (subDetailName === SubElementNameType.stroke) + applyVisibility({ + map, + layerNames: layerNames.text.hasStroke, + visibility, + }); + } + } else if (key === 'color' || key === 'saturation' || key === 'lightness') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyColor({ + map, + layerNames: layerNames.line, + color, + type: ColorType.line, + [key]: style[key as StyleKeyType], + }); + applyColor({ + map, + layerNames: layerNames.polygon, + color, + type: ColorType.fill, + [key]: style[key as StyleKeyType], + }); + } else if (subDetailName === SubElementNameType.stroke) { + applyColor({ + map, + layerNames: layerNames.stroke, + color, + type: ColorType.line, + [key]: style[key as StyleKeyType], + }); + } + } else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) { + applyColor({ + map, + layerNames: layerNames.text.all, + color, + type: ColorType.text, + [key]: style[key as StyleKeyType], + }); + } else if (subDetailName === SubElementNameType.stroke) { + applyColor({ + map, + layerNames: layerNames.text.hasStroke, + color, + type: ColorType.textHalo, + [key]: style[key as StyleKeyType], + }); + } + } + } else if (key === 'weight') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyWeight({ + map, + layerNames: layerNames.line, + type: WeightType.line, + weight, + }); + } else if (subDetailName === SubElementNameType.stroke) { + applyWeight({ + map, + layerNames: layerNames.stroke, + type: WeightType.line, + weight, + }); + } + } + if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.stroke) + applyWeight({ + map, + layerNames: layerNames.text.hasStroke, + type: WeightType.textHalo, + weight, + }); + } + } +} + +export default stylingCategory; diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index 016678f..578e815 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -1,7 +1,8 @@ import { stylingProps } from '.'; -import arterialStyling from './road-categories/arterial'; -import localStyling from './road-categories/local'; -import sidewalkStyling from './road-categories/sidewalk'; +import { arterialLayerNames } from './road-categories/arterial'; +import { localLayerNames } from './road-categories/local'; +import { sidewalkLayerNames } from './road-categories/sidewalk'; +import stylingCategory from './road-categories/stylingCategory'; function roadStyling({ map, @@ -11,34 +12,24 @@ function roadStyling({ key, style, }: stylingProps): void { - if (subFeatureName === 'arterial') { - arterialStyling({ - map, - subFeatureName, - detailName, - subDetailName, - key, - style, - }); - } else if (subFeatureName === 'local') { - localStyling({ - map, - subFeatureName, - detailName, - subDetailName, - key, - style, - }); - } else if (subFeatureName === 'sidewalk') { - sidewalkStyling({ - map, - subFeatureName, - detailName, - subDetailName, - key, - style, - }); - } + type RoadSubFeatureType = 'arterial' | 'local' | 'sidewalk'; + + const mappingSubFeatureLayerNames = { + arterial: arterialLayerNames, + local: localLayerNames, + sidewalk: sidewalkLayerNames, + }; + + stylingCategory({ + layerNames: + mappingSubFeatureLayerNames[subFeatureName as RoadSubFeatureType], + map, + subFeatureName, + detailName, + subDetailName, + key, + style, + }); } export default roadStyling; From 53c4eaf1f822c839411e5d16d17f7c63799ff85e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Wed, 2 Dec 2020 23:54:10 +0900 Subject: [PATCH 086/138] =?UTF-8?q?[feat]=20=EB=8B=A4=EC=96=91=ED=95=9C=20?= =?UTF-8?q?=EC=83=81=ED=99=A9=EC=97=90=20=EC=93=B0=EC=9D=BC=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=83=89=EC=83=81=EC=9D=84=20?= =?UTF-8?q?=EC=97=AC=EB=9F=AC=20=EA=B2=BD=EC=9A=B0=EC=9D=98=20=EC=88=98?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC=ED=95=98=EC=98=80=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - layersColor4는 default색을 정하는 것을 담당한다 - 다른 레이어들에게 교통을 추가하였다 --- src/utils/rendering-data/layerColor3.ts | 11 ++++ src/utils/rendering-data/layersColor.ts | 20 ++++-- src/utils/rendering-data/layersColor2.ts | 64 ++++++++++++++++++- src/utils/rendering-data/layersColor4.ts | 78 ++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 7 deletions(-) create mode 100644 src/utils/rendering-data/layersColor4.ts diff --git a/src/utils/rendering-data/layerColor3.ts b/src/utils/rendering-data/layerColor3.ts index 556eda7..34c54c9 100644 --- a/src/utils/rendering-data/layerColor3.ts +++ b/src/utils/rendering-data/layerColor3.ts @@ -35,4 +35,15 @@ export default { 'water-line-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, 'water-point-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, 'waterway-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'mapbox-airport-aeroway-polygon': { + color: 'hsl(40, 97%, 64%)', + type: 'fill', + }, + 'mapbox-airport-polygon': { color: 'hsl(234, 20%, 30%)', type: 'fill' }, + 'mapbox-airport-aeroway-line': { color: 'hsl(230, 23%, 82%)', type: 'line' }, + 'mapbox-airport-label': { color: 'hsl(0, 69%, 50%)', type: 'text' }, + 'transit-bus-label': { color: 'hsl(13, 68%, 63%)', type: 'text' }, + 'mapbox-rail-road-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, + 'transit-rail-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, + 'transit-subway-line': { color: 'hsl(192, 70%, 43%)', type: 'line' }, }; diff --git a/src/utils/rendering-data/layersColor.ts b/src/utils/rendering-data/layersColor.ts index 85a29ec..29a3a54 100644 --- a/src/utils/rendering-data/layersColor.ts +++ b/src/utils/rendering-data/layersColor.ts @@ -74,10 +74,22 @@ export default { mountain: {}, }, transit: { - airport: {}, - bus: {}, - rail: {}, - subway: {}, + airport: { + 'mapbox-airport-aeroway-polygon': 'hsl(40, 97%, 64%)', + 'mapbox-airport-polygon': 'hsl(234, 20%, 30%)', + 'mapbox-airport-aeroway-line': 'hsl(230, 23%, 82%)', + 'mapbox-airport-label': 'hsl(0, 69%, 50%)', + }, + bus: { + 'transit-bus-label': 'hsl(13, 68%, 63%)', + }, + rail: { + 'mapbox-rail-road-line': 'hsl(234, 20%, 30%)', + 'transit-rail-line': 'hsl(234, 20%, 30%)', + }, + subway: { + 'transit-subway-line': 'hsl(192, 70%, 43%)', + }, }, water: { water: 'hsl(205, 87%, 76%)', diff --git a/src/utils/rendering-data/layersColor2.ts b/src/utils/rendering-data/layersColor2.ts index ea12695..aa7c8a7 100644 --- a/src/utils/rendering-data/layersColor2.ts +++ b/src/utils/rendering-data/layersColor2.ts @@ -2,12 +2,17 @@ import { SubElementNameType, ElementNameType, FeatureNameType, - PropertyType, } from '../../store/common/type'; export default { [FeatureNameType.poi]: { - all: {}, + all: { + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + [ElementNameType.labelIcon]: 'transparent', + }, landmark: { [ElementNameType.labelText]: { [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', @@ -81,7 +86,16 @@ export default { }, }, [FeatureNameType.administrative]: { - all: {}, + all: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + }, country: { [ElementNameType.section]: { [SubElementNameType.fill]: 'transparent', @@ -123,6 +137,50 @@ export default { [SubElementNameType.fill]: 'transparent', [SubElementNameType.stroke]: 'transparent', }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + }, + airport: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(40, 97%, 64%)', + [SubElementNameType.stroke]: 'hsl(230, 23%, 82%)', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(0, 69%, 50%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', + }, + }, + bus: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(13, 68%, 63%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', + }, + }, + rail: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(234, 20%, 30%)', + [SubElementNameType.stroke]: 'transparent', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + }, + subway: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(192, 70%, 43%)', + [SubElementNameType.stroke]: 'transparent', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, }, }, [FeatureNameType.water]: { diff --git a/src/utils/rendering-data/layersColor4.ts b/src/utils/rendering-data/layersColor4.ts new file mode 100644 index 0000000..33cea5b --- /dev/null +++ b/src/utils/rendering-data/layersColor4.ts @@ -0,0 +1,78 @@ +interface LayersColorType { + [name: string]: string; +} + +const layersColor: LayersColorType = { + 'poi-all-labelText-fill': 'transparent', + 'poi-all-labelText-stroke': 'transparent', + 'poi-all-labelIcon': 'transparent', + 'poi-landmark-labelText-fill': 'hsl(26, 25%, 32%)', + 'poi-landmark-labelText-stroke': 'hsl(0, 0%, 100%)', + 'poi-landmark-labelIcon': 'transparent', + 'poi-business-labelText-fill': 'hsl(22, 55%, 55%)', + 'poi-business-labelText-stroke': 'hsl(0, 0%, 100%)', + 'poi-business-labelIcon': 'transparent', + 'poi-government-labelText-fill': 'hsl(26, 25%, 32%)', + 'poi-government-labelText-stroke': 'hsl(0, 0%, 100%)', + 'poi-government-labelIcon': 'transparent', + 'poi-medical-labelText-fill': 'hsl(340, 39%, 42%)', + 'poi-medical-labelText-stroke': 'hsl(0, 0%, 100%)', + 'poi-medical-labelIcon': 'transparent', + 'poi-park-labelText-fill': 'hsl(100, 45%, 37%)', + 'poi-park-labelText-stroke': 'hsl(0, 0%, 100%)', + 'poi-park-labelIcon': 'transparent', + 'poi-worship-labelText-fill': 'hsl(26, 25%, 32%)', + 'poi-worship-labelText-stroke': 'hsl(0, 0%, 100%)', + 'poi-worship-labelIcon': 'transparent', + 'poi-school-labelText-fill': 'hsl(51, 40%, 40%)', + 'poi-school-labelText-stroke': 'hsl(0, 0%, 100%)', + 'poi-school-labelIcon': 'transparent', + 'poi-sports-labelText-fill': 'hsl(26, 25%, 32%)', + 'poi-sports-labelText-stroke': 'hsl(0, 0%, 100%)', + 'poi-sports-labelIcon': 'transparent', + 'poi-etc-labelText-fill': 'hsl(26, 25%, 32%)', + 'poi-etc-labelText-stroke': 'hsl(0, 0%, 100%)', + 'poi-etc-labelIcon': 'transparent', + 'administrative-all-section-fill': 'transparent', + 'administrative-all-section-stroke': 'transparent', + 'administrative-all-labelText-fill': 'transparent', + 'administrative-all-labelText-stroke': 'transparent', + 'administrative-country-section-fill': 'transparent', + 'administrative-country-section-stroke': 'hsl(230, 8%, 51%)', + 'administrative-country-labelText-fill': 'hsl(0, 0%, 0%)', + 'administrative-country-labelText-stroke': 'hsl(0, 0%, 100%)', + 'administrative-state-section-fill': 'transparent', + 'administrative-state-section-stroke': 'hsl(230, 14%, 77%)', + 'administrative-state-labelText-fill': 'hsl(0, 0%, 0%)', + 'administrative-state-labelText-stroke': 'hsl(0, 0%, 100%)', + 'administrative-locality-section-fill': 'transparent', + 'administrative-locality-section-stroke': 'transparent', + 'administrative-locality-labelText-fill': 'hsl(0, 0%, 0%)', + 'administrative-locality-labelText-stroke': 'hsl(0, 0%, 100%)', + 'transit-all-section-fill': 'transparent', + 'transit-all-section-stroke': 'transparent', + 'transit-all-labelText-fill': 'transparent', + 'transit-all-labelText-stroke': 'transparent', + 'transit-airport-section-fill': 'hsl(40, 97%, 64%)', + 'transit-airport-section-stroke': 'hsl(230, 23%, 82%)', + 'transit-airport-labelText-fill': 'hsl(0, 69%, 50%)', + 'transit-airport-labelText-stroke': 'hsl(0, 0%, 100%)', + 'transit-bus-section-fill': 'transparent', + 'transit-bus-section-stroke': 'transparent', + 'transit-bus-labelText-fill': 'hsl(13, 68%, 63%)', + 'transit-bus-labelText-stroke': 'hsl(0, 0%, 100%)', + 'transit-rail-section-fill': 'hsl(234, 20%, 30%)', + 'transit-rail-section-stroke': 'transparent', + 'transit-rail-labelText-fill': 'transparent', + 'transit-rail-labelText-stroke': 'transparent', + 'transit-subway-section-fill': 'hsl(192, 70%, 43%)', + 'transit-subway-section-stroke': 'transparent', + 'transit-subway-labelText-fill': 'transparent', + 'transit-subway-labelText-stroke': 'transparent', + 'water-all-section-fill': 'hsl(205, 87%, 76%)', + 'water-all-section-stroke': 'transparent', + 'water-all-labelText-fill': 'hsl(230, 48%, 44%)', + 'water-all-labelText-stroke': 'transparent', +}; + +export default layersColor; From d1fe9251ca388c0c13c733ff6b9cb048df22a53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Wed, 2 Dec 2020 23:56:13 +0900 Subject: [PATCH 087/138] =?UTF-8?q?[feat]=20[#72]=20hslToHEX=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - hsl에서 hex를 바꾸는 함수를 추가. - 상태에는 hex값으로 넣어준다 --- src/utils/colorFormat.ts | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/utils/colorFormat.ts b/src/utils/colorFormat.ts index 9c4166f..8cc4596 100644 --- a/src/utils/colorFormat.ts +++ b/src/utils/colorFormat.ts @@ -40,3 +40,56 @@ export function hexToHSL(color: string): HexToHSLType { l, }; } + +export function hslToHEX(color: string): string { + if (color === 'transparent') return '#000000'; + const hsl = color.match(/\d+/g)?.map((c) => Number(c)) as number[]; + + const h: number = hsl[0]; + const s: number = hsl[1] / 100; + const l: number = hsl[2] / 100; + + const c = (1 - Math.abs(2 * l - 1)) * s; + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)); + const m = l - c / 2; + let r = 0; + let g = 0; + let b = 0; + + if (h >= 0 && h < 60) { + r = c; + g = x; + b = 0; + } else if (h >= 60 && h < 120) { + r = x; + g = c; + b = 0; + } else if (h >= 120 && h < 180) { + r = 0; + g = c; + b = x; + } else if (h >= 180 && h < 240) { + r = 0; + g = x; + b = c; + } else if (h >= 240 && h < 300) { + r = x; + g = 0; + b = c; + } else if (h >= 300 && h < 360) { + r = c; + g = 0; + b = x; + } + // Having obtained RGB, convert channels to hex + let stringR: string = Math.round((r + m) * 255).toString(16); + let stringG: string = Math.round((g + m) * 255).toString(16); + let stringB: string = Math.round((b + m) * 255).toString(16); + + // Prepend 0s, if necessary + if (stringR.length === 1) stringR = `0${stringR}`; + if (stringG.length === 1) stringG = `0${stringG}`; + if (stringB.length === 1) stringB = `0${stringB}`; + + return `#${stringR}${stringG}${stringB}`; +} From 5a6116bcc4d9f3019545038f5ab4b138c0614125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Wed, 2 Dec 2020 23:58:31 +0900 Subject: [PATCH 088/138] =?UTF-8?q?[refactor]=20=EC=A7=80=EB=8F=84=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=EC=97=90=20=EC=84=B8=ED=8C=85=20=EC=8B=9C=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20=EC=83=89=EB=93=A4=EC=9D=84=20?= =?UTF-8?q?=EC=B9=A0=ED=95=B4=EC=A4=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 지도를 초기에 세팅할 때 레이어들의 색깔을 칠해주도록 하였다 - 레이어들의 색은 layersColor3의 파일을 이용한다 --- src/store/common/type.ts | 4 ++-- src/store/map/initializeMap.ts | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index d19e689..7e25a68 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -90,8 +90,8 @@ export type ActionPayload = { }; export interface HasPropertiesType { - featureType: FeatureNameType; - subFeatureType: string; + featureName: FeatureNameType; + subFeatureName: string; } export interface PropertyType { diff --git a/src/store/map/initializeMap.ts b/src/store/map/initializeMap.ts index fcdbcfa..9a89fde 100644 --- a/src/store/map/initializeMap.ts +++ b/src/store/map/initializeMap.ts @@ -10,6 +10,8 @@ import landscape from './layers/landscape'; import water from './layers/water'; import mapboxPOI from './layers/mapbox-poi'; +import initColor from '../../utils/rendering-data/layerColor3'; + const LNG = 126.978; const LAT = 37.5656; const ZOOM = 15.5; @@ -91,6 +93,10 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { ...poi, ] as mapboxgl.Layer[]; layers.forEach((layer: mapboxgl.Layer) => map.addLayer(layer)); + + Object.entries(initColor).forEach(([key, value]) => { + map.setPaintProperty(key, `${value.type}-color`, value.color); + }); }); return map; From 49be0eb7fcdf67d19a0e2595dea5df2013591dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Wed, 2 Dec 2020 23:59:53 +0900 Subject: [PATCH 089/138] =?UTF-8?q?[feat]=20=EC=B4=88=EA=B8=B0=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=8B=9C=20=EC=83=81=ED=83=9C=EC=97=90=20=EA=B0=81?= =?UTF-8?q?=20=EC=86=8D=EC=84=B1=EB=B3=84=EB=A1=9C=20=EC=83=89=EC=83=81?= =?UTF-8?q?=EC=9D=84=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - layersColor4를 이용해서 각 속성별로 초기에 색상을 지정해준다. - 따라서 사이드바에서 옵션별로 색상이 다르게 표기된다. - 채도와 명도 값도 맞춰서 조절해야 할 것 같다. - section / labelText / labelIcon의 조절 여부에 따라서 feature마다 보여지는 세부설정 요소가 달라진다 --- .../Sidebar/SidebarContentMore/DetailType.tsx | 130 ++++++++------- src/store/style/properties.ts | 151 ++++++++++-------- 2 files changed, 156 insertions(+), 125 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 133d776..edda5c2 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -99,81 +99,103 @@ function DetailType({ <> 세부 유형 - - 구역 - {section?.fill.isChanged ? : <>} - { - styleClickHandler( - ElementNameType.section, - SubElementNameType.fill - ); - }} - name="채우기" - /> - {section?.stroke.isChanged ? : <>} - { - styleClickHandler( - ElementNameType.section, - SubElementNameType.stroke - ); - }} - name="윤곽선" - /> - - - 라벨 + {section ? ( - 텍스트 - {labelText?.fill.isChanged ? : <>} + 구역 + {section?.fill.isChanged ? : <>} { styleClickHandler( - ElementNameType.labelText, + ElementNameType.section, SubElementNameType.fill ); }} name="채우기" /> - {labelText?.stroke.isChanged ? : <>} + {section?.stroke.isChanged ? : <>} { styleClickHandler( - ElementNameType.labelText, + ElementNameType.section, SubElementNameType.stroke ); }} name="윤곽선" /> - {labelIcon?.isChanged ? : <>} - styleClickHandler(ElementNameType.labelIcon)} - name="아이콘" - /> + ) : ( + <> + )} + + {labelText ? ( + <> + 라벨 + + 텍스트 + {labelText?.fill.isChanged ? : <>} + { + styleClickHandler( + ElementNameType.labelText, + SubElementNameType.fill + ); + }} + name="채우기" + /> + {labelText?.stroke.isChanged ? ( + + ) : ( + <> + )} + { + styleClickHandler( + ElementNameType.labelText, + SubElementNameType.stroke + ); + }} + name="윤곽선" + /> + + + ) : ( + <> + )} + {labelIcon ? ( + <> + {labelIcon.isChanged ? : <>} + { + styleClickHandler(ElementNameType.labelIcon); + }} + name="아이콘" + /> + + ) : ( + <> + )} { -// return JSON.parse(JSON.stringify(style)); -// }; - -// const getDefaultElement = (): ElementType => { -// return { -// fill: getDefaultStyle(), -// stroke: getDefaultStyle(), -// }; -// }; - -// export const getDefaultFeature = (): FeatureType => { -// return { -// isChanged: false, -// section: getDefaultElement(), -// labelText: getDefaultElement(), -// labelIcon: getDefaultStyle(), -// }; -// }; - import { StyleType, ElementNameType, @@ -36,11 +5,10 @@ import { FeatureNameType, HasPropertiesType, SubElementNameType, - PropertyType, - nice, } from '../common/type'; +import { hslToHEX } from '../../utils/colorFormat'; -import defaultStyle from '../../utils/rendering-data/layersColor2'; +import defaultStyle from '../../utils/rendering-data/layersColor4'; const style: StyleType = { isChanged: false, @@ -51,70 +19,111 @@ const style: StyleType = { lightness: 0, }; -interface getDefaultStyleProps { - featureType: FeatureNameType; - subFeatureType: T; - elementType: ElementNameType; - subElementType: SubElementNameType; +interface getDefaultStyleProps { + featureName: FeatureNameType; + subFeatureName: string; + elementName: ElementNameType; + subElementName?: SubElementNameType; } +const featuresDetailOption = { + poi: { + hasSection: false, + hasLabelText: true, + hasLabelIcon: true, + }, + road: { + hasSection: true, + hasLabelText: true, + hasLabelIcon: true, + }, + administrative: { + hasSection: true, + hasLabelText: true, + hasLabelIcon: false, + }, + landscape: { + hasSection: true, + hasLabelText: true, + hasLabelIcon: false, + }, + transit: { + hasSection: true, + hasLabelText: true, + hasLabelIcon: false, + }, + water: { + hasSection: true, + hasLabelText: true, + hasLabelIcon: false, + }, + marker: { + hasSection: false, + hasLabelText: true, + hasLabelIcon: true, + }, +}; + export const getDefaultStyle = ({ - featureType, - subFeatureType, - elementType, - subElementType, + featureName, + subFeatureName, + elementName, + subElementName, }: getDefaultStyleProps): StyleType => { + const color = + defaultStyle[ + `${featureName}-${subFeatureName}-${elementName}-${subElementName || ''}` + ]; return { ...JSON.parse(JSON.stringify(style)), - color: defaultStyle[featureType][subFeatureType], - // defaultStyle[featureType][subFeatureType][elementType][subElementType], + color: color ? hslToHEX(color) : '#000000', }; }; export const getDefaultFeature = ({ - featureType, - subFeatureType, + featureName, + subFeatureName, }: HasPropertiesType): FeatureType => { return { isChanged: false, - section: defaultStyle[featureType][subFeatureType][ElementNameType.section] + section: featuresDetailOption[featureName].hasSection ? { [SubElementNameType.fill]: getDefaultStyle({ - featureType, - subFeatureType, - elementType: ElementNameType.section, - subElementType: SubElementNameType.fill, + featureName, + subFeatureName, + elementName: ElementNameType.section, + subElementName: SubElementNameType.fill, }), [SubElementNameType.stroke]: getDefaultStyle({ - featureType, - subFeatureType, - elementType: ElementNameType.section, - subElementType: SubElementNameType.stroke, + featureName, + subFeatureName, + elementName: ElementNameType.section, + subElementName: SubElementNameType.stroke, }), } : null, - labelText: defaultStyle[featureType][subFeatureType][ - ElementNameType.labelText - ] + labelText: featuresDetailOption[featureName].hasLabelText ? { [SubElementNameType.fill]: getDefaultStyle({ - featureType, - subFeatureType, - elementType: ElementNameType.labelText, - subElementType: SubElementNameType.fill, + featureName, + subFeatureName, + elementName: ElementNameType.labelText, + subElementName: SubElementNameType.fill, }), [SubElementNameType.stroke]: getDefaultStyle({ - featureType, - subFeatureType, - elementType: ElementNameType.labelText, - subElementType: SubElementNameType.stroke, + featureName, + subFeatureName, + elementName: ElementNameType.labelText, + subElementName: SubElementNameType.stroke, }), } : null, - labelIcon: defaultStyle[featureType][subFeatureType][ - ElementNameType.labelIcon - ] - ? getDefaultStyle() + labelIcon: featuresDetailOption[featureName].hasLabelIcon + ? getDefaultStyle({ + featureName, + subFeatureName, + elementName: ElementNameType.labelIcon, + }) : null, }; }; From 0dbef9696b236ca05b3ebe9ff9cbb8a7168508a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 00:01:27 +0900 Subject: [PATCH 090/138] =?UTF-8?q?[refactor]=20[#72]=20=EC=9D=B4=EC=A0=84?= =?UTF-8?q?=20=EC=BB=A4=EB=B0=8B=EC=97=90=20=EB=94=B0=EB=9D=BC=EC=84=9C=20?= =?UTF-8?q?getDefaultStyle=ED=95=A8=EC=88=98=20=ED=98=B8=EC=B6=9C=20?= =?UTF-8?q?=EC=8B=9C=20=ED=8C=94=EC=9A=94=ED=95=B4=EC=A7=84=20=EA=B0=92?= =?UTF-8?q?=EB=93=A4=EC=9D=84=20=EB=84=98=EA=B2=A8=EC=A4=80=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이전 커밋의 getDefaultStyle함수 변경으로 인해서 호출 하는 각 부분에서 필요로하는 인자를 넘겨준다 --- src/hooks/sidebar/useStyleType.ts | 7 ++++++- src/store/style/compareStyle.ts | 32 ++++++++++++++++++++++++++++--- src/store/style/getReducer.ts | 12 +++++++++--- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index b4929d4..96e4b7f 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -36,7 +36,12 @@ function useStyleType({ const map = useSelector((state) => state.map.map) as mapboxgl.Map; const styleElement = useSelector((state) => { if (!detailName) { - return getDefaultStyle(); + return getDefaultStyle({ + featureName, + subFeatureName, + elementName: detailName, + subElementName: subDetailName, + }); } const feature = state[featureName][subFeatureName]; if (detailName === ElementNameType.labelIcon) return feature[detailName]; diff --git a/src/store/style/compareStyle.ts b/src/store/style/compareStyle.ts index 7f8c506..25717e7 100644 --- a/src/store/style/compareStyle.ts +++ b/src/store/style/compareStyle.ts @@ -1,8 +1,34 @@ import { getDefaultStyle } from './properties'; -import { StyleType, StyleKeyType, objType } from '../common/type'; +import { + StyleType, + StyleKeyType, + objType, + FeatureNameType, + ElementNameType, + SubElementNameType, +} from '../common/type'; -export function checkStyleIsChanged(targetStyle: StyleType): boolean { - const defaultStyle: StyleType = getDefaultStyle(); +interface CheckStyleIsChangedProps { + targetStyle: StyleType; + featureName: FeatureNameType; + subFeatureName: string; + elementName: ElementNameType; + subElementName?: SubElementNameType; +} + +export function checkStyleIsChanged({ + targetStyle, + featureName, + subFeatureName, + elementName, + subElementName, +}: CheckStyleIsChangedProps): boolean { + const defaultStyle: StyleType = getDefaultStyle({ + featureName, + subFeatureName, + elementName, + subElementName, + }); const keys = Object.keys(defaultStyle) as StyleKeyType[]; const filteredKeys = keys.filter( diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts index f56d689..9ebe131 100644 --- a/src/store/style/getReducer.ts +++ b/src/store/style/getReducer.ts @@ -25,8 +25,8 @@ export default function getReducer(IDX: number): ReducerType { const initialState = subFeatures.reduce((acc: FeatureState, cur: string) => { acc[cur] = getDefaultFeature({ - featureType: renderingData[IDX].typeKey, - subFeatureType: cur, + featureName: renderingData[IDX].typeKey, + subFeatureName: cur, }); return acc; }, {}); @@ -49,7 +49,13 @@ export default function getReducer(IDX: number): ReducerType { if (feature !== renderingData[IDX].typeKey) return state; - style.isChanged = checkStyleIsChanged(style); + style.isChanged = checkStyleIsChanged({ + targetStyle: style, + featureName: feature, + subFeatureName: subFeature, + elementName: element, + subElementName: subElement, + }); const newState: FeatureState = JSON.parse(JSON.stringify(state)); const newFeature: FeatureType = newState[subFeature as string]; From 60da312cb337155bfb13bc31f9d2849fc8d23a49 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 00:05:15 +0900 Subject: [PATCH 091/138] =?UTF-8?q?[fix]=20=EA=B3=B5=ED=86=B5=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 중복해서 프로퍼티로 사용되는 타입을 FeaturePropsType, ElementPropsType으로 분리해 주었음 - 기본 스타일 상태를 갖고 있는 layerColor 데이터의 타입과 동일하게 PropertyType 맞춤 - PropertyType 속성값으로 기존에 선언된 enum을 사용하도록 수정함 --- src/store/common/type.ts | 60 +++++++++++++++------------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index d19e689..9c5a48d 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,25 +1,5 @@ import { init, setStyle } from '../style/action'; -export interface nice { - poi: - | 'all' - | 'landmark' - | 'business' - | 'government' - | 'medical' - | 'park' - | 'worship' - | 'school' - | 'sports' - | 'etc'; - road: 'all'; - administrative: 'all' | 'country' | 'state' | 'locality'; - landscape: 'all'; - transit: 'all'; - water: 'all'; - marker: 'all'; -} - export type hello = 'landmark'; export enum ElementNameType { @@ -79,31 +59,35 @@ export interface FeatureState { [name: string]: FeatureType; } -export type ActionType = ReturnType | ReturnType; +export interface FeaturePropsType { + feature: FeatureNameType; + subFeature: string; +} -export type ActionPayload = { +export interface ElementPropsType extends FeaturePropsType { feature: FeatureNameType; subFeature: string; element: ElementNameType; subElement?: SubElementNameType; +} + +export type ActionType = ReturnType | ReturnType; + +export interface ActionPayload extends ElementPropsType { style: StyleType; -}; +} -export interface HasPropertiesType { - featureType: FeatureNameType; - subFeatureType: string; +export interface StylePropsType { + [SubElementNameType.fill]: string; + [SubElementNameType.stroke]: string; } -export interface PropertyType { - [subFeatureType: string]: { - [ElementNameType.section]?: { - fill: string; - stroke: string; - } | null; - [ElementNameType.labelText]?: { - fill: string; - stroke: string; - } | null; - [ElementNameType.labelIcon]?: string | null; +export type PropertyType = { + [featureName in FeatureNameType]: { + [subFeatureName: string]: { + [ElementNameType.section]?: StylePropsType | null; + [ElementNameType.labelText]?: StylePropsType | null; + [ElementNameType.labelIcon]?: string | null; + }; }; -} +}; From eb73f4b7f59e8334c7ec484cd9cc10ab2178dda0 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 00:07:24 +0900 Subject: [PATCH 092/138] =?UTF-8?q?[fix]=20=EA=B8=B0=EB=B3=B8=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=ED=8C=8C=EC=9D=BC=20=EA=B2=B0=EC=A0=95,?= =?UTF-8?q?=20=EC=9E=84=EC=8B=9C=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=82=BD?= =?UTF-8?q?=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - store 초기화할 때 데이터가 필요해서 임시로 데이터를 넣었음 - layerColor2를 사용하기로 결정해서 다른 파일 삭제하고 파일명 수정함 --- src/utils/rendering-data/layerColor3.ts | 38 --- src/utils/rendering-data/layersColor.ts | 320 +++++++++++++++++++---- src/utils/rendering-data/layersColor2.ts | 148 ----------- 3 files changed, 268 insertions(+), 238 deletions(-) delete mode 100644 src/utils/rendering-data/layerColor3.ts delete mode 100644 src/utils/rendering-data/layersColor2.ts diff --git a/src/utils/rendering-data/layerColor3.ts b/src/utils/rendering-data/layerColor3.ts deleted file mode 100644 index 556eda7..0000000 --- a/src/utils/rendering-data/layerColor3.ts +++ /dev/null @@ -1,38 +0,0 @@ -export default { - 'poi-attraction': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-arts-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-landmark-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-business': { color: 'hsl(22, 55%, 55%)', type: 'text' }, - 'poi-food-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, - 'poi-store-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, - 'poi-government': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-public-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-general-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-medical': { color: 'hsl(340, 39%, 42%)', type: 'text' }, - 'poi-medical-label': { color: 'hsl(340, 39%, 42%)', type: 'text' }, - 'poi-park': { color: 'hsl(100, 45%, 37%)', type: 'text' }, - 'poi-park-label': { color: 'hsl(100, 45%, 37%)', type: 'text' }, - 'poi-worship': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-religion-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-school': { color: 'hsl(51, 40%, 40%)', type: 'text' }, - 'poi-education-label': { color: 'hsl(51, 40%, 40%)', type: 'text' }, - 'poi-sport-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-etc': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-industrial-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-historic-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-building-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'admin-0-boundary': { color: 'hsl(230, 8%, 51%)', type: 'line' }, - 'admin-0-boundary-bg': { color: 'hsl(230, 8%, 51%)', type: 'line' }, - 'admin-0-boundary-disputed': { color: 'hsl(230, 8%, 51%)', type: 'line' }, - 'country-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, - 'admin-1-boundary-bg': { color: 'hsl(230, 14%, 77%)', type: 'line' }, - 'admin-1-boundary': { color: 'hsl(230, 14%, 77%)', type: 'line' }, - 'state-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, - 'settlement-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, - 'settlement-subdivision-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, - water: { color: 'hsl(205, 87%, 76%)', type: 'fill' }, - 'water-polygon': { color: 'hsl(205, 87%, 76%)', type: 'fill' }, - 'water-line-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, - 'water-point-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, - 'waterway-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, -}; diff --git a/src/utils/rendering-data/layersColor.ts b/src/utils/rendering-data/layersColor.ts index 85a29ec..74b4e41 100644 --- a/src/utils/rendering-data/layersColor.ts +++ b/src/utils/rendering-data/layersColor.ts @@ -1,89 +1,305 @@ -// 어차피 레이어 하나 바꾸면 다 바뀔 건데 체크박스로 해둘 필요가 있을까?? -export default { +import { PropertyType } from '../../store/common/type'; + +const data: PropertyType = { poi: { + all: { + // 임시 + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', + }, landmark: { - 'poi-attraction': 'hsl(26, 25%, 32%)', - 'poi-arts-label': 'hsl(26, 25%, 32%)', - 'poi-landmark-label': 'hsl(26, 25%, 32%)', + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', }, business: { - 'poi-business': 'hsl(22, 55%, 55%)', - 'poi-food-label': 'hsl(22, 55%, 55%)', - 'poi-store-label': 'hsl(22, 55%, 55%)', + labelText: { + fill: 'hsl(22, 55%, 55%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', }, government: { - 'poi-government': 'hsl(26, 25%, 32%)', - 'poi-public-label': 'hsl(26, 25%, 32%)', - 'poi-general-label': 'hsl(26, 25%, 32%)', + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', }, medical: { - 'poi-medical': 'hsl(340, 39%, 42%)', - 'poi-medical-label': 'hsl(340, 39%, 42%)', + labelText: { + fill: 'hsl(340, 39%, 42%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', }, park: { - 'poi-park': 'hsl(100, 45%, 37%)', - 'poi-park-label': 'hsl(100, 45%, 37%)', + labelText: { + fill: 'hsl(100, 45%, 37%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', }, worship: { - 'poi-worship': 'hsl(26, 25%, 32%)', - 'poi-religion-label': 'hsl(26, 25%, 32%)', + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', }, school: { - 'poi-school': 'hsl(51, 40%, 40%)', - 'poi-education-label': 'hsl(51, 40%, 40%)', + labelText: { + fill: 'hsl(51, 40%, 40%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', }, sports: { - 'poi-sport-label': 'hsl(26, 25%, 32%)', + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', }, etc: { - 'poi-etc': 'hsl(26, 25%, 32%)', - 'poi-industrial-label': 'hsl(26, 25%, 32%)', - 'poi-historic-label': 'hsl(26, 25%, 32%)', - 'poi-building-label': 'hsl(26, 25%, 32%)', + labelText: { + fill: 'hsl(26, 25%, 32%)', + stroke: 'hsl(0, 0%, 100%)', + }, + labelIcon: 'transparent', }, }, + // 임시 road: { - highway: {}, - arterial: {}, - local: {}, - sidewalk: {}, - 'bicycle-road': {}, + all: { + section: { + fill: 'transparent', + stroke: 'hsl(230, 8%, 51%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + highway: { + section: { + fill: 'transparent', + stroke: 'hsl(230, 8%, 51%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + arterial: { + section: { + fill: 'transparent', + stroke: 'hsl(230, 8%, 51%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + local: { + section: { + fill: 'transparent', + stroke: 'hsl(230, 8%, 51%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + sidewalk: { + section: { + fill: 'transparent', + stroke: 'hsl(230, 8%, 51%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, }, administrative: { + all: { + section: { + fill: 'transparent', + stroke: 'hsl(230, 8%, 51%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, country: { - 'admin-0-boundary': 'hsl(230, 8%, 51%)', - 'admin-0-boundary-bg': 'hsl(35, 12%, 89%)', - 'admin-0-boundary-disputed': 'hsl(230, 8%, 51%)', - 'country-label': 'hsl(0, 0%, 0%)', + section: { + fill: 'transparent', + stroke: 'hsl(230, 8%, 51%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, }, state: { - 'admin-1-boundary-bg': 'hsl(35, 12%, 89%)', - 'admin-1-boundary': 'hsl(230, 14%, 77%)', - 'state-label': 'hsl(0, 0%, 0%)', + section: { + fill: 'transparent', + stroke: 'hsl(230, 14%, 77%)', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, }, locality: { - 'settlement-label': 'hsl(0, 0%, 0%)', - 'settlement-subdivision-label': 'hsl(230, 29%, 35%)', + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, }, }, + // 임시 landscape: { - 'human-made': {}, - building: {}, - natural: {}, - landcover: {}, - mountain: {}, + all: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + 'human-made': { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + building: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + natural: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + landcover: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + mountain: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, }, transit: { - airport: {}, - bus: {}, - rail: {}, - subway: {}, + all: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + airport: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + bus: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + rail: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, + subway: { + section: { + fill: 'transparent', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(0, 0%, 0%)', + stroke: 'hsl(0, 0%, 100%)', + }, + }, }, water: { - water: 'hsl(205, 87%, 76%)', - 'water-polygon': 'hsl(205, 87%, 76%)', - 'water-line-label': 'hsl(230, 48%, 44%)', - 'water-point-label': 'hsl(230, 48%, 44%)', - 'waterway-label': 'hsl(230, 48%, 44%)', + all: { + section: { + fill: 'hsl(205, 87%, 76%)', + stroke: 'transparent', + }, + labelText: { + fill: 'hsl(230, 48%, 44%)', + stroke: 'transparent', + }, + }, + }, + marker: { + all: { + labelText: { + fill: 'transparent', + stroke: 'transparent', + }, + }, }, }; + +export default data; diff --git a/src/utils/rendering-data/layersColor2.ts b/src/utils/rendering-data/layersColor2.ts deleted file mode 100644 index ea12695..0000000 --- a/src/utils/rendering-data/layersColor2.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { - SubElementNameType, - ElementNameType, - FeatureNameType, - PropertyType, -} from '../../store/common/type'; - -export default { - [FeatureNameType.poi]: { - all: {}, - landmark: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - [ElementNameType.labelIcon]: 'transparent', - }, - business: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(22, 55%, 55%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - [ElementNameType.labelIcon]: 'transparent', - }, - government: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - [ElementNameType.labelIcon]: 'transparent', - }, - medical: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(340, 39%, 42%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - [ElementNameType.labelIcon]: 'transparent', - }, - park: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(100, 45%, 37%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - [ElementNameType.labelIcon]: 'transparent', - }, - worship: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - [ElementNameType.labelIcon]: 'transparent', - }, - school: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(51, 40%, 40%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - [ElementNameType.labelIcon]: 'transparent', - }, - sports: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - [ElementNameType.labelIcon]: 'transparent', - }, - etc: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - [ElementNameType.labelIcon]: 'transparent', - }, - }, - [FeatureNameType.road]: { - all: { - [ElementNameType.section]: { - [SubElementNameType.fill]: 'transparent', - [SubElementNameType.stroke]: 'transparent', - }, - }, - }, - [FeatureNameType.administrative]: { - all: {}, - country: { - [ElementNameType.section]: { - [SubElementNameType.fill]: 'transparent', - [SubElementNameType.stroke]: 'hsl(230, 8%, 51%)', - }, - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(0, 0%, 0%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - }, - state: { - [ElementNameType.section]: { - [SubElementNameType.fill]: 'transparent', - [SubElementNameType.stroke]: 'hsl(230, 14%, 77%)', - }, - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(0, 0%, 0%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - }, - locality: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(0, 0%, 0%)', - [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', - }, - }, - }, - [FeatureNameType.landscape]: { - all: { - [ElementNameType.section]: { - [SubElementNameType.fill]: 'transparent', - [SubElementNameType.stroke]: 'transparent', - }, - }, - }, - [FeatureNameType.transit]: { - all: { - [ElementNameType.section]: { - [SubElementNameType.fill]: 'transparent', - [SubElementNameType.stroke]: 'transparent', - }, - }, - }, - [FeatureNameType.water]: { - all: { - [ElementNameType.section]: { - [SubElementNameType.fill]: 'hsl(205, 87%, 76%)', - [SubElementNameType.stroke]: 'transparent', - }, - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'hsl(230, 48%, 44%)', - [SubElementNameType.stroke]: 'transparent', - }, - }, - }, - [FeatureNameType.marker]: { - all: { - [ElementNameType.labelText]: { - [SubElementNameType.fill]: 'transparent', - [SubElementNameType.stroke]: 'transparent', - }, - }, - }, -}; From 99128feedcc2bd4e09b1daddb1101730193dbedc Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 00:09:00 +0900 Subject: [PATCH 093/138] =?UTF-8?q?[fix]=20=EC=84=B8=EB=B6=80=EC=9C=A0?= =?UTF-8?q?=ED=98=95=20=EC=9C=A0=EB=AC=B4=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기능 유형에 따라 필요하지 않는 세부유형은 표시되지 않도록 수정함 --- .../Sidebar/SidebarContentMore/DetailType.tsx | 126 ++++++++++-------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 133d776..5fe32fb 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -99,82 +99,98 @@ function DetailType({ <> 세부 유형 - - 구역 - {section?.fill.isChanged ? : <>} - { - styleClickHandler( - ElementNameType.section, - SubElementNameType.fill - ); - }} - name="채우기" - /> - {section?.stroke.isChanged ? : <>} - { - styleClickHandler( - ElementNameType.section, - SubElementNameType.stroke - ); - }} - name="윤곽선" - /> - - - 라벨 + {section ? ( - 텍스트 - {labelText?.fill.isChanged ? : <>} + 구역 + {section?.fill.isChanged ? : <>} { styleClickHandler( - ElementNameType.labelText, + ElementNameType.section, SubElementNameType.fill ); }} name="채우기" /> - {labelText?.stroke.isChanged ? : <>} + {section?.stroke.isChanged ? : <>} { styleClickHandler( - ElementNameType.labelText, + ElementNameType.section, SubElementNameType.stroke ); }} name="윤곽선" /> - {labelIcon?.isChanged ? : <>} - styleClickHandler(ElementNameType.labelIcon)} - name="아이콘" - /> - + ) : ( + <> + )} + {labelText ? ( + + 라벨 + + 텍스트 + {labelText?.fill.isChanged ? : <>} + { + styleClickHandler( + ElementNameType.labelText, + SubElementNameType.fill + ); + }} + name="채우기" + /> + {labelText?.stroke.isChanged ? : <>} + { + styleClickHandler( + ElementNameType.labelText, + SubElementNameType.stroke + ); + }} + name="윤곽선" + /> + + {labelIcon ? ( + <> + {labelIcon?.isChanged ? : <>} + { + styleClickHandler(ElementNameType.labelIcon); + }} + name="아이콘" + /> + + ) : ( + <> + )} + + ) : ( + <> + )} Date: Thu, 3 Dec 2020 00:14:05 +0900 Subject: [PATCH 094/138] =?UTF-8?q?[fix]=20=ED=83=80=EC=9E=85=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=B0=9C=EC=83=9D=20=ED=95=B4=EA=B2=B0,=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - common/type에 선언된 타입으로 바꾸면서 속성명 바뀜 - 29줄 as StylePropsType 으로 타입 단언하여 에러 발생 제거함 --- src/store/style/properties.ts | 117 ++++++++++++---------------------- 1 file changed, 40 insertions(+), 77 deletions(-) diff --git a/src/store/style/properties.ts b/src/store/style/properties.ts index d7e2e87..3a8892c 100644 --- a/src/store/style/properties.ts +++ b/src/store/style/properties.ts @@ -1,46 +1,14 @@ -// import { StyleType, ElementType, FeatureType } from '../common/type'; - -// const style: StyleType = { -// isChanged: false, -// visibility: 'inherit', -// color: '#55bf40', -// weight: 0, -// saturation: 0, -// lightness: 0, -// }; - -// export const getDefaultStyle = (): StyleType => { -// return JSON.parse(JSON.stringify(style)); -// }; - -// const getDefaultElement = (): ElementType => { -// return { -// fill: getDefaultStyle(), -// stroke: getDefaultStyle(), -// }; -// }; - -// export const getDefaultFeature = (): FeatureType => { -// return { -// isChanged: false, -// section: getDefaultElement(), -// labelText: getDefaultElement(), -// labelIcon: getDefaultStyle(), -// }; -// }; - import { StyleType, ElementNameType, FeatureType, - FeatureNameType, - HasPropertiesType, SubElementNameType, - PropertyType, - nice, + FeaturePropsType, + ElementPropsType, + StylePropsType, } from '../common/type'; -import defaultStyle from '../../utils/rendering-data/layersColor2'; +import defaultStyle from '../../utils/rendering-data/layersColor'; const style: StyleType = { isChanged: false, @@ -51,70 +19,65 @@ const style: StyleType = { lightness: 0, }; -interface getDefaultStyleProps { - featureType: FeatureNameType; - subFeatureType: T; - elementType: ElementNameType; - subElementType: SubElementNameType; -} - export const getDefaultStyle = ({ - featureType, - subFeatureType, - elementType, - subElementType, -}: getDefaultStyleProps): StyleType => { + feature, + subFeature, + element, + subElement, +}: ElementPropsType): StyleType => { + const defaultColor = subElement + ? (defaultStyle[feature][subFeature][element] as StylePropsType)[subElement] + : defaultStyle[feature][subFeature][element]; return { ...JSON.parse(JSON.stringify(style)), - color: defaultStyle[featureType][subFeatureType], - // defaultStyle[featureType][subFeatureType][elementType][subElementType], + color: defaultColor, }; }; export const getDefaultFeature = ({ - featureType, - subFeatureType, -}: HasPropertiesType): FeatureType => { + feature, + subFeature, +}: FeaturePropsType): FeatureType => { return { isChanged: false, - section: defaultStyle[featureType][subFeatureType][ElementNameType.section] + section: defaultStyle[feature][subFeature][ElementNameType.section] ? { [SubElementNameType.fill]: getDefaultStyle({ - featureType, - subFeatureType, - elementType: ElementNameType.section, - subElementType: SubElementNameType.fill, + feature, + subFeature, + element: ElementNameType.section, + subElement: SubElementNameType.fill, }), [SubElementNameType.stroke]: getDefaultStyle({ - featureType, - subFeatureType, - elementType: ElementNameType.section, - subElementType: SubElementNameType.stroke, + feature, + subFeature, + element: ElementNameType.section, + subElement: SubElementNameType.stroke, }), } : null, - labelText: defaultStyle[featureType][subFeatureType][ - ElementNameType.labelText - ] + labelText: defaultStyle[feature][subFeature][ElementNameType.labelText] ? { [SubElementNameType.fill]: getDefaultStyle({ - featureType, - subFeatureType, - elementType: ElementNameType.labelText, - subElementType: SubElementNameType.fill, + feature, + subFeature, + element: ElementNameType.labelText, + subElement: SubElementNameType.fill, }), [SubElementNameType.stroke]: getDefaultStyle({ - featureType, - subFeatureType, - elementType: ElementNameType.labelText, - subElementType: SubElementNameType.stroke, + feature, + subFeature, + element: ElementNameType.labelText, + subElement: SubElementNameType.stroke, }), } : null, - labelIcon: defaultStyle[featureType][subFeatureType][ - ElementNameType.labelIcon - ] - ? getDefaultStyle() + labelIcon: defaultStyle[feature][subFeature][ElementNameType.labelIcon] + ? getDefaultStyle({ + feature, + subFeature, + element: ElementNameType.labelIcon, + }) : null, }; }; From e3528e60ab3d68119c14a0d60cf7674608dde8ae Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 00:18:15 +0900 Subject: [PATCH 095/138] =?UTF-8?q?[fix]=20getDefaultStyle=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getDefaultStyle 수정에 맞춰서 전달하는 인자도 바꿈 --- src/hooks/sidebar/useStyleType.ts | 7 ++++++- src/store/style/compareStyle.ts | 25 ++++++++++++++++++++----- src/store/style/getReducer.ts | 8 +++----- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index b4929d4..21c6237 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -36,7 +36,12 @@ function useStyleType({ const map = useSelector((state) => state.map.map) as mapboxgl.Map; const styleElement = useSelector((state) => { if (!detailName) { - return getDefaultStyle(); + return getDefaultStyle({ + feature: featureName, + subFeature: subFeatureName, + element: detailName, + subElement: subDetailName, + }); } const feature = state[featureName][subFeatureName]; if (detailName === ElementNameType.labelIcon) return feature[detailName]; diff --git a/src/store/style/compareStyle.ts b/src/store/style/compareStyle.ts index 7f8c506..5d7cd21 100644 --- a/src/store/style/compareStyle.ts +++ b/src/store/style/compareStyle.ts @@ -1,13 +1,28 @@ import { getDefaultStyle } from './properties'; -import { StyleType, StyleKeyType, objType } from '../common/type'; +import { + StyleType, + StyleKeyType, + objType, + ActionPayload, +} from '../common/type'; -export function checkStyleIsChanged(targetStyle: StyleType): boolean { - const defaultStyle: StyleType = getDefaultStyle(); +export function checkStyleIsChanged({ + feature, + subFeature, + element, + subElement, + style, +}: ActionPayload): boolean { + const defaultStyle: StyleType = getDefaultStyle({ + feature, + subFeature, + element, + subElement, + }); const keys = Object.keys(defaultStyle) as StyleKeyType[]; const filteredKeys = keys.filter( - (key) => - key === StyleKeyType.isChanged || defaultStyle[key] === targetStyle[key] + (key) => key === StyleKeyType.isChanged || defaultStyle[key] === style[key] ); return keys.length !== filteredKeys.length; } diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts index f56d689..b835704 100644 --- a/src/store/style/getReducer.ts +++ b/src/store/style/getReducer.ts @@ -25,12 +25,11 @@ export default function getReducer(IDX: number): ReducerType { const initialState = subFeatures.reduce((acc: FeatureState, cur: string) => { acc[cur] = getDefaultFeature({ - featureType: renderingData[IDX].typeKey, - subFeatureType: cur, + feature: renderingData[IDX].typeKey, + subFeature: cur, }); return acc; }, {}); - return function reducer( state: FeatureState = initialState, action: ActionType @@ -46,10 +45,9 @@ export default function getReducer(IDX: number): ReducerType { subElement, style, } = action.payload; - if (feature !== renderingData[IDX].typeKey) return state; - style.isChanged = checkStyleIsChanged(style); + style.isChanged = checkStyleIsChanged(action.payload); const newState: FeatureState = JSON.parse(JSON.stringify(state)); const newFeature: FeatureType = newState[subFeature as string]; From e9b5a8779ac928e7ba0a802d3e0708f949745e81 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 00:24:05 +0900 Subject: [PATCH 096/138] =?UTF-8?q?[fix]=20=ED=83=80=EC=9E=85=20=EC=83=81?= =?UTF-8?q?=EC=86=8D=ED=95=98=EB=A9=B4=EC=84=9C=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=9C=20=EB=B6=80=EB=B6=84=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/common/type.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 9c5a48d..1b7dc90 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -65,8 +65,6 @@ export interface FeaturePropsType { } export interface ElementPropsType extends FeaturePropsType { - feature: FeatureNameType; - subFeature: string; element: ElementNameType; subElement?: SubElementNameType; } From 73769d0f2e44c68ce53854a16bbffe7481627cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 00:38:59 +0900 Subject: [PATCH 097/138] =?UTF-8?q?[fix]=20[#76]=20weight=EC=9D=98=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=EA=B0=80=20=EC=9E=98?= =?UTF-8?q?=EB=AA=BB=EB=90=98=EC=96=B4=EC=9E=88=EC=96=B4=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - !weight로 검사하면 0일때를 걸러내게 된다. = 1로 undefined일 경우를 처리하였다 --- src/utils/applyStyle.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index 42da7d0..15d41c4 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -86,9 +86,9 @@ export function applyWeight({ map, layerNames, type, - weight, + weight = 1, }: ApplyProps): void { - if (!type || !weight) return; + if (!type) return; const weightValue = weight === 0 ? 0 : weight * 2 + 1; layerNames.forEach((layerName) => { From 34d1340fb0ddb41500a5b50d2bb1d354738ac39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 00:40:12 +0900 Subject: [PATCH 098/138] =?UTF-8?q?[fix]=20compareStyle=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 순환할 떄 null이 넘어가게될 경우가 생긴다. 그 때를 검사해서 null이면 false를 반환하였다 --- src/store/style/compareStyle.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/store/style/compareStyle.ts b/src/store/style/compareStyle.ts index 25717e7..292dcde 100644 --- a/src/store/style/compareStyle.ts +++ b/src/store/style/compareStyle.ts @@ -39,6 +39,9 @@ export function checkStyleIsChanged({ } export function checkFeatureIsChanged(targetFeature: objType): boolean { + if (!targetFeature) { + return false; + } const keys = Object.keys(targetFeature); for (let i = 0; i < keys.length; i += 1) { if ( From 9c1e59fad0b142687c44a6758057c1836e5b4f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 00:40:45 +0900 Subject: [PATCH 099/138] =?UTF-8?q?[refactor]=20[#79]=20poi=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=9D=84=20=EC=A1=B0=EA=B8=88=20=EB=8D=94=20=EA=B0=84?= =?UTF-8?q?=EB=8B=A8=ED=95=98=EA=B2=8C=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20=ED=95=98=EC=98=80=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 타입스크릷트가 조금은 더 익숙해져서 필요없는 요소를 선언하지 않도록 고쳐보았다 --- src/utils/map-styling/poi.ts | 42 ++++++------------------------------ 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/src/utils/map-styling/poi.ts b/src/utils/map-styling/poi.ts index a7acf1f..9f371b2 100644 --- a/src/utils/map-styling/poi.ts +++ b/src/utils/map-styling/poi.ts @@ -86,16 +86,12 @@ const mappingDetailToFunc = { }, weight: { typeName: null, - funcName: () => null, + funcName: null, }, visibility: { typeName: 'what is my name?', funcName: applyVisibility, }, - isChanged: { - typeName: null, - funcName: () => null, - }, }, stroke: { color: { @@ -118,37 +114,13 @@ const mappingDetailToFunc = { typeName: WeightType.textHalo, funcName: applyWeight, }, - isChanged: { - typeName: null, - funcName: () => null, - }, }, }, labelIcon: { - color: { - typeName: null, - funcName: () => null, - }, - saturation: { - typeName: null, - funcName: () => null, - }, - lightness: { - typeName: null, - funcName: () => null, - }, - weight: { - typeName: null, - funcName: () => null, - }, visibility: { typeName: ColorType.icon, funcName: applyWeight, }, - isChanged: { - typeName: null, - funcName: () => null, - }, }, }; @@ -164,21 +136,19 @@ function poiStyling({ let func = null; if (detailName === ElementNameType.section) return; - if (detailName === ElementNameType.labelText) { + if (detailName === ElementNameType.labelText && key !== 'isChanged') { const { typeName, funcName } = mappingDetailToFunc[detailName][ subDetailName - ][key as StyleKeyType]; + ][key]; type = typeName; func = funcName; - } else { - const { typeName, funcName } = mappingDetailToFunc[detailName][ - key as StyleKeyType - ]; + } else if (detailName === ElementNameType.labelIcon && key === 'visibility') { + const { typeName, funcName } = mappingDetailToFunc[detailName][key]; type = typeName; func = funcName; } - if (!type) return; + if (!type || !func) return; if ( key === 'visibility' && (type === ColorType.icon || type === WeightType.textHalo) From 77f4af61e4052b048accabfe810f50805a7f95ec Mon Sep 17 00:00:00 2001 From: tejava Date: Thu, 3 Dec 2020 01:23:50 +0900 Subject: [PATCH 100/138] =?UTF-8?q?[fix]=20landscape=EC=9D=98=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 지도에서 지하철 등이 지나가는 경로가 빌딩에 가려지는 경우가 있습니다. 이를 방지하기 위해서 landscape를 보다 후순위로 조정하였습니다. --- src/store/map/initializeMap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/map/initializeMap.ts b/src/store/map/initializeMap.ts index fcdbcfa..4b4737b 100644 --- a/src/store/map/initializeMap.ts +++ b/src/store/map/initializeMap.ts @@ -84,9 +84,9 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { const layers = [ ...road, + ...landscape, ...transit, ...water, - ...landscape, ...mapboxPOI, ...poi, ] as mapboxgl.Layer[]; From 0b3b782a4868705ec0ff64930b4b4dd8c26303e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 01:32:45 +0900 Subject: [PATCH 101/138] =?UTF-8?q?[feat]=20[#72]=20=EC=83=89=EC=83=81?= =?UTF-8?q?=ED=8C=90=EB=93=A4=EC=97=90=EA=B2=8C=20=EB=8F=84=EB=A1=9C=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=EB=93=A4=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 색상판들에게 도로 레이어들을 추가하였다. --- src/utils/rendering-data/layerColor3.ts | 37 ++++++++++++++++++++- src/utils/rendering-data/layersColor.ts | 41 +++++++++++++++++++++--- src/utils/rendering-data/layersColor2.ts | 36 ++++++++++++++++++++- 3 files changed, 107 insertions(+), 7 deletions(-) diff --git a/src/utils/rendering-data/layerColor3.ts b/src/utils/rendering-data/layerColor3.ts index 34c54c9..0b65ba3 100644 --- a/src/utils/rendering-data/layerColor3.ts +++ b/src/utils/rendering-data/layerColor3.ts @@ -36,7 +36,7 @@ export default { 'water-point-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, 'waterway-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, 'mapbox-airport-aeroway-polygon': { - color: 'hsl(40, 97%, 64%)', + color: 'hsl(234, 20%, 30%)', type: 'fill', }, 'mapbox-airport-polygon': { color: 'hsl(234, 20%, 30%)', type: 'fill' }, @@ -46,4 +46,39 @@ export default { 'mapbox-rail-road-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, 'transit-rail-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, 'transit-subway-line': { color: 'hsl(192, 70%, 43%)', type: 'line' }, + 'road-arterial': 'hsl(0, 0%, 100%)', + 'road-arterial-polygon': { color: 'hsl(0, 0%, 100%)', type: 'fill' }, + 'road-primary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-secondary-teritary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-motorway-trunk': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-minor': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-minor-low': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-primary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-secondary-teritary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-motorway-trunk-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-minor-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-number-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, + 'road-exit-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, + 'road-arterial-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, + // + 'road-local-polygon': { color: 'hsl(35, 14%, 93%)', type: 'fill' }, + ferry: 'hsl(230, 48%, 44%)', + 'ferry-auto': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + 'road-local': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + 'road-street': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + 'road-street-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-local-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, + // + 'road-pedestrian-polygon-fill': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, + 'road-pedestrian-polygon-pattern': { + color: 'hsl(35, 10%, 83%)', + type: 'fill', + }, + 'road-sidewalk-polygon': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, + 'road-footway': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-pedestrian': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-steps': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-path': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-pedestrian-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-sidewalk-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, }; diff --git a/src/utils/rendering-data/layersColor.ts b/src/utils/rendering-data/layersColor.ts index 29a3a54..8f33b03 100644 --- a/src/utils/rendering-data/layersColor.ts +++ b/src/utils/rendering-data/layersColor.ts @@ -43,11 +43,42 @@ export default { }, }, road: { - highway: {}, - arterial: {}, - local: {}, - sidewalk: {}, - 'bicycle-road': {}, + arterial: { + 'road-arterial': 'hsl(0, 0%, 100%)', + 'road-arterial-polygon': 'hsl(0, 0%, 100%)', + 'road-primary': 'hsl(0, 0%, 100%)', + 'road-secondary-teritary': 'hsl(230, 24%, 87%)', + 'road-motorway-trunk': 'hsl(26, 100%, 78%)', + 'road-minor': 'hsl(0, 0%, 100%)', + 'road-minor-low': 'hsl(0, 0%, 100%)', + 'road-primary-case': 'hsl(230, 24%, 87%)', + 'road-secondary-teritary-case': 'hsl(230, 24%, 87%)', + 'road-motorway-trunk-case': 'hsl(0, 0%, 100%)', + 'road-minor-case': 'hsl(230, 24%, 87%)', + 'road-number-shield': 'hsl(0, 0%, 100%)', + 'road-exit-shield': 'hsl(0, 0%, 100%)', + 'road-arterial-label': 'hsl(230, 48%, 44%)', + }, + local: { + 'road-local-polygon': 'hsl(0, 0%, 100%)', + ferry: 'hsl(230, 48%, 44%)', + 'ferry-auto': 'hsl(230, 73%, 63%)', + 'road-local': 'hsl(230, 48%, 44%)', + 'road-street': 'hsl(35, 14%, 93%)', + 'road-street-case': 'hsl(230, 24%, 87%)', + 'road-local-label': 'hsl(230, 48%, 44%)', + }, + sidewalk: { + 'road-pedestrian-polygon-fill': 'hsl(35, 10%, 83%)', + 'road-pedestrian-polygon-pattern': 'hsl(35, 10%, 83%)', + 'road-sidewalk-polygon': 'hsl(0, 0%, 100%)', + 'road-footway': 'hsl(0, 0%, 100%)', + 'road-pedestrian': 'hsl(0, 0%, 100%)', + 'road-steps': 'hsl(0, 0%, 100%)', + 'road-path': 'hsl(0, 0%, 100%)', + 'road-pedestrian-case': 'hsl(230, 24%, 87%)', + 'road-sidewalk-label': 'hsl(230, 48%, 44%)', + }, }, administrative: { country: { diff --git a/src/utils/rendering-data/layersColor2.ts b/src/utils/rendering-data/layersColor2.ts index aa7c8a7..0472d74 100644 --- a/src/utils/rendering-data/layersColor2.ts +++ b/src/utils/rendering-data/layersColor2.ts @@ -83,6 +83,40 @@ export default { [SubElementNameType.fill]: 'transparent', [SubElementNameType.stroke]: 'transparent', }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + }, + arterial: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(0, 0%, 100%)', + [SubElementNameType.stroke]: 'hsl(230, 24%, 87%)', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(230, 48%, 44%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', + }, + }, + local: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(35, 14%, 93%)', + [SubElementNameType.stroke]: 'hsl(230, 24%, 87%)', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(230, 48%, 44%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', + }, + }, + sidewalk: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(0, 0%, 100%)', + [SubElementNameType.stroke]: 'hsl(230, 24%, 87%)', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(230, 48%, 44%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', + }, }, }, [FeatureNameType.administrative]: { @@ -144,7 +178,7 @@ export default { }, airport: { [ElementNameType.section]: { - [SubElementNameType.fill]: 'hsl(40, 97%, 64%)', + [SubElementNameType.fill]: 'hsl(234, 20%, 30%)', [SubElementNameType.stroke]: 'hsl(230, 23%, 82%)', }, [ElementNameType.labelText]: { From dcda167964ecba19e4ce7975676e9341d4860b22 Mon Sep 17 00:00:00 2001 From: tejava Date: Thu, 3 Dec 2020 01:36:43 +0900 Subject: [PATCH 102/138] =?UTF-8?q?[feat]=20landscape=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이전 구현 방식은 polygon만이 들어오는 가정을 했습니다. 기존 맵에서 line, label 등이 존재하는 것을 확인하여 수정하였습니다. #93 을 참고하여 작성, 일관성을 추구하고자 하였습니다. --- src/utils/map-styling/landscape.ts | 333 ++++++++++++++++++++++++----- 1 file changed, 275 insertions(+), 58 deletions(-) diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index 1964333..f6df5ff 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -1,63 +1,229 @@ import { stylingProps } from './index'; -import { applyColor, applyVisibility, ColorType } from '../applyStyle'; -import { StyleKeyType, StyleType } from '../../store/common/type'; +import { + applyColor, + applyWeight, + applyVisibility, + WeightType, + ColorType, + StyleTypes, +} from '../applyStyle'; +import { + StyleKeyType, + ElementNameType, + StyleType, +} from '../../store/common/type'; const SECTION = 'section'; -const FILL = 'fill'; +const LABELTEXT = 'labelText'; +const LABELICON = 'labelIcon'; + const ALL = 'all'; -const layers = ['human-made', 'building', 'natural', 'landcover']; +type LandscapeSubFeature = + | 'all' + | 'human-made' + | 'building' + | 'natural' + | 'landcover'; + +const humanMadeLayers: string[] = ['landscape-human-made']; + +const buildingLayers: string[] = [ + 'landscape-building', + 'building-outline', + 'building', +]; + +const naturalLayers: string[] = ['landscape-natural']; -interface applyStyleProps { - map: mapboxgl.Map; - subFeatureName: string; - key: StyleKeyType; - style: StyleType; +const landcoverLayers: string[] = ['landscape-landcover']; + +interface SubDetailType { + fill: string[]; + stroke: string[]; } -function applyLandscapeStyle({ - map, - subFeatureName, - key, - style, -}: applyStyleProps): void { - const layerNames = [`landscape-${subFeatureName}`]; - - switch (key) { - case 'color': - applyColor({ - map, - layerNames, - type: ColorType.fill, - color: style.color, - }); - break; - case 'visibility': - applyVisibility({ map, layerNames, visibility: style.visibility }); - break; - case 'lightness': - applyColor({ - map, - layerNames, - type: ColorType.fill, - color: style.color, - lightness: Number(style.lightness), - }); - break; - case 'saturation': - applyColor({ - map, - layerNames, - type: ColorType.fill, - color: style.color, - saturation: Number(style.saturation), - }); - break; - default: - break; - } +interface DetailType { + section: SubDetailType; + labelText: SubDetailType; + labelIcon: SubDetailType; } +function makeSubDetail( + fillLayers: string[], + strokeLayers: string[] +): SubDetailType { + return { fill: fillLayers, stroke: strokeLayers }; +} + +const layersByType: { [key in LandscapeSubFeature]: DetailType } = { + 'human-made': { + [SECTION]: makeSubDetail([], []), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], []), + }, + building: { + [SECTION]: makeSubDetail([], []), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], []), + }, + natural: { + [SECTION]: makeSubDetail([], []), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], []), + }, + landcover: { + [SECTION]: makeSubDetail([], []), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], []), + }, + all: { + [SECTION]: makeSubDetail([], []), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], []), + }, +}; + +const VISIBLE = 1; +const INVISIBLE = 0; + +const mappingDetailToFunc = { + labelText: { + fill: { + color: { + typeName: ColorType.text, + funcName: applyColor, + }, + saturation: { + typeName: ColorType.text, + funcName: applyColor, + }, + lightness: { + typeName: ColorType.text, + funcName: applyColor, + }, + weight: { + typeName: null, + funcName: () => null, + }, + visibility: { + typeName: 'what is my name?', + funcName: applyVisibility, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + stroke: { + color: { + typeName: ColorType.textHalo, + funcName: applyColor, + }, + saturation: { + typeName: ColorType.textHalo, + funcName: applyColor, + }, + lightness: { + typeName: ColorType.textHalo, + funcName: applyColor, + }, + weight: { + typeName: WeightType.textHalo, + funcName: applyWeight, + }, + visibility: { + typeName: WeightType.textHalo, + funcName: applyWeight, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + }, + labelIcon: { + color: { + typeName: null, + funcName: () => null, + }, + saturation: { + typeName: null, + funcName: () => null, + }, + lightness: { + typeName: null, + funcName: () => null, + }, + weight: { + typeName: null, + funcName: () => null, + }, + visibility: { + typeName: ColorType.icon, + funcName: applyWeight, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + section: { + fill: { + color: { + typeName: ColorType.fill, + funcName: applyColor, + }, + saturation: { + typeName: ColorType.fill, + funcName: applyColor, + }, + lightness: { + typeName: ColorType.fill, + funcName: applyColor, + }, + weight: { + typeName: null, + funcName: () => null, + }, + visibility: { + typeName: ColorType.fill, + funcName: applyVisibility, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + stroke: { + color: { + typeName: ColorType.line, + funcName: applyColor, + }, + saturation: { + typeName: ColorType.line, + funcName: applyColor, + }, + lightness: { + typeName: ColorType.line, + funcName: applyColor, + }, + weight: { + typeName: WeightType.line, + funcName: applyWeight, + }, + visibility: { + typeName: WeightType.line, + funcName: applyVisibility, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + }, +}; + function landscapeStyling({ map, subFeatureName, @@ -66,17 +232,68 @@ function landscapeStyling({ key, style, }: stylingProps): void { - if (detailName !== SECTION || subDetailName !== FILL) { - return; - } + let type = null; + let func = null; + + console.log(subFeatureName, detailName, subDetailName, key); - if (subFeatureName === ALL) { - layers.forEach((item) => - applyLandscapeStyle({ map, subFeatureName: item, key, style }) - ); + if ( + detailName === ElementNameType.section || + detailName === ElementNameType.labelText + ) { + const { typeName, funcName } = mappingDetailToFunc[detailName][ + subDetailName + ][key as StyleKeyType]; + type = typeName; + func = funcName; } else { - applyLandscapeStyle({ map, subFeatureName, key, style }); + const { typeName, funcName } = mappingDetailToFunc[detailName][ + key as StyleKeyType + ]; + type = typeName; + func = funcName; + } + + if (!type) { + return; + } + if ( + key === 'visibility' && + (type === ColorType.icon || type === WeightType.textHalo) + ) { + func({ + map, + layerNames: + layersByType[subFeatureName as LandscapeSubFeature][detailName][ + subDetailName + ], + type, + weight: style.visibility === 'none' ? INVISIBLE : VISIBLE, + }); + return; + } + if (key === 'visibility') { + console.log(func); + func({ + map, + layerNames: + layersByType[subFeatureName as LandscapeSubFeature][detailName][ + subDetailName + ], + visibility: style.visibility, + }); } + + func({ + map, + layerNames: + layersByType[subFeatureName as LandscapeSubFeature][detailName][ + subDetailName + ], + type: type as StyleTypes, + color: style.color, + [key]: style[key as StyleKeyType], + }); } export default landscapeStyling; From 6c66f971ef0a76f5ed9eb9c511c9598312561832 Mon Sep 17 00:00:00 2001 From: tejava Date: Thu, 3 Dec 2020 03:01:42 +0900 Subject: [PATCH 103/138] =?UTF-8?q?[feat]=20#83=20landscape=EC=9D=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 아직 일부 변하지 않는 부위가 존재합니다. ex. 지붕에 그어진 선으로 보이는 부분이 아직 색이 변하지 않습니다. 어느 부위인지 확인하기가 어려워 추후 논의하고 싶습니다. 텍스트도 랜드마크와 일부 겹치는 경우가 많아 합쳐서 확인해야 한다 생각이 듭니다. --- src/utils/map-styling/landscape.ts | 74 +++++++++++++++++++----------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index f6df5ff..0ebf009 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -17,26 +17,13 @@ const SECTION = 'section'; const LABELTEXT = 'labelText'; const LABELICON = 'labelIcon'; -const ALL = 'all'; - type LandscapeSubFeature = | 'all' | 'human-made' | 'building' | 'natural' - | 'landcover'; - -const humanMadeLayers: string[] = ['landscape-human-made']; - -const buildingLayers: string[] = [ - 'landscape-building', - 'building-outline', - 'building', -]; - -const naturalLayers: string[] = ['landscape-natural']; - -const landcoverLayers: string[] = ['landscape-landcover']; + | 'landcover' + | 'mountain'; interface SubDetailType { fill: string[]; @@ -56,32 +43,70 @@ function makeSubDetail( return { fill: fillLayers, stroke: strokeLayers }; } +const humanMadeSectionFill = ['landscape-human-made']; +const humanMadeSectionStroke = ['pitch-outline']; + +const buildingSectionFill = ['landscape-building', 'building']; +const buildingSectionStroke = ['building-outline']; +const buildingLabel = ['building-number-label']; + +const naturalSectionFill = ['landscape-natural']; +const naturalLabel = ['natural-point-label']; + +const landCoverSectionFill = [ + 'landscape-landcover', + 'landcover', + 'landuse', + 'lang-structure-polygon', +]; +const landCoverSectionStroke = ['lang-structure-line']; + +const mountainCoverSectionFill = ['hillshade']; + const layersByType: { [key in LandscapeSubFeature]: DetailType } = { 'human-made': { - [SECTION]: makeSubDetail([], []), + [SECTION]: makeSubDetail(humanMadeSectionFill, humanMadeSectionStroke), [LABELTEXT]: makeSubDetail([], []), [LABELICON]: makeSubDetail([], []), }, building: { - [SECTION]: makeSubDetail([], []), + [SECTION]: makeSubDetail(buildingSectionFill, buildingSectionStroke), [LABELTEXT]: makeSubDetail([], []), - [LABELICON]: makeSubDetail([], []), + [LABELICON]: makeSubDetail(buildingLabel, buildingLabel), }, natural: { - [SECTION]: makeSubDetail([], []), + [SECTION]: makeSubDetail(naturalSectionFill, []), [LABELTEXT]: makeSubDetail([], []), - [LABELICON]: makeSubDetail([], []), + [LABELICON]: makeSubDetail(naturalLabel, naturalLabel), }, landcover: { - [SECTION]: makeSubDetail([], []), + [SECTION]: makeSubDetail(landCoverSectionFill, landCoverSectionStroke), [LABELTEXT]: makeSubDetail([], []), [LABELICON]: makeSubDetail([], []), }, - all: { - [SECTION]: makeSubDetail([], []), + mountain: { + [SECTION]: makeSubDetail(mountainCoverSectionFill, []), [LABELTEXT]: makeSubDetail([], []), [LABELICON]: makeSubDetail([], []), }, + all: { + [SECTION]: makeSubDetail( + [ + ...humanMadeSectionFill, + ...buildingSectionFill, + ...naturalSectionFill, + ...landCoverSectionFill, + ...mountainCoverSectionFill, + ], + [ + ...humanMadeSectionStroke, + ...buildingSectionStroke, + ...landCoverSectionStroke, + ] + ), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], [...buildingLabel, ...naturalLabel]), + }, }; const VISIBLE = 1; @@ -235,8 +260,6 @@ function landscapeStyling({ let type = null; let func = null; - console.log(subFeatureName, detailName, subDetailName, key); - if ( detailName === ElementNameType.section || detailName === ElementNameType.labelText @@ -273,7 +296,6 @@ function landscapeStyling({ return; } if (key === 'visibility') { - console.log(func); func({ map, layerNames: From 03b82ca9dd210cf73f1a87e154ce90ad7d98b14c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 10:30:54 +0900 Subject: [PATCH 104/138] =?UTF-8?q?[feat]=20layers=EC=83=89=EC=83=81=20?= =?UTF-8?q?=ED=8C=94=EB=A0=88=ED=8A=B8=EC=97=90=20=EA=B2=BD=EA=B4=80=20?= =?UTF-8?q?=EC=83=89=EC=83=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - layers색상 팔레트에 경관 색상을 추가하였다 --- src/utils/rendering-data/layerColor3.ts | 14 ++++++ src/utils/rendering-data/layersColor.ts | 29 ++++++++++--- src/utils/rendering-data/layersColor2.ts | 54 ++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/utils/rendering-data/layerColor3.ts b/src/utils/rendering-data/layerColor3.ts index 0b65ba3..89aba20 100644 --- a/src/utils/rendering-data/layerColor3.ts +++ b/src/utils/rendering-data/layerColor3.ts @@ -81,4 +81,18 @@ export default { 'road-path': { color: 'hsl(35, 10%, 83%)', type: 'line' }, 'road-pedestrian-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, 'road-sidewalk-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, + 'landscape-human-made': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + 'pitch-outline': { color: 'hsl(75, 57%, 84%)', type: 'fill' }, + 'landscape-building': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + building: { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + 'building-outline': { color: 'hsl(35, 6%, 79%)', type: 'line' }, + 'building-number-label': { color: 'hsl(35, 2%, 69%)', type: 'text' }, + 'landscape-natural': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + 'natural-point-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'landscape-landcover': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + landcover: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + landuse: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + 'land-structure-polygon': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + 'land-structure-line': { color: 'hsl(35, 12%, 89%)', type: 'line' }, + hillshade: { color: 'hsl(56, 59%, 22%)', type: 'fill' }, }; diff --git a/src/utils/rendering-data/layersColor.ts b/src/utils/rendering-data/layersColor.ts index 8f33b03..a5a2629 100644 --- a/src/utils/rendering-data/layersColor.ts +++ b/src/utils/rendering-data/layersColor.ts @@ -98,11 +98,30 @@ export default { }, }, landscape: { - 'human-made': {}, - building: {}, - natural: {}, - landcover: {}, - mountain: {}, + 'human-made': { + 'landscape-human-made': 'hsl(35, 11%, 86%)', + 'pitch-outline': 'hsl(75, 57%, 84%)', + }, + building: { + 'landscape-building': 'hsl(35, 11%, 86%)', + building: 'hsl(35, 11%, 86%)', + 'building-outline': 'hsl(35, 6%, 79%)', + 'building-number-label': 'hsl(35, 2%, 69%)', + }, + natural: { + 'landscape-natural': 'hsl(75, 62%, 81%)', + 'natural-point-label': 'hsl(26, 25%, 32%)', + }, + landcover: { + 'landscape-landcover': 'hsl(75, 62%, 81%)', + landcover: 'hsl(75, 62%, 81%)', + landuse: 'hsl(100, 58%, 76%)', + 'land-structure-polygon': 'hsl(35, 12%, 89%)', + 'land-structure-line': 'hsl(35, 12%, 89%)', + }, + mountain: { + hillshade: 'hsl(56, 59%, 22%)', + }, }, transit: { airport: { diff --git a/src/utils/rendering-data/layersColor2.ts b/src/utils/rendering-data/layersColor2.ts index 0472d74..67489bf 100644 --- a/src/utils/rendering-data/layersColor2.ts +++ b/src/utils/rendering-data/layersColor2.ts @@ -163,6 +163,60 @@ export default { [SubElementNameType.fill]: 'transparent', [SubElementNameType.stroke]: 'transparent', }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + }, + 'human-made': { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(35, 11%, 86%)', + [SubElementNameType.stroke]: 'hsl(75, 57%, 84%)', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + }, + building: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(35, 11%, 86%)', + [SubElementNameType.stroke]: 'hsl(35, 6%, 79%)', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(35, 2%, 69%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', + }, + }, + natural: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(75, 62%, 81%)', + [SubElementNameType.stroke]: 'transparent', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'hsl(26, 25%, 32%)', + [SubElementNameType.stroke]: 'hsl(0, 0%, 100%)', + }, + }, + landcover: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(75, 62%, 81%)', + [SubElementNameType.stroke]: 'hsl(35, 12%, 89%)', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, + }, + mountain: { + [ElementNameType.section]: { + [SubElementNameType.fill]: 'hsl(56, 59%, 22%)', + [SubElementNameType.stroke]: 'transparent', + }, + [ElementNameType.labelText]: { + [SubElementNameType.fill]: 'transparent', + [SubElementNameType.stroke]: 'transparent', + }, }, }, [FeatureNameType.transit]: { From 3bcb4a69365f64d59c3b49f954a6075bd0b403f1 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 10:59:07 +0900 Subject: [PATCH 105/138] =?UTF-8?q?[fix]=20=EB=B8=8C=EB=9E=9C=EC=B9=9C=20?= =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sidebar/SidebarContentMore/DetailType.tsx | 100 +++--- src/store/map/initializeMap.ts | 11 +- src/store/map/layers/road.ts | 257 ++++++++++++++ src/store/map/layers/transit.ts | 123 ++++++- src/store/style/compareStyle.ts | 3 + src/store/style/properties.ts | 3 +- src/utils/applyStyle.ts | 4 +- src/utils/colorFormat.ts | 53 +++ src/utils/map-styling/landscape.ts | 333 ++++++++++++++++-- src/utils/map-styling/poi.ts | 42 +-- .../map-styling/road-categories/arterial.ts | 30 ++ .../map-styling/road-categories/local.ts | 12 + .../map-styling/road-categories/sidewalk.ts | 16 + .../road-categories/stylingCategory.ts | 158 +++++++++ src/utils/map-styling/road.ts | 132 ++----- src/utils/map-styling/transit.ts | 132 +++---- src/utils/rendering-data/featureTypeData.ts | 1 - src/utils/rendering-data/layerColor3.ts | 98 ++++++ src/utils/rendering-data/layersColor.ts | 109 +++--- 19 files changed, 1227 insertions(+), 390 deletions(-) create mode 100644 src/utils/map-styling/road-categories/arterial.ts create mode 100644 src/utils/map-styling/road-categories/local.ts create mode 100644 src/utils/map-styling/road-categories/sidewalk.ts create mode 100644 src/utils/map-styling/road-categories/stylingCategory.ts create mode 100644 src/utils/rendering-data/layerColor3.ts diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 5fe32fb..edda5c2 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -136,61 +136,67 @@ function DetailType({ ) : ( <> )} - {labelText ? ( - - 라벨 - - 텍스트 - {labelText?.fill.isChanged ? : <>} - { - styleClickHandler( + + {labelText ? ( + <> + 라벨 + + 텍스트 + {labelText?.fill.isChanged ? : <>} + - {labelText?.stroke.isChanged ? : <>} - { + styleClickHandler( + ElementNameType.labelText, + SubElementNameType.fill + ); + }} + name="채우기" + /> + {labelText?.stroke.isChanged ? ( + + ) : ( + <> )} - padding="third" - clickHandler={() => { - styleClickHandler( + - - {labelIcon ? ( - <> - {labelIcon?.isChanged ? : <>} - { - styleClickHandler(ElementNameType.labelIcon); + styleClickHandler( + ElementNameType.labelText, + SubElementNameType.stroke + ); }} - name="아이콘" + name="윤곽선" /> - - ) : ( - <> - )} - - ) : ( - <> - )} + + + ) : ( + <> + )} + {labelIcon ? ( + <> + {labelIcon.isChanged ? : <>} + { + styleClickHandler(ElementNameType.labelIcon); + }} + name="아이콘" + /> + + ) : ( + <> + )} + { translate(map); map.removeLayer('poi-label'); + map.removeLayer('road-label'); + map.removeLayer('road-polygon'); map.addSource(Sources.polygon, { type: 'vector', @@ -84,13 +87,17 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { const layers = [ ...road, + ...landscape, ...transit, ...water, - ...landscape, ...mapboxPOI, ...poi, ] as mapboxgl.Layer[]; layers.forEach((layer: mapboxgl.Layer) => map.addLayer(layer)); + + Object.entries(initColor).forEach(([key, value]) => { + map.setPaintProperty(key, `${value.type}-color`, value.color); + }); }); return map; diff --git a/src/store/map/layers/road.ts b/src/store/map/layers/road.ts index e1ea102..bc4d19a 100644 --- a/src/store/map/layers/road.ts +++ b/src/store/map/layers/road.ts @@ -66,4 +66,261 @@ export default [ filter: ['match', ['get', 'type'], ['pedestrian', 'footway'], true, false], id: 'road-footway', }, + { + id: 'road-arterial-label', + type: 'symbol', + source: 'composite', + 'source-layer': 'road', + metadata: {}, + minzoom: 10, + filter: [ + 'step', + ['zoom'], + [ + 'match', + ['get', 'class'], + ['motorway', 'trunk', 'primary', 'secondary', 'tertiary'], + true, + false, + ], + 12, + [ + 'match', + ['get', 'class'], + ['motorway', 'trunk', 'primary', 'secondary', 'tertiary'], + true, + false, + ], + ], + layout: { + 'text-size': [ + 'interpolate', + ['linear'], + ['zoom'], + 10, + [ + 'match', + ['get', 'class'], + ['motorway', 'trunk', 'primary', 'secondary', 'tertiary'], + 10, + [ + 'motorway_link', + 'trunk_link', + 'primary_link', + 'secondary_link', + 'tertiary_link', + ], + 9, + 6.5, + ], + 18, + [ + 'match', + ['get', 'class'], + ['motorway', 'trunk', 'primary', 'secondary', 'tertiary'], + 16, + [ + 'motorway_link', + 'trunk_link', + 'primary_link', + 'secondary_link', + 'tertiary_link', + ], + 14, + 13, + ], + ], + 'text-max-angle': 30, + 'text-font': ['DIN Offc Pro Regular', 'Arial Unicode MS Regular'], + 'symbol-placement': 'line', + 'text-padding': 1, + 'text-rotation-alignment': 'map', + 'text-pitch-alignment': 'viewport', + 'text-field': ['coalesce', ['get', 'name_en'], ['get', 'name']], + 'text-letter-spacing': 0.01, + }, + paint: { + 'text-color': [ + 'match', + ['get', 'class'], + 'ferry', + 'hsl(230, 48%, 44%)', + 'hsl(0, 0%, 0%)', + ], + 'text-halo-color': [ + 'match', + ['get', 'class'], + ['motorway', 'trunk'], + 'hsla(0, 0%, 100%, 0.75)', + 'hsl(0, 0%, 100%)', + ], + 'text-halo-width': 1, + 'text-halo-blur': 1, + }, + }, + { + id: 'road-local-label', + type: 'symbol', + source: 'composite', + 'source-layer': 'road', + metadata: {}, + minzoom: 10, + filter: ['match', ['get', 'class'], ['street'], true, false], + layout: { + 'text-size': [ + 'interpolate', + ['linear'], + ['zoom'], + 10, + ['match', ['get', 'class'], ['street'], 9, 6.5], + 18, + ['match', ['get', 'class'], ['street'], 14, 13], + ], + 'text-max-angle': 30, + 'text-font': ['DIN Offc Pro Regular', 'Arial Unicode MS Regular'], + 'symbol-placement': 'line', + 'text-padding': 1, + 'text-rotation-alignment': 'map', + 'text-pitch-alignment': 'viewport', + 'text-field': ['coalesce', ['get', 'name_en'], ['get', 'name']], + 'text-letter-spacing': 0.01, + }, + paint: { + 'text-color': [ + 'match', + ['get', 'class'], + 'ferry', + 'hsl(230, 48%, 44%)', + 'hsl(0, 0%, 0%)', + ], + 'text-halo-color': [ + 'match', + ['get', 'class'], + ['ferry'], + 'hsla(0, 0%, 100%, 0.75)', + 'hsl(0, 0%, 100%)', + ], + 'text-halo-width': 1, + 'text-halo-blur': 1, + }, + }, + { + id: 'road-sidewalk-label', + type: 'symbol', + source: 'composite', + 'source-layer': 'road', + metadata: {}, + minzoom: 10, + filter: [ + 'match', + ['get', 'class'], + ['pedestrian', 'street_limited'], + true, + false, + ], + layout: { + 'text-size': [ + 'interpolate', + ['linear'], + ['zoom'], + 10, + ['match', ['get', 'class'], ['pedestrian', 'street_limited'], 9, 6.5], + 18, + ['match', ['get', 'class'], ['pedestrian', 'street_limited'], 14, 13], + ], + 'text-max-angle': 30, + 'text-font': ['DIN Offc Pro Regular', 'Arial Unicode MS Regular'], + 'symbol-placement': 'line', + 'text-padding': 1, + 'text-rotation-alignment': 'map', + 'text-pitch-alignment': 'viewport', + 'text-field': ['coalesce', ['get', 'name_en'], ['get', 'name']], + 'text-letter-spacing': 0.01, + }, + paint: { + 'text-color': [ + 'match', + ['get', 'class'], + 'ferry', + 'hsl(230, 48%, 44%)', + 'hsl(0, 0%, 0%)', + ], + 'text-halo-color': [ + 'match', + ['get', 'class'], + ['motorway', 'trunk'], + 'hsla(0, 0%, 100%, 0.75)', + 'ferry', + 'hsl(196, 80%, 70%)', + 'hsl(0, 0%, 100%)', + ], + 'text-halo-width': 1, + 'text-halo-blur': 1, + }, + }, + { + id: 'road-arterial-polygon', + type: 'fill', + source: 'composite', + 'source-layer': 'road', + metadata: { 'mapbox:group': '1444855786460.0557' }, + minzoom: 12, + filter: [ + 'all', + ['==', ['geometry-type'], 'Polygon'], + [ + 'match', + ['get', 'class'], + [ + 'primary', + 'secondary', + 'tertiary', + 'primary_link', + 'secondary_link', + 'tertiary_link', + ], + true, + false, + ], + ['match', ['get', 'structure'], ['none', 'ford'], true, false], + ], + paint: { + 'fill-color': 'hsl(0, 0%, 100%)', + 'fill-outline-color': '#d6d9e6', + }, + }, + { + id: 'road-local-polygon', + type: 'fill', + source: 'composite', + 'source-layer': 'road', + metadata: { 'mapbox:group': '1444855786460.0557' }, + minzoom: 12, + filter: [ + 'all', + ['==', ['geometry-type'], 'Polygon'], + ['match', ['get', 'class'], ['street'], true, false], + ], + paint: { + 'fill-color': 'hsl(0, 0%, 100%)', + 'fill-outline-color': '#d6d9e6', + }, + }, + { + id: 'road-sidewalk-polygon', + type: 'fill', + source: 'composite', + 'source-layer': 'road', + metadata: { 'mapbox:group': '1444855786460.0557' }, + minzoom: 12, + filter: [ + 'all', + ['==', ['geometry-type'], 'Polygon'], + ['match', ['get', 'class'], ['street_limited'], true, false], + ], + paint: { + 'fill-color': 'hsl(0, 0%, 100%)', + 'fill-outline-color': '#d6d9e6', + }, + }, ]; diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts index 18a3ae4..93d66ee 100644 --- a/src/store/map/layers/transit.ts +++ b/src/store/map/layers/transit.ts @@ -1,5 +1,6 @@ export default [ { + id: 'mapbox-airport-polygon', type: 'fill', source: 'composite', 'source-layer': 'landuse', @@ -7,12 +8,84 @@ export default [ visibility: 'visible', }, paint: { - 'fill-color': 'hsl(230, 100%, 44%)', + 'fill-color': 'hsl(234, 20%, 30%)', }, - filter: ['in', 'class', 'airport'], - id: 'transit-airport', + filter: ['==', ['get', 'class'], 'airport'], }, { + id: 'mapbox-airport-aeroway-polygon', + type: 'fill', + metadata: {}, + source: 'composite', + 'source-layer': 'aeroway', + minzoom: 11, + filter: [ + 'all', + ['==', ['geometry-type'], 'Polygon'], + ['match', ['get', 'type'], ['runway', 'taxiway', 'helipad'], true, false], + ], + layout: {}, + paint: { + 'fill-color': 'hsl(40, 97%, 64%)', + }, + }, + { + id: 'mapbox-airport-aeroway-line', + type: 'line', + metadata: {}, + source: 'composite', + 'source-layer': 'aeroway', + minzoom: 9, + filter: ['==', ['geometry-type'], 'LineString'], + layout: {}, + paint: { + 'line-color': 'hsl(230, 23%, 82%)', + 'line-width': [ + 'interpolate', + ['exponential', 1.5], + ['zoom'], + 9, + ['match', ['get', 'type'], 'runway', 1, 'taxiway', 0.5, 0.5], + 18, + ['match', ['get', 'type'], 'runway', 80, 'taxiway', 20, 20], + ], + }, + }, + { + id: 'mapbox-airport-label', + type: 'symbol', + source: 'composite', + 'source-layer': 'airport_label', + minzoom: 8, + layout: { + 'text-size': 12, + 'icon-image': [ + 'step', + ['get', 'sizerank'], + ['concat', ['get', 'maki'], '-15'], + 12, + ['concat', ['get', 'maki'], '-11'], + ], + 'text-offset': [0, 0.75], + 'text-rotation-alignment': 'viewport', + 'text-anchor': 'top', + 'text-field': [ + 'step', + ['get', 'sizerank'], + ['coalesce', ['get', 'name_en'], ['get', 'name']], + 15, + ['get', 'ref'], + ], + 'text-max-width': 9, + }, + paint: { + 'text-color': 'hsl(0, 69%, 50%)', + 'text-halo-color': 'hsl(0, 0%, 100%)', + 'text-halo-width': 1, + }, + }, + { + id: 'transit-subway-line', type: 'line', source: 'line_source', 'source-layer': 'line', @@ -20,12 +93,13 @@ export default [ visibility: 'visible', }, paint: { - 'line-color': 'yellow', + 'line-color': 'hsl(192, 70%, 43%)', + 'line-width': 2, }, - filter: ['in', 'type', 'subway'], - id: 'transit-subway', + filter: ['==', ['get', 'type'], 'subway'], }, { + id: 'transit-rail-line', type: 'line', source: 'line_source', 'source-layer': 'line', @@ -33,12 +107,34 @@ export default [ visibility: 'visible', }, paint: { - 'line-color': 'black', + 'line-color': 'hsl(201, 100%, 14%)', + 'line-width': 2, + }, + filter: ['match', ['get', 'type'], 'rail'], + }, + { + id: 'mapbox-rail-road-line', + type: 'line', + source: 'composite', + 'source-layer': 'road', + layout: { + visibility: 'visible', + 'line-join': 'round', + }, + minzoom: 13, + + paint: { + 'line-color': 'hsl(234, 20%, 30%)', + 'line-width': 6, }, - filter: ['in', 'type', 'rail'], - id: 'transit-rail', + filter: [ + 'all', + ['match', ['get', 'class'], ['major_rail', 'minor_rail'], true, false], + ['match', ['get', 'structure'], ['none', 'ford'], true, false], + ], }, { + id: 'transit-bus-label', type: 'symbol', source: 'poi_source', 'source-layer': 'poi', @@ -48,11 +144,10 @@ export default [ visibility: 'visible', }, paint: { - 'text-halo-color': 'green', - 'text-halo-width': 0.5, - 'text-color': 'red', + 'text-halo-color': 'hsl(151, 24%, 60%)', + 'text-halo-width': 1, + 'text-color': 'hsl(13, 68%, 63%)', }, - filter: ['in', 'type', 'bus_stop'], - id: 'transit-bus-label', + filter: ['==', ['get', 'type'], 'bus_stop'], }, ]; diff --git a/src/store/style/compareStyle.ts b/src/store/style/compareStyle.ts index 5d7cd21..b675fb6 100644 --- a/src/store/style/compareStyle.ts +++ b/src/store/style/compareStyle.ts @@ -28,6 +28,9 @@ export function checkStyleIsChanged({ } export function checkFeatureIsChanged(targetFeature: objType): boolean { + if (!targetFeature) { + return false; + } const keys = Object.keys(targetFeature); for (let i = 0; i < keys.length; i += 1) { if ( diff --git a/src/store/style/properties.ts b/src/store/style/properties.ts index 3a8892c..5574b5a 100644 --- a/src/store/style/properties.ts +++ b/src/store/style/properties.ts @@ -7,6 +7,7 @@ import { ElementPropsType, StylePropsType, } from '../common/type'; +import { hslToHEX } from '../../utils/colorFormat'; import defaultStyle from '../../utils/rendering-data/layersColor'; @@ -30,7 +31,7 @@ export const getDefaultStyle = ({ : defaultStyle[feature][subFeature][element]; return { ...JSON.parse(JSON.stringify(style)), - color: defaultColor, + color: defaultColor ? hslToHEX(defaultColor as string) : '#000000', }; }; diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index 42da7d0..15d41c4 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -86,9 +86,9 @@ export function applyWeight({ map, layerNames, type, - weight, + weight = 1, }: ApplyProps): void { - if (!type || !weight) return; + if (!type) return; const weightValue = weight === 0 ? 0 : weight * 2 + 1; layerNames.forEach((layerName) => { diff --git a/src/utils/colorFormat.ts b/src/utils/colorFormat.ts index 9c4166f..8cc4596 100644 --- a/src/utils/colorFormat.ts +++ b/src/utils/colorFormat.ts @@ -40,3 +40,56 @@ export function hexToHSL(color: string): HexToHSLType { l, }; } + +export function hslToHEX(color: string): string { + if (color === 'transparent') return '#000000'; + const hsl = color.match(/\d+/g)?.map((c) => Number(c)) as number[]; + + const h: number = hsl[0]; + const s: number = hsl[1] / 100; + const l: number = hsl[2] / 100; + + const c = (1 - Math.abs(2 * l - 1)) * s; + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)); + const m = l - c / 2; + let r = 0; + let g = 0; + let b = 0; + + if (h >= 0 && h < 60) { + r = c; + g = x; + b = 0; + } else if (h >= 60 && h < 120) { + r = x; + g = c; + b = 0; + } else if (h >= 120 && h < 180) { + r = 0; + g = c; + b = x; + } else if (h >= 180 && h < 240) { + r = 0; + g = x; + b = c; + } else if (h >= 240 && h < 300) { + r = x; + g = 0; + b = c; + } else if (h >= 300 && h < 360) { + r = c; + g = 0; + b = x; + } + // Having obtained RGB, convert channels to hex + let stringR: string = Math.round((r + m) * 255).toString(16); + let stringG: string = Math.round((g + m) * 255).toString(16); + let stringB: string = Math.round((b + m) * 255).toString(16); + + // Prepend 0s, if necessary + if (stringR.length === 1) stringR = `0${stringR}`; + if (stringG.length === 1) stringG = `0${stringG}`; + if (stringB.length === 1) stringB = `0${stringB}`; + + return `#${stringR}${stringG}${stringB}`; +} diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index 48ee551..0ebf009 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -1,58 +1,321 @@ import { stylingProps } from './index'; -import { applyColor, applyVisibility, ColorType } from '../applyStyle'; -import { StyleType } from '../../store/common/type'; +import { + applyColor, + applyWeight, + applyVisibility, + WeightType, + ColorType, + StyleTypes, +} from '../applyStyle'; +import { + StyleKeyType, + ElementNameType, + StyleType, +} from '../../store/common/type'; const SECTION = 'section'; -const FILL = 'fill'; -const ALL = 'all'; +const LABELTEXT = 'labelText'; +const LABELICON = 'labelIcon'; -const layers = ['human-made', 'building', 'natural', 'landcover']; +type LandscapeSubFeature = + | 'all' + | 'human-made' + | 'building' + | 'natural' + | 'landcover' + | 'mountain'; -interface applyStyleProps { - map: mapboxgl.Map; - subFeatureName: string; - style: StyleType; +interface SubDetailType { + fill: string[]; + stroke: string[]; } -function applyLandscapeStyle({ map, subFeatureName, style }: applyStyleProps) { - const layerNames = [`landscape-${subFeatureName}`]; +interface DetailType { + section: SubDetailType; + labelText: SubDetailType; + labelIcon: SubDetailType; +} - applyColor({ map, layerNames, type: ColorType.fill, color: style.color }); - applyVisibility({ map, layerNames, visibility: style.visibility }); - applyColor({ - map, - layerNames, - type: ColorType.fill, - color: style.color, - lightness: Number(style.lightness), - }); - applyColor({ - map, - layerNames, - type: ColorType.fill, - color: style.color, - saturation: Number(style.saturation), - }); +function makeSubDetail( + fillLayers: string[], + strokeLayers: string[] +): SubDetailType { + return { fill: fillLayers, stroke: strokeLayers }; } +const humanMadeSectionFill = ['landscape-human-made']; +const humanMadeSectionStroke = ['pitch-outline']; + +const buildingSectionFill = ['landscape-building', 'building']; +const buildingSectionStroke = ['building-outline']; +const buildingLabel = ['building-number-label']; + +const naturalSectionFill = ['landscape-natural']; +const naturalLabel = ['natural-point-label']; + +const landCoverSectionFill = [ + 'landscape-landcover', + 'landcover', + 'landuse', + 'lang-structure-polygon', +]; +const landCoverSectionStroke = ['lang-structure-line']; + +const mountainCoverSectionFill = ['hillshade']; + +const layersByType: { [key in LandscapeSubFeature]: DetailType } = { + 'human-made': { + [SECTION]: makeSubDetail(humanMadeSectionFill, humanMadeSectionStroke), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], []), + }, + building: { + [SECTION]: makeSubDetail(buildingSectionFill, buildingSectionStroke), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail(buildingLabel, buildingLabel), + }, + natural: { + [SECTION]: makeSubDetail(naturalSectionFill, []), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail(naturalLabel, naturalLabel), + }, + landcover: { + [SECTION]: makeSubDetail(landCoverSectionFill, landCoverSectionStroke), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], []), + }, + mountain: { + [SECTION]: makeSubDetail(mountainCoverSectionFill, []), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], []), + }, + all: { + [SECTION]: makeSubDetail( + [ + ...humanMadeSectionFill, + ...buildingSectionFill, + ...naturalSectionFill, + ...landCoverSectionFill, + ...mountainCoverSectionFill, + ], + [ + ...humanMadeSectionStroke, + ...buildingSectionStroke, + ...landCoverSectionStroke, + ] + ), + [LABELTEXT]: makeSubDetail([], []), + [LABELICON]: makeSubDetail([], [...buildingLabel, ...naturalLabel]), + }, +}; + +const VISIBLE = 1; +const INVISIBLE = 0; + +const mappingDetailToFunc = { + labelText: { + fill: { + color: { + typeName: ColorType.text, + funcName: applyColor, + }, + saturation: { + typeName: ColorType.text, + funcName: applyColor, + }, + lightness: { + typeName: ColorType.text, + funcName: applyColor, + }, + weight: { + typeName: null, + funcName: () => null, + }, + visibility: { + typeName: 'what is my name?', + funcName: applyVisibility, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + stroke: { + color: { + typeName: ColorType.textHalo, + funcName: applyColor, + }, + saturation: { + typeName: ColorType.textHalo, + funcName: applyColor, + }, + lightness: { + typeName: ColorType.textHalo, + funcName: applyColor, + }, + weight: { + typeName: WeightType.textHalo, + funcName: applyWeight, + }, + visibility: { + typeName: WeightType.textHalo, + funcName: applyWeight, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + }, + labelIcon: { + color: { + typeName: null, + funcName: () => null, + }, + saturation: { + typeName: null, + funcName: () => null, + }, + lightness: { + typeName: null, + funcName: () => null, + }, + weight: { + typeName: null, + funcName: () => null, + }, + visibility: { + typeName: ColorType.icon, + funcName: applyWeight, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + section: { + fill: { + color: { + typeName: ColorType.fill, + funcName: applyColor, + }, + saturation: { + typeName: ColorType.fill, + funcName: applyColor, + }, + lightness: { + typeName: ColorType.fill, + funcName: applyColor, + }, + weight: { + typeName: null, + funcName: () => null, + }, + visibility: { + typeName: ColorType.fill, + funcName: applyVisibility, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + stroke: { + color: { + typeName: ColorType.line, + funcName: applyColor, + }, + saturation: { + typeName: ColorType.line, + funcName: applyColor, + }, + lightness: { + typeName: ColorType.line, + funcName: applyColor, + }, + weight: { + typeName: WeightType.line, + funcName: applyWeight, + }, + visibility: { + typeName: WeightType.line, + funcName: applyVisibility, + }, + isChanged: { + typeName: null, + funcName: () => null, + }, + }, + }, +}; + function landscapeStyling({ map, subFeatureName, detailName, subDetailName, + key, style, }: stylingProps): void { - if (detailName !== SECTION || subDetailName !== FILL) { - return; - } + let type = null; + let func = null; - if (subFeatureName === ALL) { - layers.forEach((item) => - applyLandscapeStyle({ map, subFeatureName: item, style }) - ); + if ( + detailName === ElementNameType.section || + detailName === ElementNameType.labelText + ) { + const { typeName, funcName } = mappingDetailToFunc[detailName][ + subDetailName + ][key as StyleKeyType]; + type = typeName; + func = funcName; } else { - applyLandscapeStyle({ map, subFeatureName, style }); + const { typeName, funcName } = mappingDetailToFunc[detailName][ + key as StyleKeyType + ]; + type = typeName; + func = funcName; } + + if (!type) { + return; + } + if ( + key === 'visibility' && + (type === ColorType.icon || type === WeightType.textHalo) + ) { + func({ + map, + layerNames: + layersByType[subFeatureName as LandscapeSubFeature][detailName][ + subDetailName + ], + type, + weight: style.visibility === 'none' ? INVISIBLE : VISIBLE, + }); + return; + } + if (key === 'visibility') { + func({ + map, + layerNames: + layersByType[subFeatureName as LandscapeSubFeature][detailName][ + subDetailName + ], + visibility: style.visibility, + }); + } + + func({ + map, + layerNames: + layersByType[subFeatureName as LandscapeSubFeature][detailName][ + subDetailName + ], + type: type as StyleTypes, + color: style.color, + [key]: style[key as StyleKeyType], + }); } export default landscapeStyling; diff --git a/src/utils/map-styling/poi.ts b/src/utils/map-styling/poi.ts index a7acf1f..9f371b2 100644 --- a/src/utils/map-styling/poi.ts +++ b/src/utils/map-styling/poi.ts @@ -86,16 +86,12 @@ const mappingDetailToFunc = { }, weight: { typeName: null, - funcName: () => null, + funcName: null, }, visibility: { typeName: 'what is my name?', funcName: applyVisibility, }, - isChanged: { - typeName: null, - funcName: () => null, - }, }, stroke: { color: { @@ -118,37 +114,13 @@ const mappingDetailToFunc = { typeName: WeightType.textHalo, funcName: applyWeight, }, - isChanged: { - typeName: null, - funcName: () => null, - }, }, }, labelIcon: { - color: { - typeName: null, - funcName: () => null, - }, - saturation: { - typeName: null, - funcName: () => null, - }, - lightness: { - typeName: null, - funcName: () => null, - }, - weight: { - typeName: null, - funcName: () => null, - }, visibility: { typeName: ColorType.icon, funcName: applyWeight, }, - isChanged: { - typeName: null, - funcName: () => null, - }, }, }; @@ -164,21 +136,19 @@ function poiStyling({ let func = null; if (detailName === ElementNameType.section) return; - if (detailName === ElementNameType.labelText) { + if (detailName === ElementNameType.labelText && key !== 'isChanged') { const { typeName, funcName } = mappingDetailToFunc[detailName][ subDetailName - ][key as StyleKeyType]; + ][key]; type = typeName; func = funcName; - } else { - const { typeName, funcName } = mappingDetailToFunc[detailName][ - key as StyleKeyType - ]; + } else if (detailName === ElementNameType.labelIcon && key === 'visibility') { + const { typeName, funcName } = mappingDetailToFunc[detailName][key]; type = typeName; func = funcName; } - if (!type) return; + if (!type || !func) return; if ( key === 'visibility' && (type === ColorType.icon || type === WeightType.textHalo) diff --git a/src/utils/map-styling/road-categories/arterial.ts b/src/utils/map-styling/road-categories/arterial.ts new file mode 100644 index 0000000..bf9f685 --- /dev/null +++ b/src/utils/map-styling/road-categories/arterial.ts @@ -0,0 +1,30 @@ +export const arterialLayerNames = { + all: [ + 'road-arterial', + 'road-number-shield', + 'road-exit-shield', + 'road-arterial-label', + ], + polygon: ['road-arterial-polygon'], + line: [ + 'road-arterial', + 'road-primary', + 'road-secondary-tertiary', + 'road-motorway-trunk', + 'road-minor', + 'road-minor-low', + ], + stroke: [ + 'road-primary-case', + 'road-secondary-tertiary-case', + 'road-motorway-trunk-case', + 'road-minor-case', + 'road-minor-low', + ], + text: { + all: ['road-number-shield', 'road-exit-shield', 'road-arterial-label'], + hasStroke: ['road-arterial-label'], + noStroke: ['road-number-shield', 'road-exit-shield'], + }, + icon: [], +}; diff --git a/src/utils/map-styling/road-categories/local.ts b/src/utils/map-styling/road-categories/local.ts new file mode 100644 index 0000000..617961f --- /dev/null +++ b/src/utils/map-styling/road-categories/local.ts @@ -0,0 +1,12 @@ +export const localLayerNames = { + all: ['ferry', 'ferry-auto', 'road-local', 'road-local-label'], + polygon: ['road-local-polygon'], + line: ['ferry', 'ferry-auto', 'road-local', 'road-street'], + stroke: ['road-street-case'], + text: { + all: ['road-local-label'], + hasStroke: ['road-local-label'], + noStroke: [], + }, + icon: [], +}; diff --git a/src/utils/map-styling/road-categories/sidewalk.ts b/src/utils/map-styling/road-categories/sidewalk.ts new file mode 100644 index 0000000..6137a5a --- /dev/null +++ b/src/utils/map-styling/road-categories/sidewalk.ts @@ -0,0 +1,16 @@ +export const sidewalkLayerNames = { + all: ['road-footway', 'road-sidewalk-label'], + polygon: [ + 'road-pedestrian-polygon-fill', + 'road-pedestrian-polygon-pattern', + 'road-sidewalk-polygon', + ], + line: ['road-footway', 'road-pedestrian', 'road-steps', 'road-path'], + stroke: ['road-pedestrian-case'], + text: { + all: ['road-sidewalk-label'], + hasStroke: ['road-sidewalk-label'], + noStroke: [], + }, + icon: [], +}; diff --git a/src/utils/map-styling/road-categories/stylingCategory.ts b/src/utils/map-styling/road-categories/stylingCategory.ts new file mode 100644 index 0000000..5e6b4f0 --- /dev/null +++ b/src/utils/map-styling/road-categories/stylingCategory.ts @@ -0,0 +1,158 @@ +import { + applyVisibility, + applyColor, + applyWeight, + ColorType, + WeightType, +} from '../../applyStyle'; +import mapboxgl from 'mapbox-gl'; +import { + ElementNameType, + SubElementNameType, + StyleKeyType, + StyleType, +} from '../../../store/common/type'; + +export interface layerProps { + all: string[]; + polygon: string[]; + line: string[]; + stroke: string[]; + text: { + all: string[]; + hasStroke: string[]; + noStroke: string[]; + }; + icon: string[]; +} + +export interface catergoryStylingProps { + map: mapboxgl.Map; + subFeatureName: string; + detailName: ElementNameType; + subDetailName: SubElementNameType; + key: StyleKeyType; + style: StyleType; + layerNames: layerProps; +} + +function stylingCategory({ + layerNames, + map, + subFeatureName, + detailName, + subDetailName, + key, + style, +}: catergoryStylingProps): void { + const { visibility, color, weight } = style; + + if (key === 'visibility') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyVisibility({ + map, + layerNames: layerNames.line, + visibility, + }); + applyVisibility({ + map, + layerNames: layerNames.polygon, + visibility, + }); + } else if (subDetailName === SubElementNameType.stroke) + applyVisibility({ + map, + layerNames: layerNames.stroke, + visibility, + }); + } else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) + applyVisibility({ + map, + layerNames: layerNames.text.noStroke, + visibility, + }); + else if (subDetailName === SubElementNameType.stroke) + applyVisibility({ + map, + layerNames: layerNames.text.hasStroke, + visibility, + }); + } + } else if (key === 'color' || key === 'saturation' || key === 'lightness') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyColor({ + map, + layerNames: layerNames.line, + color, + type: ColorType.line, + [key]: style[key as StyleKeyType], + }); + applyColor({ + map, + layerNames: layerNames.polygon, + color, + type: ColorType.fill, + [key]: style[key as StyleKeyType], + }); + } else if (subDetailName === SubElementNameType.stroke) { + applyColor({ + map, + layerNames: layerNames.stroke, + color, + type: ColorType.line, + [key]: style[key as StyleKeyType], + }); + } + } else if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.fill) { + applyColor({ + map, + layerNames: layerNames.text.all, + color, + type: ColorType.text, + [key]: style[key as StyleKeyType], + }); + } else if (subDetailName === SubElementNameType.stroke) { + applyColor({ + map, + layerNames: layerNames.text.hasStroke, + color, + type: ColorType.textHalo, + [key]: style[key as StyleKeyType], + }); + } + } + } else if (key === 'weight') { + if (detailName === ElementNameType.section) { + if (subDetailName === SubElementNameType.fill) { + applyWeight({ + map, + layerNames: layerNames.line, + type: WeightType.line, + weight, + }); + } else if (subDetailName === SubElementNameType.stroke) { + applyWeight({ + map, + layerNames: layerNames.stroke, + type: WeightType.line, + weight, + }); + } + } + if (detailName === ElementNameType.labelText) { + if (subDetailName === SubElementNameType.stroke) + applyWeight({ + map, + layerNames: layerNames.text.hasStroke, + type: WeightType.textHalo, + weight, + }); + } + } +} + +export default stylingCategory; diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index aaf310a..578e815 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -1,12 +1,8 @@ import { stylingProps } from '.'; -import { - applyVisibility, - applyColor, - applyWeight, - ColorType, - WeightType, -} from '../applyStyle'; -import { StyleKeyType } from '../../store/common/type'; +import { arterialLayerNames } from './road-categories/arterial'; +import { localLayerNames } from './road-categories/local'; +import { sidewalkLayerNames } from './road-categories/sidewalk'; +import stylingCategory from './road-categories/stylingCategory'; function roadStyling({ map, @@ -16,110 +12,24 @@ function roadStyling({ key, style, }: stylingProps): void { - const { visibility, color, weight } = style; - const arterialLayerNames = [ - 'road-number-shield', - 'road-exit-shield', - 'road-label', - 'road-arterial', - ]; - const localLayerNames = ['ferry', 'ferry-auto', 'road-local']; - const sidewalkLayerNames = ['road-footway']; + type RoadSubFeatureType = 'arterial' | 'local' | 'sidewalk'; - if (subFeatureName === 'arterial') { - if ( - detailName === 'section' && - subDetailName === 'fill' && - key === 'visibility' - ) { - applyVisibility({ map, layerNames: arterialLayerNames, visibility }); - } else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === 'section' && subDetailName === 'fill') { - applyColor({ - map, - layerNames: ['road-arterial'], - color, - type: ColorType.line, - [key]: style[key as StyleKeyType], - }); - } else if (detailName === 'labelText' && subDetailName === 'fill') { - applyColor({ - map, - layerNames: ['road-number-shield', 'road-exit-shield', 'road-label'], - color, - type: ColorType.text, - [key]: style[key as StyleKeyType], - }); - } else if (detailName === 'labelText' && subDetailName === 'stroke') { - applyColor({ - map, - layerNames: ['road-label'], - color, - type: ColorType.textHalo, - [key]: style[key as StyleKeyType], - }); - } - } else if (key === 'weight') { - if (detailName === 'section' && subDetailName === 'fill') { - applyWeight({ - map, - layerNames: ['road-arterial'], - type: WeightType.line, - weight, - }); - } - if (detailName === 'labelText' && subDetailName === 'stroke') - applyWeight({ - map, - layerNames: ['road-label'], - type: ColorType.textHalo, - weight, - }); - } - } else if (subFeatureName === 'local') { - if (key === 'visibility') - applyVisibility({ map, layerNames: localLayerNames, visibility }); - else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === 'section' && subDetailName === 'fill') { - applyColor({ - map, - layerNames: ['ferry', 'ferry-auto', 'road-local'], - color, - type: ColorType.line, - [key]: style[key as StyleKeyType], - }); - } - } else if (key === 'weight') { - if (detailName === 'section' && subDetailName === 'fill') - applyWeight({ - map, - layerNames: ['ferry', 'ferry-auto', 'road-local'], - type: WeightType.line, - weight, - }); - } - } else if (subFeatureName === 'sidewalk') { - if (key === 'visibility') - applyVisibility({ map, layerNames: sidewalkLayerNames, visibility }); - else if (key === 'weight') { - if (detailName === 'section' && subDetailName === 'fill') - applyWeight({ - map, - layerNames: sidewalkLayerNames, - type: WeightType.line, - weight, - }); - } else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === 'section' && subDetailName === 'fill') - applyColor({ - map, - layerNames: sidewalkLayerNames, - color, - type: ColorType.line, - [key]: style[key as StyleKeyType], - }); - } - } + const mappingSubFeatureLayerNames = { + arterial: arterialLayerNames, + local: localLayerNames, + sidewalk: sidewalkLayerNames, + }; + + stylingCategory({ + layerNames: + mappingSubFeatureLayerNames[subFeatureName as RoadSubFeatureType], + map, + subFeatureName, + detailName, + subDetailName, + key, + style, + }); } export default roadStyling; diff --git a/src/utils/map-styling/transit.ts b/src/utils/map-styling/transit.ts index 6f84c12..c0e9cd9 100644 --- a/src/utils/map-styling/transit.ts +++ b/src/utils/map-styling/transit.ts @@ -1,6 +1,7 @@ /* eslint-disable no-nested-ternary */ /* eslint-disable no-case-declarations */ import { stylingProps } from '.'; + import { StyleKeyType, ElementNameType, @@ -15,23 +16,17 @@ import { StyleTypes, } from '../../utils/applyStyle'; -const AirportLayerNames = [ - 'airport-label', - 'aeroway-polygon', - 'transit-airport', - 'aeroway-line', +const PolygonLayers = [ + 'mapbox-airport-aeroway-polygon', + 'mapbox-airport-polygon', ]; -const BusLayerNames = ['transit-bus-label']; -const RailLayerNames = ['transit-rail']; -const SubwayLayerNames = ['transit-subway', 'transit-subway-label']; - -const AllLayerTypeKeys = [ - ...AirportLayerNames, - ...BusLayerNames, - ...RailLayerNames, - ...SubwayLayerNames, - 'transit-label', +const LineLayers = [ + 'mapbox-airport-aeroway-line', + 'transit-subway-line', + 'transit-rail-line', + 'mapbox-rail-road-line', ]; +const LabelLayers = ['mapbox-airport-label', 'transit-bus-label']; function transitStyling({ map, @@ -41,71 +36,37 @@ function transitStyling({ subDetailName, style, }: stylingProps): void { - let layers: string[]; - switch (subFeatureName) { - case 'all': - layers = [...AllLayerTypeKeys]; - break; - case 'subway': - layers = [...SubwayLayerNames]; - break; - case 'bus': - layers = [...BusLayerNames]; - break; - case 'airport': - layers = [...AirportLayerNames]; - break; - case 'rail': - layers = [...RailLayerNames]; - break; - default: - return; - } + // TODO: labelIcon 관련 구현 + if (detailName === ElementNameType.labelIcon) return; - let layersByDetailName: string[]; - let styleType: StyleTypes; + let layerNames: string[] = + subDetailName === SubElementNameType.fill + ? key === StyleKeyType.weight + ? [] + : detailName === ElementNameType.labelText + ? [...LabelLayers] + : [...PolygonLayers] + : detailName === ElementNameType.labelText + ? [...LabelLayers] + : [...LineLayers]; - switch (detailName) { - case ElementNameType.section: - layersByDetailName = layers.filter((layer) => !layer.includes('label')); - styleType = - subDetailName === SubElementNameType.stroke - ? key === StyleKeyType.weight - ? WeightType.line - : ColorType.line - : ColorType.fill; - - break; - case ElementNameType.labelText: - layersByDetailName = layers.filter((layer) => layer.includes('label')); - styleType = - subDetailName === SubElementNameType.stroke - ? key === StyleKeyType.weight - ? WeightType.textHalo - : ColorType.textHalo - : ColorType.text; - break; - case ElementNameType.labelIcon: - layersByDetailName = layers.filter((layer) => !layer.includes('label')); - styleType = - subDetailName === SubElementNameType.stroke - ? ColorType.line - : ColorType.fill; - break; + layerNames = + subFeatureName === 'all' + ? layerNames + : layerNames.filter((layer) => layer.includes(subFeatureName)); - default: - return; - } + if (layerNames.length === 0) return; - const styleKey: StyleKeyType = key as StyleKeyType; - const { [styleKey]: value } = style; + const styleKey = key as StyleKeyType; + const { [styleKey]: styleValue } = style; + let styleType: StyleTypes; switch (styleKey) { case StyleKeyType.visibility: applyVisibility({ map, - layerNames: layersByDetailName, - visibility: value as string, + layerNames, + visibility: styleValue as string, }); break; @@ -116,6 +77,15 @@ function transitStyling({ const satKey = StyleKeyType.saturation; const ligKey = StyleKeyType.lightness; + styleType = + detailName === ElementNameType.labelText + ? subDetailName === SubElementNameType.fill + ? ColorType.text + : ColorType.textHalo + : subDetailName === SubElementNameType.fill + ? ColorType.fill + : ColorType.line; + const satureOrLight = key === 'saturation' ? { saturation: +style[satKey] } @@ -124,7 +94,7 @@ function transitStyling({ : {}; applyColor({ map, - layerNames: layersByDetailName, + layerNames, type: styleType, color: style[colorKey], ...satureOrLight, @@ -132,14 +102,16 @@ function transitStyling({ break; case StyleKeyType.weight: - if (subDetailName !== 'fill') { - applyWeight({ - map, - layerNames: layersByDetailName, - type: styleType as WeightType, - weight: value as number, - }); - } + styleType = + detailName === ElementNameType.labelText + ? WeightType.textHalo + : WeightType.line; + applyWeight({ + map, + layerNames, + type: styleType, + weight: styleValue as number, + }); break; diff --git a/src/utils/rendering-data/featureTypeData.ts b/src/utils/rendering-data/featureTypeData.ts index fb7a5d5..7e6a4f7 100644 --- a/src/utils/rendering-data/featureTypeData.ts +++ b/src/utils/rendering-data/featureTypeData.ts @@ -30,7 +30,6 @@ const data: DataType[] = [ typeKey: FeatureNameType.road, typeName: '도로', features: [ - { key: 'highway', name: '고속도로' }, { key: 'arterial', name: '주요도로' }, { key: 'local', name: '일반도로' }, { key: 'sidewalk', name: '인도' }, diff --git a/src/utils/rendering-data/layerColor3.ts b/src/utils/rendering-data/layerColor3.ts new file mode 100644 index 0000000..89aba20 --- /dev/null +++ b/src/utils/rendering-data/layerColor3.ts @@ -0,0 +1,98 @@ +export default { + 'poi-attraction': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-arts-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-landmark-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-business': { color: 'hsl(22, 55%, 55%)', type: 'text' }, + 'poi-food-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, + 'poi-store-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, + 'poi-government': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-public-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-general-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-medical': { color: 'hsl(340, 39%, 42%)', type: 'text' }, + 'poi-medical-label': { color: 'hsl(340, 39%, 42%)', type: 'text' }, + 'poi-park': { color: 'hsl(100, 45%, 37%)', type: 'text' }, + 'poi-park-label': { color: 'hsl(100, 45%, 37%)', type: 'text' }, + 'poi-worship': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-religion-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-school': { color: 'hsl(51, 40%, 40%)', type: 'text' }, + 'poi-education-label': { color: 'hsl(51, 40%, 40%)', type: 'text' }, + 'poi-sport-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-etc': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-industrial-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-historic-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-building-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'admin-0-boundary': { color: 'hsl(230, 8%, 51%)', type: 'line' }, + 'admin-0-boundary-bg': { color: 'hsl(230, 8%, 51%)', type: 'line' }, + 'admin-0-boundary-disputed': { color: 'hsl(230, 8%, 51%)', type: 'line' }, + 'country-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + 'admin-1-boundary-bg': { color: 'hsl(230, 14%, 77%)', type: 'line' }, + 'admin-1-boundary': { color: 'hsl(230, 14%, 77%)', type: 'line' }, + 'state-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + 'settlement-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + 'settlement-subdivision-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + water: { color: 'hsl(205, 87%, 76%)', type: 'fill' }, + 'water-polygon': { color: 'hsl(205, 87%, 76%)', type: 'fill' }, + 'water-line-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'water-point-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'waterway-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'mapbox-airport-aeroway-polygon': { + color: 'hsl(234, 20%, 30%)', + type: 'fill', + }, + 'mapbox-airport-polygon': { color: 'hsl(234, 20%, 30%)', type: 'fill' }, + 'mapbox-airport-aeroway-line': { color: 'hsl(230, 23%, 82%)', type: 'line' }, + 'mapbox-airport-label': { color: 'hsl(0, 69%, 50%)', type: 'text' }, + 'transit-bus-label': { color: 'hsl(13, 68%, 63%)', type: 'text' }, + 'mapbox-rail-road-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, + 'transit-rail-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, + 'transit-subway-line': { color: 'hsl(192, 70%, 43%)', type: 'line' }, + 'road-arterial': 'hsl(0, 0%, 100%)', + 'road-arterial-polygon': { color: 'hsl(0, 0%, 100%)', type: 'fill' }, + 'road-primary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-secondary-teritary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-motorway-trunk': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-minor': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-minor-low': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-primary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-secondary-teritary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-motorway-trunk-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-minor-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-number-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, + 'road-exit-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, + 'road-arterial-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, + // + 'road-local-polygon': { color: 'hsl(35, 14%, 93%)', type: 'fill' }, + ferry: 'hsl(230, 48%, 44%)', + 'ferry-auto': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + 'road-local': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + 'road-street': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + 'road-street-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-local-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, + // + 'road-pedestrian-polygon-fill': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, + 'road-pedestrian-polygon-pattern': { + color: 'hsl(35, 10%, 83%)', + type: 'fill', + }, + 'road-sidewalk-polygon': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, + 'road-footway': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-pedestrian': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-steps': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-path': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-pedestrian-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-sidewalk-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, + 'landscape-human-made': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + 'pitch-outline': { color: 'hsl(75, 57%, 84%)', type: 'fill' }, + 'landscape-building': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + building: { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + 'building-outline': { color: 'hsl(35, 6%, 79%)', type: 'line' }, + 'building-number-label': { color: 'hsl(35, 2%, 69%)', type: 'text' }, + 'landscape-natural': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + 'natural-point-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'landscape-landcover': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + landcover: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + landuse: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + 'land-structure-polygon': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + 'land-structure-line': { color: 'hsl(35, 12%, 89%)', type: 'line' }, + hillshade: { color: 'hsl(56, 59%, 22%)', type: 'fill' }, +}; diff --git a/src/utils/rendering-data/layersColor.ts b/src/utils/rendering-data/layersColor.ts index 74b4e41..0e23150 100644 --- a/src/utils/rendering-data/layersColor.ts +++ b/src/utils/rendering-data/layersColor.ts @@ -1,12 +1,11 @@ import { PropertyType } from '../../store/common/type'; -const data: PropertyType = { +const layersColor: PropertyType = { poi: { all: { - // 임시 labelText: { - fill: 'hsl(26, 25%, 32%)', - stroke: 'hsl(0, 0%, 100%)', + fill: 'transparent', + stroke: 'transparent', }, labelIcon: 'transparent', }, @@ -74,55 +73,44 @@ const data: PropertyType = { labelIcon: 'transparent', }, }, - // 임시 road: { all: { section: { fill: 'transparent', - stroke: 'hsl(230, 8%, 51%)', + stroke: 'transparent', }, labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', - }, - }, - highway: { - section: { fill: 'transparent', - stroke: 'hsl(230, 8%, 51%)', - }, - labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + stroke: 'transparent', }, }, arterial: { section: { - fill: 'transparent', - stroke: 'hsl(230, 8%, 51%)', + fill: 'hsl(0, 0%, 100%)', + stroke: 'hsl(230, 24%, 87%)', }, labelText: { - fill: 'hsl(0, 0%, 0%)', + fill: 'hsl(230, 48%, 44%)', stroke: 'hsl(0, 0%, 100%)', }, }, local: { section: { - fill: 'transparent', - stroke: 'hsl(230, 8%, 51%)', + fill: 'hsl(35, 14%, 93%)', + stroke: 'hsl(230, 24%, 87%)', }, labelText: { - fill: 'hsl(0, 0%, 0%)', + fill: 'hsl(230, 48%, 44%)', stroke: 'hsl(0, 0%, 100%)', }, }, sidewalk: { section: { - fill: 'transparent', - stroke: 'hsl(230, 8%, 51%)', + fill: 'hsl(0, 0%, 100%)', + stroke: 'hsl(230, 24%, 87%)', }, labelText: { - fill: 'hsl(0, 0%, 0%)', + fill: 'hsl(230, 48%, 44%)', stroke: 'hsl(0, 0%, 100%)', }, }, @@ -131,11 +119,11 @@ const data: PropertyType = { all: { section: { fill: 'transparent', - stroke: 'hsl(230, 8%, 51%)', + stroke: 'transparent', }, labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + fill: 'transparent', + stroke: 'transparent', }, }, country: { @@ -165,7 +153,6 @@ const data: PropertyType = { }, }, }, - // 임시 landscape: { all: { section: { @@ -173,58 +160,58 @@ const data: PropertyType = { stroke: 'transparent', }, labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + fill: 'transparent', + stroke: 'transparent', }, }, 'human-made': { section: { - fill: 'transparent', - stroke: 'transparent', + fill: 'hsl(35, 11%, 86%)', + stroke: 'hsl(75, 57%, 84%)', }, labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + fill: 'transparent', + stroke: 'transparent', }, }, building: { section: { - fill: 'transparent', - stroke: 'transparent', + fill: 'hsl(35, 11%, 86%)', + stroke: 'hsl(35, 6%, 79%)', }, labelText: { - fill: 'hsl(0, 0%, 0%)', + fill: 'hsl(35, 2%, 69%)', stroke: 'hsl(0, 0%, 100%)', }, }, natural: { section: { - fill: 'transparent', + fill: 'hsl(75, 62%, 81%)', stroke: 'transparent', }, labelText: { - fill: 'hsl(0, 0%, 0%)', + fill: 'hsl(26, 25%, 32%)', stroke: 'hsl(0, 0%, 100%)', }, }, landcover: { section: { - fill: 'transparent', - stroke: 'transparent', + fill: 'hsl(75, 62%, 81%)', + stroke: 'hsl(35, 12%, 89%)', }, labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + fill: 'transparent', + stroke: 'transparent', }, }, mountain: { section: { - fill: 'transparent', + fill: 'hsl(56, 59%, 22%)', stroke: 'transparent', }, labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + fill: 'transparent', + stroke: 'transparent', }, }, }, @@ -235,17 +222,17 @@ const data: PropertyType = { stroke: 'transparent', }, labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + fill: 'transparent', + stroke: 'transparent', }, }, airport: { section: { - fill: 'transparent', - stroke: 'transparent', + fill: 'hsl(234, 20%, 30%)', + stroke: 'hsl(230, 23%, 82%)', }, labelText: { - fill: 'hsl(0, 0%, 0%)', + fill: 'hsl(0, 69%, 50%)', stroke: 'hsl(0, 0%, 100%)', }, }, @@ -255,28 +242,28 @@ const data: PropertyType = { stroke: 'transparent', }, labelText: { - fill: 'hsl(0, 0%, 0%)', + fill: 'hsl(13, 68%, 63%)', stroke: 'hsl(0, 0%, 100%)', }, }, rail: { section: { - fill: 'transparent', + fill: 'hsl(234, 20%, 30%)', stroke: 'transparent', }, labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + fill: 'transparent', + stroke: 'transparent', }, }, subway: { section: { - fill: 'transparent', + fill: 'hsl(192, 70%, 43%)', stroke: 'transparent', }, labelText: { - fill: 'hsl(0, 0%, 0%)', - stroke: 'hsl(0, 0%, 100%)', + fill: 'transparent', + stroke: 'transparent', }, }, }, @@ -302,4 +289,4 @@ const data: PropertyType = { }, }; -export default data; +export default layersColor; From 9d2742448d873dddf3ecf3878d14b8f4b76a6918 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 11:27:43 +0900 Subject: [PATCH 106/138] =?UTF-8?q?[fix]=20=ED=83=80=EC=9E=85=20=EC=9D=B4?= =?UTF-8?q?=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 타입 선언해서 문제 해결 --- src/store/map/initializeMap.ts | 11 ++- src/utils/rendering-data/initLayerColor.ts | 106 +++++++++++++++++++++ src/utils/rendering-data/layerColor3.ts | 98 ------------------- 3 files changed, 112 insertions(+), 103 deletions(-) create mode 100644 src/utils/rendering-data/initLayerColor.ts delete mode 100644 src/utils/rendering-data/layerColor3.ts diff --git a/src/store/map/initializeMap.ts b/src/store/map/initializeMap.ts index b5ad4b5..025c492 100644 --- a/src/store/map/initializeMap.ts +++ b/src/store/map/initializeMap.ts @@ -10,7 +10,7 @@ import landscape from './layers/landscape'; import water from './layers/water'; import mapboxPOI from './layers/mapbox-poi'; -import initColor from '../../utils/rendering-data/layerColor3'; +import initLayersColor from '../../utils/rendering-data/initLayerColor'; const LNG = 126.978; const LAT = 37.5656; @@ -68,9 +68,10 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { map.on('load', () => { translate(map); - map.removeLayer('poi-label'); - map.removeLayer('road-label'); - map.removeLayer('road-polygon'); + const removalLayers: string[] = ['poi-label', 'road-label', 'road-polygon']; + removalLayers.forEach((element) => { + map.removeLayer(element); + }); map.addSource(Sources.polygon, { type: 'vector', @@ -95,7 +96,7 @@ function initializeMap({ mapRef }: InitializeMapProps): mapboxgl.Map { ] as mapboxgl.Layer[]; layers.forEach((layer: mapboxgl.Layer) => map.addLayer(layer)); - Object.entries(initColor).forEach(([key, value]) => { + Object.entries(initLayersColor).forEach(([key, value]) => { map.setPaintProperty(key, `${value.type}-color`, value.color); }); }); diff --git a/src/utils/rendering-data/initLayerColor.ts b/src/utils/rendering-data/initLayerColor.ts new file mode 100644 index 0000000..36bb800 --- /dev/null +++ b/src/utils/rendering-data/initLayerColor.ts @@ -0,0 +1,106 @@ +interface InitLayerColor { + [name: string]: { + color: string; + type: string; + }; +} + +const initLayerColor: InitLayerColor = { + 'poi-attraction': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-arts-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-landmark-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-business': { color: 'hsl(22, 55%, 55%)', type: 'text' }, + 'poi-food-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, + 'poi-store-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, + 'poi-government': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-public-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-general-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-medical': { color: 'hsl(340, 39%, 42%)', type: 'text' }, + 'poi-medical-label': { color: 'hsl(340, 39%, 42%)', type: 'text' }, + 'poi-park': { color: 'hsl(100, 45%, 37%)', type: 'text' }, + 'poi-park-label': { color: 'hsl(100, 45%, 37%)', type: 'text' }, + 'poi-worship': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-religion-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-school': { color: 'hsl(51, 40%, 40%)', type: 'text' }, + 'poi-education-label': { color: 'hsl(51, 40%, 40%)', type: 'text' }, + 'poi-sport-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-etc': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-industrial-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-historic-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'poi-building-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'admin-0-boundary': { color: 'hsl(230, 8%, 51%)', type: 'line' }, + 'admin-0-boundary-bg': { color: 'hsl(230, 8%, 51%)', type: 'line' }, + 'admin-0-boundary-disputed': { color: 'hsl(230, 8%, 51%)', type: 'line' }, + 'country-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + 'admin-1-boundary-bg': { color: 'hsl(230, 14%, 77%)', type: 'line' }, + 'admin-1-boundary': { color: 'hsl(230, 14%, 77%)', type: 'line' }, + 'state-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + 'settlement-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + 'settlement-subdivision-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, + water: { color: 'hsl(205, 87%, 76%)', type: 'fill' }, + 'water-polygon': { color: 'hsl(205, 87%, 76%)', type: 'fill' }, + 'water-line-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'water-point-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'waterway-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + // + 'mapbox-airport-aeroway-polygon': { + color: 'hsl(234, 20%, 30%)', + type: 'fill', + }, + // 'mapbox-airport-polygon': { color: 'hsl(234, 20%, 30%)', type: 'fill' }, + // 'mapbox-airport-aeroway-line': { color: 'hsl(230, 23%, 82%)', type: 'line' }, + // 'mapbox-airport-label': { color: 'hsl(0, 69%, 50%)', type: 'text' }, + // 'transit-bus-label': { color: 'hsl(13, 68%, 63%)', type: 'text' }, + // 'mapbox-rail-road-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, + // 'transit-rail-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, + // 'transit-subway-line': { color: 'hsl(192, 70%, 43%)', type: 'line' }, + // 'road-arterial': { color: 'hsl(0, 0%, 100%)', type: 'fill' }, + // 'road-arterial-polygon': { color: 'hsl(0, 0%, 100%)', type: 'fill' }, + // 'road-primary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + // 'road-secondary-teritary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + // 'road-motorway-trunk': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + // 'road-minor': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + // 'road-minor-low': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + // 'road-primary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + // 'road-secondary-teritary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + // 'road-motorway-trunk-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + // 'road-minor-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + // 'road-number-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, + // 'road-exit-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, + // 'road-arterial-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, + // 'road-local-polygon': { color: 'hsl(35, 14%, 93%)', type: 'fill' }, + // ferry: { color: 'hsl(230, 48%, 44%)', type: 'text' }, + // 'ferry-auto': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + // 'road-local': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + // 'road-street': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + // 'road-street-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + // 'road-local-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, + // 'road-pedestrian-polygon-fill': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, + // 'road-pedestrian-polygon-pattern': { + // color: 'hsl(35, 10%, 83%)', + // type: 'fill', + // }, + // 'road-sidewalk-polygon': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, + // 'road-footway': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + // 'road-pedestrian': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + // 'road-steps': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + // 'road-path': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + // 'road-pedestrian-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + // 'road-sidewalk-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, + // 'landscape-human-made': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + // 'pitch-outline': { color: 'hsl(75, 57%, 84%)', type: 'fill' }, + // 'landscape-building': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + // building: { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + // 'building-outline': { color: 'hsl(35, 6%, 79%)', type: 'line' }, + // 'building-number-label': { color: 'hsl(35, 2%, 69%)', type: 'text' }, + // 'landscape-natural': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + // 'natural-point-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + // 'landscape-landcover': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + // landcover: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + // landuse: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + // 'land-structure-polygon': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + // 'land-structure-line': { color: 'hsl(35, 12%, 89%)', type: 'line' }, + // hillshade: { color: 'hsl(56, 59%, 22%)', type: 'fill' }, +}; + +export default initLayerColor; diff --git a/src/utils/rendering-data/layerColor3.ts b/src/utils/rendering-data/layerColor3.ts deleted file mode 100644 index 89aba20..0000000 --- a/src/utils/rendering-data/layerColor3.ts +++ /dev/null @@ -1,98 +0,0 @@ -export default { - 'poi-attraction': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-arts-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-landmark-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-business': { color: 'hsl(22, 55%, 55%)', type: 'text' }, - 'poi-food-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, - 'poi-store-label': { color: 'hsl(22, 55%, 55%)', type: 'text' }, - 'poi-government': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-public-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-general-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-medical': { color: 'hsl(340, 39%, 42%)', type: 'text' }, - 'poi-medical-label': { color: 'hsl(340, 39%, 42%)', type: 'text' }, - 'poi-park': { color: 'hsl(100, 45%, 37%)', type: 'text' }, - 'poi-park-label': { color: 'hsl(100, 45%, 37%)', type: 'text' }, - 'poi-worship': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-religion-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-school': { color: 'hsl(51, 40%, 40%)', type: 'text' }, - 'poi-education-label': { color: 'hsl(51, 40%, 40%)', type: 'text' }, - 'poi-sport-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-etc': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-industrial-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-historic-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'poi-building-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'admin-0-boundary': { color: 'hsl(230, 8%, 51%)', type: 'line' }, - 'admin-0-boundary-bg': { color: 'hsl(230, 8%, 51%)', type: 'line' }, - 'admin-0-boundary-disputed': { color: 'hsl(230, 8%, 51%)', type: 'line' }, - 'country-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, - 'admin-1-boundary-bg': { color: 'hsl(230, 14%, 77%)', type: 'line' }, - 'admin-1-boundary': { color: 'hsl(230, 14%, 77%)', type: 'line' }, - 'state-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, - 'settlement-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, - 'settlement-subdivision-label': { color: 'hsl(0, 0%, 0%)', type: 'text' }, - water: { color: 'hsl(205, 87%, 76%)', type: 'fill' }, - 'water-polygon': { color: 'hsl(205, 87%, 76%)', type: 'fill' }, - 'water-line-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, - 'water-point-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, - 'waterway-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, - 'mapbox-airport-aeroway-polygon': { - color: 'hsl(234, 20%, 30%)', - type: 'fill', - }, - 'mapbox-airport-polygon': { color: 'hsl(234, 20%, 30%)', type: 'fill' }, - 'mapbox-airport-aeroway-line': { color: 'hsl(230, 23%, 82%)', type: 'line' }, - 'mapbox-airport-label': { color: 'hsl(0, 69%, 50%)', type: 'text' }, - 'transit-bus-label': { color: 'hsl(13, 68%, 63%)', type: 'text' }, - 'mapbox-rail-road-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, - 'transit-rail-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, - 'transit-subway-line': { color: 'hsl(192, 70%, 43%)', type: 'line' }, - 'road-arterial': 'hsl(0, 0%, 100%)', - 'road-arterial-polygon': { color: 'hsl(0, 0%, 100%)', type: 'fill' }, - 'road-primary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - 'road-secondary-teritary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - 'road-motorway-trunk': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - 'road-minor': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - 'road-minor-low': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - 'road-primary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - 'road-secondary-teritary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - 'road-motorway-trunk-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - 'road-minor-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - 'road-number-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, - 'road-exit-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, - 'road-arterial-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, - // - 'road-local-polygon': { color: 'hsl(35, 14%, 93%)', type: 'fill' }, - ferry: 'hsl(230, 48%, 44%)', - 'ferry-auto': { color: 'hsl(35, 14%, 93%)', type: 'line' }, - 'road-local': { color: 'hsl(35, 14%, 93%)', type: 'line' }, - 'road-street': { color: 'hsl(35, 14%, 93%)', type: 'line' }, - 'road-street-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - 'road-local-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, - // - 'road-pedestrian-polygon-fill': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, - 'road-pedestrian-polygon-pattern': { - color: 'hsl(35, 10%, 83%)', - type: 'fill', - }, - 'road-sidewalk-polygon': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, - 'road-footway': { color: 'hsl(35, 10%, 83%)', type: 'line' }, - 'road-pedestrian': { color: 'hsl(35, 10%, 83%)', type: 'line' }, - 'road-steps': { color: 'hsl(35, 10%, 83%)', type: 'line' }, - 'road-path': { color: 'hsl(35, 10%, 83%)', type: 'line' }, - 'road-pedestrian-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - 'road-sidewalk-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, - 'landscape-human-made': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, - 'pitch-outline': { color: 'hsl(75, 57%, 84%)', type: 'fill' }, - 'landscape-building': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, - building: { color: 'hsl(35, 11%, 86%)', type: 'fill' }, - 'building-outline': { color: 'hsl(35, 6%, 79%)', type: 'line' }, - 'building-number-label': { color: 'hsl(35, 2%, 69%)', type: 'text' }, - 'landscape-natural': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - 'natural-point-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - 'landscape-landcover': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - landcover: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - landuse: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - 'land-structure-polygon': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - 'land-structure-line': { color: 'hsl(35, 12%, 89%)', type: 'line' }, - hillshade: { color: 'hsl(56, 59%, 22%)', type: 'fill' }, -}; From 9c5e3806096438bf7ea8bfd304a9a7292c831694 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 11:29:24 +0900 Subject: [PATCH 107/138] =?UTF-8?q?[fix]=20=EC=98=A4=ED=83=80=20=EB=B0=8F?= =?UTF-8?q?=20=EC=9E=98=EB=AA=BB=20=EA=B8=B0=EC=9E=85=EB=90=9C=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/map/layers/transit.ts | 2 +- src/utils/map-styling/administrative.ts | 2 +- src/utils/map-styling/landscape.ts | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts index 93d66ee..d2e1cfe 100644 --- a/src/store/map/layers/transit.ts +++ b/src/store/map/layers/transit.ts @@ -110,7 +110,7 @@ export default [ 'line-color': 'hsl(201, 100%, 14%)', 'line-width': 2, }, - filter: ['match', ['get', 'type'], 'rail'], + filter: ['match', ['get', 'type'], 'rail', true, false], }, { id: 'mapbox-rail-road-line', diff --git a/src/utils/map-styling/administrative.ts b/src/utils/map-styling/administrative.ts index 164c051..6c53ba4 100644 --- a/src/utils/map-styling/administrative.ts +++ b/src/utils/map-styling/administrative.ts @@ -29,7 +29,7 @@ const layers = { }, locality: { line: [], - symbol: ['settlement-label', 'settlement_subdivision-label'], + symbol: ['settlement-label', 'settlement-subdivision-label'], }, all: { line: [ diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index 0ebf009..e154cb7 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -57,9 +57,9 @@ const landCoverSectionFill = [ 'landscape-landcover', 'landcover', 'landuse', - 'lang-structure-polygon', + 'land-structure-polygon', ]; -const landCoverSectionStroke = ['lang-structure-line']; +const landCoverSectionStroke = ['land-structure-line']; const mountainCoverSectionFill = ['hillshade']; @@ -71,13 +71,13 @@ const layersByType: { [key in LandscapeSubFeature]: DetailType } = { }, building: { [SECTION]: makeSubDetail(buildingSectionFill, buildingSectionStroke), - [LABELTEXT]: makeSubDetail([], []), - [LABELICON]: makeSubDetail(buildingLabel, buildingLabel), + [LABELTEXT]: makeSubDetail(buildingLabel, buildingLabel), + [LABELICON]: makeSubDetail([], []), }, natural: { [SECTION]: makeSubDetail(naturalSectionFill, []), - [LABELTEXT]: makeSubDetail([], []), - [LABELICON]: makeSubDetail(naturalLabel, naturalLabel), + [LABELTEXT]: makeSubDetail(naturalLabel, naturalLabel), + [LABELICON]: makeSubDetail([], []), }, landcover: { [SECTION]: makeSubDetail(landCoverSectionFill, landCoverSectionStroke), From 951e3a8a56b923431d3dd89a16090ad80ef1e39b Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 13:02:54 +0900 Subject: [PATCH 108/138] =?UTF-8?q?[fix]=20range=EC=9D=98=20numbe,=20strin?= =?UTF-8?q?g=20=EA=B5=AC=EB=B6=84=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/common/useInputRange.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hooks/common/useInputRange.ts b/src/hooks/common/useInputRange.ts index 1b1bfc8..577f784 100644 --- a/src/hooks/common/useInputRange.ts +++ b/src/hooks/common/useInputRange.ts @@ -23,7 +23,10 @@ function useInputRange({ }, [range]); const rangeChangeHandler = (e: React.ChangeEvent) => { - setCurRange(e.target.value); + const value = Number.isNaN(Number(e.target.value)) + ? e.target.value + : Number(e.target.value); + setCurRange(value); }; const rangeMouseUpHandler = (key: StyleKeyType) => { From 73ab8dc9b83384cd5072553979393f1d1f342500 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 13:06:01 +0900 Subject: [PATCH 109/138] =?UTF-8?q?[fix]=20mapbox=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EC=9C=A0=ED=98=95=20=EC=9D=B4=EB=8F=99=20=EB=B0=8F?= =?UTF-8?q?=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/map-styling/administrative.ts | 2 +- src/utils/map-styling/landscape.ts | 2 +- src/utils/map-styling/water.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/map-styling/administrative.ts b/src/utils/map-styling/administrative.ts index 6c53ba4..1f9b040 100644 --- a/src/utils/map-styling/administrative.ts +++ b/src/utils/map-styling/administrative.ts @@ -42,7 +42,7 @@ const layers = { symbol: [ 'country-label', 'settlement-label', - 'settlement_subdivision-label', + 'settlement-subdivision-label', 'state-label', ], }, diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index e154cb7..29c3420 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -48,7 +48,7 @@ const humanMadeSectionStroke = ['pitch-outline']; const buildingSectionFill = ['landscape-building', 'building']; const buildingSectionStroke = ['building-outline']; -const buildingLabel = ['building-number-label']; +const buildingLabel = ['building-number-label', 'poi-building-label']; const naturalSectionFill = ['landscape-natural']; const naturalLabel = ['natural-point-label']; diff --git a/src/utils/map-styling/water.ts b/src/utils/map-styling/water.ts index a87215d..e32d2ba 100644 --- a/src/utils/map-styling/water.ts +++ b/src/utils/map-styling/water.ts @@ -13,7 +13,7 @@ import { } from '../applyStyle'; const layers = { - polygon: ['water-polygon', 'water'], + polygon: ['water-polygon', 'water', 'water-shadow'], symbol: ['water-line-label', 'water-point-label', 'waterway-label'], }; From 2421955f9717d4857e174f328980765ed6c86940 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 13:16:24 +0900 Subject: [PATCH 110/138] =?UTF-8?q?[feat]=20=EB=8F=84=EB=A1=9C=20All=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=81=EC=9A=A9=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/map-styling/road-categories/all.ts | 44 +++++++++++++++++++ .../map-styling/road-categories/arterial.ts | 12 ++++- .../map-styling/road-categories/local.ts | 10 ++++- .../map-styling/road-categories/sidewalk.ts | 12 ++++- src/utils/map-styling/road.ts | 4 +- 5 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 src/utils/map-styling/road-categories/all.ts diff --git a/src/utils/map-styling/road-categories/all.ts b/src/utils/map-styling/road-categories/all.ts new file mode 100644 index 0000000..e5f983b --- /dev/null +++ b/src/utils/map-styling/road-categories/all.ts @@ -0,0 +1,44 @@ +import { arterialLayerNames } from './arterial'; +import { localLayerNames } from './local'; +import { sidewalkLayerNames } from './sidewalk'; + +export const allRoadLayerNames = { + all: [ + ...arterialLayerNames.all, + ...localLayerNames.all, + ...sidewalkLayerNames.all, + ], + polygon: [ + ...arterialLayerNames.polygon, + ...localLayerNames.polygon, + ...sidewalkLayerNames.polygon, + ], + line: [ + ...arterialLayerNames.line, + ...localLayerNames.line, + ...sidewalkLayerNames.line, + ], + stroke: [ + ...arterialLayerNames.stroke, + ...localLayerNames.stroke, + ...sidewalkLayerNames.stroke, + ], + text: { + all: [ + ...arterialLayerNames.text.all, + ...localLayerNames.text.all, + ...sidewalkLayerNames.text.all, + ], + hasStroke: [ + ...arterialLayerNames.text.hasStroke, + ...localLayerNames.text.hasStroke, + ...sidewalkLayerNames.text.hasStroke, + ], + noStroke: [ + ...arterialLayerNames.text.noStroke, + ...localLayerNames.text.noStroke, + ...sidewalkLayerNames.text.noStroke, + ], + }, + icon: [], +}; diff --git a/src/utils/map-styling/road-categories/arterial.ts b/src/utils/map-styling/road-categories/arterial.ts index bf9f685..cc19e0e 100644 --- a/src/utils/map-styling/road-categories/arterial.ts +++ b/src/utils/map-styling/road-categories/arterial.ts @@ -1,6 +1,17 @@ export const arterialLayerNames = { all: [ + 'road-arterial-polygon', 'road-arterial', + 'road-primary', + 'road-secondary-tertiary', + 'road-motorway-trunk', + 'road-minor', + 'road-minor-low', + 'road-primary-case', + 'road-secondary-tertiary-case', + 'road-motorway-trunk-case', + 'road-minor-case', + 'road-minor-low', 'road-number-shield', 'road-exit-shield', 'road-arterial-label', @@ -19,7 +30,6 @@ export const arterialLayerNames = { 'road-secondary-tertiary-case', 'road-motorway-trunk-case', 'road-minor-case', - 'road-minor-low', ], text: { all: ['road-number-shield', 'road-exit-shield', 'road-arterial-label'], diff --git a/src/utils/map-styling/road-categories/local.ts b/src/utils/map-styling/road-categories/local.ts index 617961f..be70479 100644 --- a/src/utils/map-styling/road-categories/local.ts +++ b/src/utils/map-styling/road-categories/local.ts @@ -1,5 +1,13 @@ export const localLayerNames = { - all: ['ferry', 'ferry-auto', 'road-local', 'road-local-label'], + all: [ + 'road-local-polygon', + 'ferry', + 'ferry-auto', + 'road-local', + 'road-street', + 'road-street-case', + 'road-local-label', + ], polygon: ['road-local-polygon'], line: ['ferry', 'ferry-auto', 'road-local', 'road-street'], stroke: ['road-street-case'], diff --git a/src/utils/map-styling/road-categories/sidewalk.ts b/src/utils/map-styling/road-categories/sidewalk.ts index 6137a5a..57f3a6b 100644 --- a/src/utils/map-styling/road-categories/sidewalk.ts +++ b/src/utils/map-styling/road-categories/sidewalk.ts @@ -1,5 +1,15 @@ export const sidewalkLayerNames = { - all: ['road-footway', 'road-sidewalk-label'], + all: [ + 'road-footway', + 'road-sidewalk-label', + 'road-pedestrian-polygon-fill', + 'road-pedestrian-polygon-pattern', + 'road-sidewalk-polygon', + 'road-pedestrian', + 'road-steps', + 'road-path', + 'road-pedestrian-case', + ], polygon: [ 'road-pedestrian-polygon-fill', 'road-pedestrian-polygon-pattern', diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index 578e815..83d8ea4 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -2,6 +2,7 @@ import { stylingProps } from '.'; import { arterialLayerNames } from './road-categories/arterial'; import { localLayerNames } from './road-categories/local'; import { sidewalkLayerNames } from './road-categories/sidewalk'; +import { allRoadLayerNames } from './road-categories/all'; import stylingCategory from './road-categories/stylingCategory'; function roadStyling({ @@ -12,9 +13,10 @@ function roadStyling({ key, style, }: stylingProps): void { - type RoadSubFeatureType = 'arterial' | 'local' | 'sidewalk'; + type RoadSubFeatureType = 'all' | 'arterial' | 'local' | 'sidewalk'; const mappingSubFeatureLayerNames = { + all: allRoadLayerNames, arterial: arterialLayerNames, local: localLayerNames, sidewalk: sidewalkLayerNames, From f415dbc7144561e6e9226f1fc96773788cc9392f Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 13:17:24 +0900 Subject: [PATCH 111/138] =?UTF-8?q?[fix]=20=EA=B8=B0=EB=B3=B8=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=20=EC=86=8D=EC=84=B1=EA=B0=92=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 잘못 기입되거나 값에 수정이 필요한 부분들 적용함 --- src/store/map/layers/transit.ts | 6 +- src/utils/rendering-data/initLayerColor.ts | 108 ++++++++++----------- src/utils/rendering-data/layersColor.ts | 4 +- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/store/map/layers/transit.ts b/src/store/map/layers/transit.ts index d2e1cfe..12b0a5a 100644 --- a/src/store/map/layers/transit.ts +++ b/src/store/map/layers/transit.ts @@ -108,7 +108,7 @@ export default [ }, paint: { 'line-color': 'hsl(201, 100%, 14%)', - 'line-width': 2, + 'line-width': 1, }, filter: ['match', ['get', 'type'], 'rail', true, false], }, @@ -125,7 +125,7 @@ export default [ paint: { 'line-color': 'hsl(234, 20%, 30%)', - 'line-width': 6, + 'line-width': 1, }, filter: [ 'all', @@ -144,7 +144,7 @@ export default [ visibility: 'visible', }, paint: { - 'text-halo-color': 'hsl(151, 24%, 60%)', + 'text-halo-color': 'hsl(0, 0%, 100%)', 'text-halo-width': 1, 'text-color': 'hsl(13, 68%, 63%)', }, diff --git a/src/utils/rendering-data/initLayerColor.ts b/src/utils/rendering-data/initLayerColor.ts index 36bb800..c24f56d 100644 --- a/src/utils/rendering-data/initLayerColor.ts +++ b/src/utils/rendering-data/initLayerColor.ts @@ -47,60 +47,60 @@ const initLayerColor: InitLayerColor = { color: 'hsl(234, 20%, 30%)', type: 'fill', }, - // 'mapbox-airport-polygon': { color: 'hsl(234, 20%, 30%)', type: 'fill' }, - // 'mapbox-airport-aeroway-line': { color: 'hsl(230, 23%, 82%)', type: 'line' }, - // 'mapbox-airport-label': { color: 'hsl(0, 69%, 50%)', type: 'text' }, - // 'transit-bus-label': { color: 'hsl(13, 68%, 63%)', type: 'text' }, - // 'mapbox-rail-road-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, - // 'transit-rail-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, - // 'transit-subway-line': { color: 'hsl(192, 70%, 43%)', type: 'line' }, - // 'road-arterial': { color: 'hsl(0, 0%, 100%)', type: 'fill' }, - // 'road-arterial-polygon': { color: 'hsl(0, 0%, 100%)', type: 'fill' }, - // 'road-primary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - // 'road-secondary-teritary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - // 'road-motorway-trunk': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - // 'road-minor': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - // 'road-minor-low': { color: 'hsl(0, 0%, 100%)', type: 'line' }, - // 'road-primary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - // 'road-secondary-teritary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - // 'road-motorway-trunk-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - // 'road-minor-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - // 'road-number-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, - // 'road-exit-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, - // 'road-arterial-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, - // 'road-local-polygon': { color: 'hsl(35, 14%, 93%)', type: 'fill' }, - // ferry: { color: 'hsl(230, 48%, 44%)', type: 'text' }, - // 'ferry-auto': { color: 'hsl(35, 14%, 93%)', type: 'line' }, - // 'road-local': { color: 'hsl(35, 14%, 93%)', type: 'line' }, - // 'road-street': { color: 'hsl(35, 14%, 93%)', type: 'line' }, - // 'road-street-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - // 'road-local-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, - // 'road-pedestrian-polygon-fill': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, - // 'road-pedestrian-polygon-pattern': { - // color: 'hsl(35, 10%, 83%)', - // type: 'fill', - // }, - // 'road-sidewalk-polygon': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, - // 'road-footway': { color: 'hsl(35, 10%, 83%)', type: 'line' }, - // 'road-pedestrian': { color: 'hsl(35, 10%, 83%)', type: 'line' }, - // 'road-steps': { color: 'hsl(35, 10%, 83%)', type: 'line' }, - // 'road-path': { color: 'hsl(35, 10%, 83%)', type: 'line' }, - // 'road-pedestrian-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, - // 'road-sidewalk-label': { color: 'hsl(230, 48%, 44%)', type: 'line' }, - // 'landscape-human-made': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, - // 'pitch-outline': { color: 'hsl(75, 57%, 84%)', type: 'fill' }, - // 'landscape-building': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, - // building: { color: 'hsl(35, 11%, 86%)', type: 'fill' }, - // 'building-outline': { color: 'hsl(35, 6%, 79%)', type: 'line' }, - // 'building-number-label': { color: 'hsl(35, 2%, 69%)', type: 'text' }, - // 'landscape-natural': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - // 'natural-point-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, - // 'landscape-landcover': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - // landcover: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - // landuse: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - // 'land-structure-polygon': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, - // 'land-structure-line': { color: 'hsl(35, 12%, 89%)', type: 'line' }, - // hillshade: { color: 'hsl(56, 59%, 22%)', type: 'fill' }, + 'mapbox-airport-polygon': { color: 'hsl(234, 20%, 30%)', type: 'fill' }, + 'mapbox-airport-aeroway-line': { color: 'hsl(230, 23%, 82%)', type: 'line' }, + 'mapbox-airport-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'transit-bus-label': { color: 'hsl(234, 20%, 30%)', type: 'text' }, + 'mapbox-rail-road-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, + 'transit-rail-line': { color: 'hsl(234, 20%, 30%)', type: 'line' }, + 'transit-subway-line': { color: 'hsl(192, 70%, 43%)', type: 'line' }, + 'road-arterial': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-arterial-polygon': { color: 'hsl(0, 0%, 100%)', type: 'fill' }, + 'road-primary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-secondary-tertiary': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-motorway-trunk': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-minor': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-minor-low': { color: 'hsl(0, 0%, 100%)', type: 'line' }, + 'road-primary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-secondary-tertiary-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-motorway-trunk-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-minor-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-number-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, + 'road-exit-shield': { color: 'hsl(0, 0%, 100%)', type: 'text' }, + 'road-arterial-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'road-local-polygon': { color: 'hsl(35, 14%, 93%)', type: 'fill' }, + ferry: { color: 'hsl(230, 48%, 44%)', type: 'line' }, + 'ferry-auto': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + 'road-local': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + 'road-street': { color: 'hsl(35, 14%, 93%)', type: 'line' }, + 'road-street-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-local-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'road-pedestrian-polygon-fill': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, + 'road-pedestrian-polygon-pattern': { + color: 'hsl(35, 10%, 83%)', + type: 'fill', + }, + 'road-sidewalk-polygon': { color: 'hsl(35, 10%, 83%)', type: 'fill' }, + 'road-footway': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-pedestrian': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-steps': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-path': { color: 'hsl(35, 10%, 83%)', type: 'line' }, + 'road-pedestrian-case': { color: 'hsl(230, 24%, 87%)', type: 'line' }, + 'road-sidewalk-label': { color: 'hsl(230, 48%, 44%)', type: 'text' }, + 'landscape-human-made': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + 'pitch-outline': { color: 'hsl(75, 57%, 84%)', type: 'line' }, + 'landscape-building': { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + building: { color: 'hsl(35, 11%, 86%)', type: 'fill' }, + 'building-outline': { color: 'hsl(35, 6%, 79%)', type: 'line' }, + 'building-number-label': { color: 'hsl(35, 2%, 69%)', type: 'text' }, + 'landscape-natural': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + 'natural-point-label': { color: 'hsl(26, 25%, 32%)', type: 'text' }, + 'landscape-landcover': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + landcover: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + landuse: { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + 'land-structure-polygon': { color: 'hsl(75, 62%, 81%)', type: 'fill' }, + 'land-structure-line': { color: 'hsl(35, 12%, 89%)', type: 'line' }, + hillshade: { color: 'hsl(56, 59%, 22%)', type: 'fill' }, }; export default initLayerColor; diff --git a/src/utils/rendering-data/layersColor.ts b/src/utils/rendering-data/layersColor.ts index 0e23150..7cc42a1 100644 --- a/src/utils/rendering-data/layersColor.ts +++ b/src/utils/rendering-data/layersColor.ts @@ -232,7 +232,7 @@ const layersColor: PropertyType = { stroke: 'hsl(230, 23%, 82%)', }, labelText: { - fill: 'hsl(0, 69%, 50%)', + fill: 'hsl(230, 48%, 44%)', stroke: 'hsl(0, 0%, 100%)', }, }, @@ -242,7 +242,7 @@ const layersColor: PropertyType = { stroke: 'transparent', }, labelText: { - fill: 'hsl(13, 68%, 63%)', + fill: 'hsl(234, 20%, 30%)', stroke: 'hsl(0, 0%, 100%)', }, }, From 9312d2c1fb2f00594701c01e1ddb907379f3d7ac Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 13:19:04 +0900 Subject: [PATCH 112/138] =?UTF-8?q?[fix]=20weight=20=EC=A0=88=EB=8C=80?= =?UTF-8?q?=EA=B0=92=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 두께 변화가 커서 조정 - icon의 경우 opacity 범위가 제한되어서 수정함 --- src/utils/applyStyle.ts | 2 +- src/utils/map-styling/poi.ts | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index 15d41c4..db26af6 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -90,7 +90,7 @@ export function applyWeight({ }: ApplyProps): void { if (!type) return; - const weightValue = weight === 0 ? 0 : weight * 2 + 1; + const weightValue = weight === 0 ? 0 : weight; layerNames.forEach((layerName) => { map.setPaintProperty(layerName, type, weightValue); }); diff --git a/src/utils/map-styling/poi.ts b/src/utils/map-styling/poi.ts index 9f371b2..1b36d61 100644 --- a/src/utils/map-styling/poi.ts +++ b/src/utils/map-styling/poi.ts @@ -26,7 +26,7 @@ type POI_LAYERS_TYPE = { [name in PoiSubFeature]: string[]; }; -const VISIBLE = 1; +const VISIBLE = -0.001; const INVISIBLE = 0; const POI_LAYERS: POI_LAYERS_TYPE = { all: [ @@ -51,7 +51,6 @@ const POI_LAYERS: POI_LAYERS_TYPE = { 'poi-etc', 'poi-industrial-label', 'poi-historic-label', - 'poi-building-label', ], landmark: ['poi-attraction', 'poi-arts-label', 'poi-landmark-label'], business: ['poi-business', 'poi-food-label', 'poi-store-label'], @@ -61,12 +60,7 @@ const POI_LAYERS: POI_LAYERS_TYPE = { worship: ['poi-worship', 'poi-religion-label'], school: ['poi-school', 'poi-education-label'], sports: ['poi-sport-label'], - etc: [ - 'poi-etc', - 'poi-industrial-label', - 'poi-historic-label', - 'poi-building-label', - ], + etc: ['poi-etc', 'poi-industrial-label', 'poi-historic-label'], }; const mappingDetailToFunc = { From 0bcf6ed72d2d1a59557554d518db2d71992ce6d9 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 14:00:15 +0900 Subject: [PATCH 113/138] =?UTF-8?q?[refactor]=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20=EB=B3=80=EC=88=98=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 세부 유형을 의미하는 deatil -> element로 변경 - 공통 타입에 선언된 인터페이스 사용하도록 수정하여 Name 제거 --- .../Sidebar/SidebarContentMore/DetailType.tsx | 27 +++---- .../SidebarContentMore/FeatureType.tsx | 4 +- .../Sidebar/SidebarContentMore/Styler.tsx | 33 +++------ src/hooks/sidebar/useDetailType.ts | 30 ++++---- src/hooks/sidebar/useStyleType.ts | 52 ++++++------- src/utils/map-styling/administrative.ts | 19 ++--- src/utils/map-styling/index.ts | 6 +- src/utils/map-styling/landscape.ts | 74 ++++++++----------- src/utils/map-styling/marker.ts | 6 +- src/utils/map-styling/poi.ts | 24 +++--- .../road-categories/stylingCategory.ts | 48 ++++++------ src/utils/map-styling/road.ts | 15 ++-- src/utils/map-styling/transit.ts | 26 +++---- src/utils/map-styling/water.ts | 15 ++-- 14 files changed, 169 insertions(+), 210 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index edda5c2..4f147b2 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -9,9 +9,9 @@ import useDetailType, { } from '../../../hooks/sidebar/useDetailType'; import Styler from './Styler'; import { - FeatureNameType, ElementNameType, SubElementNameType, + FeaturePropsType, } from '../../../store/common/type'; interface PaddingProp { @@ -62,15 +62,10 @@ const CheckRight = styled.div` color: ${(props) => props.theme.GREEN}; `; -interface DetailTypeProps { - featureName: FeatureNameType; - subFeatureName: string; -} - function DetailType({ - featureName, - subFeatureName, -}: DetailTypeProps): React.ReactElement { + feature, + subFeature, +}: FeaturePropsType): React.ReactElement { const { sidebarTypeName, sidebarSubTypeName, @@ -87,11 +82,11 @@ function DetailType({ sidebarSubTypeClickHandler, sidebarTypeName, sidebarSubTypeName, - featureName, - subFeatureName, + feature, + subFeature, }); - if (!featureName) { + if (!feature) { return <>; } @@ -199,10 +194,10 @@ function DetailType({ ); diff --git a/src/components/Sidebar/SidebarContentMore/FeatureType.tsx b/src/components/Sidebar/SidebarContentMore/FeatureType.tsx index da87cae..9818601 100644 --- a/src/components/Sidebar/SidebarContentMore/FeatureType.tsx +++ b/src/components/Sidebar/SidebarContentMore/FeatureType.tsx @@ -53,8 +53,8 @@ function FeatureType(): React.ReactElement { ))} ); diff --git a/src/components/Sidebar/SidebarContentMore/Styler.tsx b/src/components/Sidebar/SidebarContentMore/Styler.tsx index e6b1391..34d2ed0 100644 --- a/src/components/Sidebar/SidebarContentMore/Styler.tsx +++ b/src/components/Sidebar/SidebarContentMore/Styler.tsx @@ -1,10 +1,6 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; -import { - FeatureNameType, - ElementNameType, - SubElementNameType, -} from '../../../store/common/type'; +import { ElementPropsType } from '../../../store/common/type'; import useStyleType, { UseStyleHookType, } from '../../../hooks/sidebar/useStyleType'; @@ -39,30 +35,23 @@ const Hr = styled.hr` color: ${(props) => props.theme.GREY}; `; -interface StylerProps { - featureName: FeatureNameType; - subFeatureName: string; - detailName: ElementNameType; - subDetailName?: SubElementNameType; -} - function Styler({ - featureName, - subFeatureName, - detailName, - subDetailName, -}: StylerProps): React.ReactElement { + feature, + subFeature, + element, + subElement, +}: ElementPropsType): React.ReactElement { const { styleElement: { visibility, color, weight, saturation, lightness }, onStyleChange, }: UseStyleHookType = useStyleType({ - featureName, - subFeatureName, - detailName, - subDetailName, + feature, + subFeature, + element, + subElement, }); - if (!detailName) { + if (!element) { return <>; } diff --git a/src/hooks/sidebar/useDetailType.ts b/src/hooks/sidebar/useDetailType.ts index 802dfbd..4fdfb51 100644 --- a/src/hooks/sidebar/useDetailType.ts +++ b/src/hooks/sidebar/useDetailType.ts @@ -8,8 +8,8 @@ import { } from '../../store/common/type'; interface UseDetailTypeProps { - featureName: FeatureNameType; - subFeatureName: string; + feature: FeatureNameType; + subFeature: string; sidebarTypeName: string; sidebarSubTypeName: string; sidebarTypeClickHandler: (name: string) => void; @@ -34,37 +34,35 @@ const dummyDetail = { }; function useDetailType({ - featureName, - subFeatureName, + feature, + subFeature, sidebarTypeName, sidebarSubTypeName, sidebarTypeClickHandler, sidebarSubTypeClickHandler, }: UseDetailTypeProps): UseDetailHookType { const detail = useSelector((state) => { - if (!featureName) { + if (!feature) { return dummyDetail; } - return state[featureName][subFeatureName]; + return state[feature][subFeature]; }) as FeatureType; const styleClickHandler = ( - elementName: ElementNameType, - subElementName?: SubElementNameType + element: ElementNameType, + subElement?: SubElementNameType ) => { - sidebarTypeClickHandler(elementName); - if (subElementName) sidebarSubTypeClickHandler(subElementName); + sidebarTypeClickHandler(element); + if (subElement) sidebarSubTypeClickHandler(subElement); }; const checkIsSelected = ( - elementName: ElementNameType, - subElementName?: SubElementNameType + element: ElementNameType, + subElement?: SubElementNameType ) => { - if (!subElementName) return sidebarTypeName === elementName; - return ( - sidebarTypeName === elementName && sidebarSubTypeName === subElementName - ); + if (!subElement) return sidebarTypeName === element; + return sidebarTypeName === element && sidebarSubTypeName === subElement; }; return { diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index 21c6237..2c79394 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -8,54 +8,48 @@ import { StyleKeyType, ElementNameType, SubElementNameType, + ElementPropsType, } from '../../store/common/type'; import { setStyle } from '../../store/style/action'; import { getDefaultStyle } from '../../store/style/properties'; import * as mapStyling from '../../utils/map-styling'; -interface UseStyleTypeProps { - featureName: FeatureNameType; - subFeatureName: string; - detailName: ElementNameType; - subDetailName?: SubElementNameType; -} - export interface UseStyleHookType { styleElement: StyleType; onStyleChange: (key: StyleKeyType, value: string | number) => void; } function useStyleType({ - featureName, - subFeatureName, - detailName, - subDetailName, -}: UseStyleTypeProps): UseStyleHookType { + feature, + subFeature, + element, + subElement, +}: ElementPropsType): UseStyleHookType { const dispatch = useDispatch(); const map = useSelector((state) => state.map.map) as mapboxgl.Map; const styleElement = useSelector((state) => { - if (!detailName) { + if (!element) { return getDefaultStyle({ - feature: featureName, - subFeature: subFeatureName, - element: detailName, - subElement: subDetailName, + feature, + subFeature, + element, + subElement, }); } - const feature = state[featureName][subFeatureName]; - if (detailName === ElementNameType.labelIcon) return feature[detailName]; - return feature[detailName][subDetailName as SubElementNameType]; + const newFeature = state[feature][subFeature]; + if (element === ElementNameType.labelIcon) return newFeature[element]; + return newFeature[element][subElement as SubElementNameType]; }) as StyleType; const onStyleChange = useCallback( (key: StyleKeyType, value: string | number) => { - mapStyling[featureName]({ + mapStyling[feature]({ map, - subFeatureName, + subFeature, key, - detailName, - subDetailName: subDetailName as SubElementNameType, + element, + subElement: subElement as SubElementNameType, style: { ...styleElement, [key]: value, @@ -64,10 +58,10 @@ function useStyleType({ dispatch( setStyle({ - feature: featureName, - subFeature: subFeatureName, - element: detailName, - subElement: subDetailName, + feature, + subFeature, + element, + subElement, style: { ...styleElement, [key]: value, @@ -75,7 +69,7 @@ function useStyleType({ }) ); }, - [featureName, subFeatureName, detailName, subDetailName, styleElement] + [feature, subFeature, element, subElement, styleElement] ); return { diff --git a/src/utils/map-styling/administrative.ts b/src/utils/map-styling/administrative.ts index 1f9b040..8f42d90 100644 --- a/src/utils/map-styling/administrative.ts +++ b/src/utils/map-styling/administrative.ts @@ -50,18 +50,18 @@ const layers = { function administrativeStyling({ map, - subFeatureName, - detailName, - subDetailName, + subFeature, + element, + subElement, key, style, }: stylingProps): void { - const mappingLayers = layers[subFeatureName as subFeatureNameType]; + const mappingLayers = layers[subFeature as subFeatureNameType]; - if (detailName === ElementNameType.labelIcon) return; - if (detailName === ElementNameType.labelText) { + if (element === ElementNameType.labelIcon) return; + if (element === ElementNameType.labelText) { // labelText - fill - if (subDetailName === SubElementNameType.fill) { + if (subElement === SubElementNameType.fill) { if (key === StyleKeyType.weight) return; if (key === StyleKeyType.visibility) { const styleValue = style[key] === 'none' ? 'none' : 'visible'; @@ -106,10 +106,7 @@ function administrativeStyling({ } // section - fill - if ( - subFeatureName === 'locality' || - subDetailName === SubElementNameType.fill - ) + if (subFeature === 'locality' || subElement === SubElementNameType.fill) return; // section - stroke diff --git a/src/utils/map-styling/index.ts b/src/utils/map-styling/index.ts index 1b259cf..9eb2660 100644 --- a/src/utils/map-styling/index.ts +++ b/src/utils/map-styling/index.ts @@ -8,9 +8,9 @@ import { export interface stylingProps { map: mapboxgl.Map; - subFeatureName: string; - detailName: ElementNameType; - subDetailName: SubElementNameType; + subFeature: string; + element: ElementNameType; + subElement: SubElementNameType; key: StyleKeyType; style: StyleType; } diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index 29c3420..f618334 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -7,11 +7,7 @@ import { ColorType, StyleTypes, } from '../applyStyle'; -import { - StyleKeyType, - ElementNameType, - StyleType, -} from '../../store/common/type'; +import { StyleKeyType, ElementNameType } from '../../store/common/type'; const SECTION = 'section'; const LABELTEXT = 'labelText'; @@ -36,7 +32,7 @@ interface DetailType { labelIcon: SubDetailType; } -function makeSubDetail( +function makeSubElement( fillLayers: string[], strokeLayers: string[] ): SubDetailType { @@ -65,32 +61,32 @@ const mountainCoverSectionFill = ['hillshade']; const layersByType: { [key in LandscapeSubFeature]: DetailType } = { 'human-made': { - [SECTION]: makeSubDetail(humanMadeSectionFill, humanMadeSectionStroke), - [LABELTEXT]: makeSubDetail([], []), - [LABELICON]: makeSubDetail([], []), + [SECTION]: makeSubElement(humanMadeSectionFill, humanMadeSectionStroke), + [LABELTEXT]: makeSubElement([], []), + [LABELICON]: makeSubElement([], []), }, building: { - [SECTION]: makeSubDetail(buildingSectionFill, buildingSectionStroke), - [LABELTEXT]: makeSubDetail(buildingLabel, buildingLabel), - [LABELICON]: makeSubDetail([], []), + [SECTION]: makeSubElement(buildingSectionFill, buildingSectionStroke), + [LABELTEXT]: makeSubElement(buildingLabel, buildingLabel), + [LABELICON]: makeSubElement([], []), }, natural: { - [SECTION]: makeSubDetail(naturalSectionFill, []), - [LABELTEXT]: makeSubDetail(naturalLabel, naturalLabel), - [LABELICON]: makeSubDetail([], []), + [SECTION]: makeSubElement(naturalSectionFill, []), + [LABELTEXT]: makeSubElement(naturalLabel, naturalLabel), + [LABELICON]: makeSubElement([], []), }, landcover: { - [SECTION]: makeSubDetail(landCoverSectionFill, landCoverSectionStroke), - [LABELTEXT]: makeSubDetail([], []), - [LABELICON]: makeSubDetail([], []), + [SECTION]: makeSubElement(landCoverSectionFill, landCoverSectionStroke), + [LABELTEXT]: makeSubElement([], []), + [LABELICON]: makeSubElement([], []), }, mountain: { - [SECTION]: makeSubDetail(mountainCoverSectionFill, []), - [LABELTEXT]: makeSubDetail([], []), - [LABELICON]: makeSubDetail([], []), + [SECTION]: makeSubElement(mountainCoverSectionFill, []), + [LABELTEXT]: makeSubElement([], []), + [LABELICON]: makeSubElement([], []), }, all: { - [SECTION]: makeSubDetail( + [SECTION]: makeSubElement( [ ...humanMadeSectionFill, ...buildingSectionFill, @@ -104,8 +100,8 @@ const layersByType: { [key in LandscapeSubFeature]: DetailType } = { ...landCoverSectionStroke, ] ), - [LABELTEXT]: makeSubDetail([], []), - [LABELICON]: makeSubDetail([], [...buildingLabel, ...naturalLabel]), + [LABELTEXT]: makeSubElement([], [...buildingLabel, ...naturalLabel]), + [LABELICON]: makeSubElement([], []), }, }; @@ -251,9 +247,9 @@ const mappingDetailToFunc = { function landscapeStyling({ map, - subFeatureName, - detailName, - subDetailName, + subFeature, + element, + subElement, key, style, }: stylingProps): void { @@ -261,16 +257,16 @@ function landscapeStyling({ let func = null; if ( - detailName === ElementNameType.section || - detailName === ElementNameType.labelText + element === ElementNameType.section || + element === ElementNameType.labelText ) { - const { typeName, funcName } = mappingDetailToFunc[detailName][ - subDetailName - ][key as StyleKeyType]; + const { typeName, funcName } = mappingDetailToFunc[element][subElement][ + key as StyleKeyType + ]; type = typeName; func = funcName; } else { - const { typeName, funcName } = mappingDetailToFunc[detailName][ + const { typeName, funcName } = mappingDetailToFunc[element][ key as StyleKeyType ]; type = typeName; @@ -287,9 +283,7 @@ function landscapeStyling({ func({ map, layerNames: - layersByType[subFeatureName as LandscapeSubFeature][detailName][ - subDetailName - ], + layersByType[subFeature as LandscapeSubFeature][element][subElement], type, weight: style.visibility === 'none' ? INVISIBLE : VISIBLE, }); @@ -299,9 +293,7 @@ function landscapeStyling({ func({ map, layerNames: - layersByType[subFeatureName as LandscapeSubFeature][detailName][ - subDetailName - ], + layersByType[subFeature as LandscapeSubFeature][element][subElement], visibility: style.visibility, }); } @@ -309,9 +301,7 @@ function landscapeStyling({ func({ map, layerNames: - layersByType[subFeatureName as LandscapeSubFeature][detailName][ - subDetailName - ], + layersByType[subFeature as LandscapeSubFeature][element][subElement], type: type as StyleTypes, color: style.color, [key]: style[key as StyleKeyType], diff --git a/src/utils/map-styling/marker.ts b/src/utils/map-styling/marker.ts index 051a573..80da01c 100644 --- a/src/utils/map-styling/marker.ts +++ b/src/utils/map-styling/marker.ts @@ -2,9 +2,9 @@ import { stylingProps } from './index'; function markerStyling({ map, - subFeatureName, - detailName, - subDetailName, + subFeature, + element, + subElement, style, }: stylingProps): void { console.log(1); diff --git a/src/utils/map-styling/poi.ts b/src/utils/map-styling/poi.ts index 1b36d61..e4f92ad 100644 --- a/src/utils/map-styling/poi.ts +++ b/src/utils/map-styling/poi.ts @@ -120,24 +120,24 @@ const mappingDetailToFunc = { function poiStyling({ map, - subFeatureName, - detailName, - subDetailName, + subFeature, + element, + subElement, style, key, }: stylingProps): void { let type = null; let func = null; - if (detailName === ElementNameType.section) return; - if (detailName === ElementNameType.labelText && key !== 'isChanged') { - const { typeName, funcName } = mappingDetailToFunc[detailName][ - subDetailName - ][key]; + if (element === ElementNameType.section) return; + if (element === ElementNameType.labelText && key !== 'isChanged') { + const { typeName, funcName } = mappingDetailToFunc[element][subElement][ + key + ]; type = typeName; func = funcName; - } else if (detailName === ElementNameType.labelIcon && key === 'visibility') { - const { typeName, funcName } = mappingDetailToFunc[detailName][key]; + } else if (element === ElementNameType.labelIcon && key === 'visibility') { + const { typeName, funcName } = mappingDetailToFunc[element][key]; type = typeName; func = funcName; } @@ -149,7 +149,7 @@ function poiStyling({ ) { func({ map, - layerNames: POI_LAYERS[subFeatureName as PoiSubFeature], + layerNames: POI_LAYERS[subFeature as PoiSubFeature], type, weight: style.visibility === 'none' ? INVISIBLE : VISIBLE, }); @@ -158,7 +158,7 @@ function poiStyling({ func({ map, - layerNames: POI_LAYERS[subFeatureName as PoiSubFeature], + layerNames: POI_LAYERS[subFeature as PoiSubFeature], type: type as StyleTypes, color: style.color, [key]: style[key as StyleKeyType], diff --git a/src/utils/map-styling/road-categories/stylingCategory.ts b/src/utils/map-styling/road-categories/stylingCategory.ts index 5e6b4f0..6e8ace0 100644 --- a/src/utils/map-styling/road-categories/stylingCategory.ts +++ b/src/utils/map-styling/road-categories/stylingCategory.ts @@ -28,9 +28,9 @@ export interface layerProps { export interface catergoryStylingProps { map: mapboxgl.Map; - subFeatureName: string; - detailName: ElementNameType; - subDetailName: SubElementNameType; + subFeature: string; + element: ElementNameType; + subElement: SubElementNameType; key: StyleKeyType; style: StyleType; layerNames: layerProps; @@ -39,17 +39,17 @@ export interface catergoryStylingProps { function stylingCategory({ layerNames, map, - subFeatureName, - detailName, - subDetailName, + subFeature, + element, + subElement, key, style, }: catergoryStylingProps): void { const { visibility, color, weight } = style; - + console.log(map); if (key === 'visibility') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) { + if (element === ElementNameType.section) { + if (subElement === SubElementNameType.fill) { applyVisibility({ map, layerNames: layerNames.line, @@ -60,20 +60,20 @@ function stylingCategory({ layerNames: layerNames.polygon, visibility, }); - } else if (subDetailName === SubElementNameType.stroke) + } else if (subElement === SubElementNameType.stroke) applyVisibility({ map, layerNames: layerNames.stroke, visibility, }); - } else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.fill) + } else if (element === ElementNameType.labelText) { + if (subElement === SubElementNameType.fill) applyVisibility({ map, layerNames: layerNames.text.noStroke, visibility, }); - else if (subDetailName === SubElementNameType.stroke) + else if (subElement === SubElementNameType.stroke) applyVisibility({ map, layerNames: layerNames.text.hasStroke, @@ -81,8 +81,8 @@ function stylingCategory({ }); } } else if (key === 'color' || key === 'saturation' || key === 'lightness') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) { + if (element === ElementNameType.section) { + if (subElement === SubElementNameType.fill) { applyColor({ map, layerNames: layerNames.line, @@ -97,7 +97,7 @@ function stylingCategory({ type: ColorType.fill, [key]: style[key as StyleKeyType], }); - } else if (subDetailName === SubElementNameType.stroke) { + } else if (subElement === SubElementNameType.stroke) { applyColor({ map, layerNames: layerNames.stroke, @@ -106,8 +106,8 @@ function stylingCategory({ [key]: style[key as StyleKeyType], }); } - } else if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.fill) { + } else if (element === ElementNameType.labelText) { + if (subElement === SubElementNameType.fill) { applyColor({ map, layerNames: layerNames.text.all, @@ -115,7 +115,7 @@ function stylingCategory({ type: ColorType.text, [key]: style[key as StyleKeyType], }); - } else if (subDetailName === SubElementNameType.stroke) { + } else if (subElement === SubElementNameType.stroke) { applyColor({ map, layerNames: layerNames.text.hasStroke, @@ -126,15 +126,15 @@ function stylingCategory({ } } } else if (key === 'weight') { - if (detailName === ElementNameType.section) { - if (subDetailName === SubElementNameType.fill) { + if (element === ElementNameType.section) { + if (subElement === SubElementNameType.fill) { applyWeight({ map, layerNames: layerNames.line, type: WeightType.line, weight, }); - } else if (subDetailName === SubElementNameType.stroke) { + } else if (subElement === SubElementNameType.stroke) { applyWeight({ map, layerNames: layerNames.stroke, @@ -143,8 +143,8 @@ function stylingCategory({ }); } } - if (detailName === ElementNameType.labelText) { - if (subDetailName === SubElementNameType.stroke) + if (element === ElementNameType.labelText) { + if (subElement === SubElementNameType.stroke) applyWeight({ map, layerNames: layerNames.text.hasStroke, diff --git a/src/utils/map-styling/road.ts b/src/utils/map-styling/road.ts index 83d8ea4..67243d9 100644 --- a/src/utils/map-styling/road.ts +++ b/src/utils/map-styling/road.ts @@ -7,9 +7,9 @@ import stylingCategory from './road-categories/stylingCategory'; function roadStyling({ map, - subFeatureName, - detailName, - subDetailName, + subFeature, + element, + subElement, key, style, }: stylingProps): void { @@ -23,12 +23,11 @@ function roadStyling({ }; stylingCategory({ - layerNames: - mappingSubFeatureLayerNames[subFeatureName as RoadSubFeatureType], + layerNames: mappingSubFeatureLayerNames[subFeature as RoadSubFeatureType], map, - subFeatureName, - detailName, - subDetailName, + subFeature, + element, + subElement, key, style, }); diff --git a/src/utils/map-styling/transit.ts b/src/utils/map-styling/transit.ts index c0e9cd9..1295fcf 100644 --- a/src/utils/map-styling/transit.ts +++ b/src/utils/map-styling/transit.ts @@ -30,30 +30,30 @@ const LabelLayers = ['mapbox-airport-label', 'transit-bus-label']; function transitStyling({ map, - subFeatureName, + subFeature, key, - detailName, - subDetailName, + element, + subElement, style, }: stylingProps): void { // TODO: labelIcon 관련 구현 - if (detailName === ElementNameType.labelIcon) return; + if (element === ElementNameType.labelIcon) return; let layerNames: string[] = - subDetailName === SubElementNameType.fill + subElement === SubElementNameType.fill ? key === StyleKeyType.weight ? [] - : detailName === ElementNameType.labelText + : element === ElementNameType.labelText ? [...LabelLayers] : [...PolygonLayers] - : detailName === ElementNameType.labelText + : element === ElementNameType.labelText ? [...LabelLayers] : [...LineLayers]; layerNames = - subFeatureName === 'all' + subFeature === 'all' ? layerNames - : layerNames.filter((layer) => layer.includes(subFeatureName)); + : layerNames.filter((layer) => layer.includes(subFeature)); if (layerNames.length === 0) return; @@ -78,11 +78,11 @@ function transitStyling({ const ligKey = StyleKeyType.lightness; styleType = - detailName === ElementNameType.labelText - ? subDetailName === SubElementNameType.fill + element === ElementNameType.labelText + ? subElement === SubElementNameType.fill ? ColorType.text : ColorType.textHalo - : subDetailName === SubElementNameType.fill + : subElement === SubElementNameType.fill ? ColorType.fill : ColorType.line; @@ -103,7 +103,7 @@ function transitStyling({ case StyleKeyType.weight: styleType = - detailName === ElementNameType.labelText + element === ElementNameType.labelText ? WeightType.textHalo : WeightType.line; applyWeight({ diff --git a/src/utils/map-styling/water.ts b/src/utils/map-styling/water.ts index e32d2ba..df3c3d3 100644 --- a/src/utils/map-styling/water.ts +++ b/src/utils/map-styling/water.ts @@ -19,16 +19,16 @@ const layers = { function waterStyling({ map, - detailName, - subDetailName, + element, + subElement, key, style, }: stylingProps): void { - if (detailName === ElementNameType.labelIcon) return; + if (element === ElementNameType.labelIcon) return; - if (detailName === ElementNameType.labelText) { + if (element === ElementNameType.labelText) { // labelText - fill - if (subDetailName === SubElementNameType.fill) { + if (subElement === SubElementNameType.fill) { if (key === StyleKeyType.weight) return; if (key === StyleKeyType.visibility) { const styleValue = style[key] === 'none' ? 'none' : 'visible'; @@ -73,10 +73,7 @@ function waterStyling({ } // section - stroke - if ( - subDetailName === SubElementNameType.stroke || - key === StyleKeyType.weight - ) + if (subElement === SubElementNameType.stroke || key === StyleKeyType.weight) return; // section - fill From 748ea545c4696fd67fe081974a841e937a936399 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 14:09:59 +0900 Subject: [PATCH 114/138] =?UTF-8?q?[chore]=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C,=20POI=20=EC=95=84=EC=9D=B4=EC=BD=98=20Weigh?= =?UTF-8?q?t=20=EC=84=A4=EC=A0=95=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/map-styling/poi.ts | 2 +- src/utils/map-styling/road-categories/stylingCategory.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/map-styling/poi.ts b/src/utils/map-styling/poi.ts index e4f92ad..4aa1ed8 100644 --- a/src/utils/map-styling/poi.ts +++ b/src/utils/map-styling/poi.ts @@ -26,7 +26,7 @@ type POI_LAYERS_TYPE = { [name in PoiSubFeature]: string[]; }; -const VISIBLE = -0.001; +const VISIBLE = 1; const INVISIBLE = 0; const POI_LAYERS: POI_LAYERS_TYPE = { all: [ diff --git a/src/utils/map-styling/road-categories/stylingCategory.ts b/src/utils/map-styling/road-categories/stylingCategory.ts index 6e8ace0..e60ed56 100644 --- a/src/utils/map-styling/road-categories/stylingCategory.ts +++ b/src/utils/map-styling/road-categories/stylingCategory.ts @@ -46,7 +46,6 @@ function stylingCategory({ style, }: catergoryStylingProps): void { const { visibility, color, weight } = style; - console.log(map); if (key === 'visibility') { if (element === ElementNameType.section) { if (subElement === SubElementNameType.fill) { From b49c30fe826f4b5dfadacf15435973fadd32541b Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 14:18:49 +0900 Subject: [PATCH 115/138] =?UTF-8?q?[fix]=20=ED=8F=89=EC=A7=80=20background?= =?UTF-8?q?=EB=A1=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - background의 경우 맵박스 함수가 달라서 구분하도록 처리 --- src/utils/applyStyle.ts | 1 + src/utils/map-styling/landscape.ts | 39 +++++++++++++++++++----------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/utils/applyStyle.ts b/src/utils/applyStyle.ts index db26af6..75c35d0 100644 --- a/src/utils/applyStyle.ts +++ b/src/utils/applyStyle.ts @@ -8,6 +8,7 @@ export enum ColorType { fill = 'fill-color', line = 'line-color', text = 'text-color', + background = 'background-color', textHalo = 'text-halo-color', icon = 'icon-opacity', } diff --git a/src/utils/map-styling/landscape.ts b/src/utils/map-styling/landscape.ts index f618334..8a97622 100644 --- a/src/utils/map-styling/landscape.ts +++ b/src/utils/map-styling/landscape.ts @@ -21,21 +21,21 @@ type LandscapeSubFeature = | 'landcover' | 'mountain'; -interface SubDetailType { +interface SubElementsType { fill: string[]; stroke: string[]; } -interface DetailType { - section: SubDetailType; - labelText: SubDetailType; - labelIcon: SubDetailType; +interface ElementsType { + section: SubElementsType; + labelText: SubElementsType; + labelIcon: SubElementsType; } function makeSubElement( fillLayers: string[], strokeLayers: string[] -): SubDetailType { +): SubElementsType { return { fill: fillLayers, stroke: strokeLayers }; } @@ -46,10 +46,11 @@ const buildingSectionFill = ['landscape-building', 'building']; const buildingSectionStroke = ['building-outline']; const buildingLabel = ['building-number-label', 'poi-building-label']; -const naturalSectionFill = ['landscape-natural']; +const naturalSectionFill = ['landscape-natural', 'national-park']; const naturalLabel = ['natural-point-label']; const landCoverSectionFill = [ + 'land', 'landscape-landcover', 'landcover', 'landuse', @@ -59,7 +60,7 @@ const landCoverSectionStroke = ['land-structure-line']; const mountainCoverSectionFill = ['hillshade']; -const layersByType: { [key in LandscapeSubFeature]: DetailType } = { +const layersByType: { [key in LandscapeSubFeature]: ElementsType } = { 'human-made': { [SECTION]: makeSubElement(humanMadeSectionFill, humanMadeSectionStroke), [LABELTEXT]: makeSubElement([], []), @@ -276,14 +277,16 @@ function landscapeStyling({ if (!type) { return; } + + let layerNames = + layersByType[subFeature as LandscapeSubFeature][element][subElement]; if ( key === 'visibility' && (type === ColorType.icon || type === WeightType.textHalo) ) { func({ map, - layerNames: - layersByType[subFeature as LandscapeSubFeature][element][subElement], + layerNames, type, weight: style.visibility === 'none' ? INVISIBLE : VISIBLE, }); @@ -292,16 +295,24 @@ function landscapeStyling({ if (key === 'visibility') { func({ map, - layerNames: - layersByType[subFeature as LandscapeSubFeature][element][subElement], + layerNames, visibility: style.visibility, }); } + if (layerNames.includes('land')) { + func({ + map, + layerNames: ['land'], + type: ColorType.background, + color: style.color, + }); + layerNames = layerNames.filter((item) => item !== 'land'); + } + func({ map, - layerNames: - layersByType[subFeature as LandscapeSubFeature][element][subElement], + layerNames, type: type as StyleTypes, color: style.color, [key]: style[key as StyleKeyType], From 05594828040868fee3b22b27c8a2fac5a3c11630 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 14:19:12 +0900 Subject: [PATCH 116/138] =?UTF-8?q?[refactor]=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/sidebar/useStyleType.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index 2c79394..6f610e5 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -3,7 +3,6 @@ import { useSelector, useDispatch } from 'react-redux'; import { useCallback } from 'react'; import { RootState } from '../../store'; import { - FeatureNameType, StyleType, StyleKeyType, ElementNameType, From 6f8db7138f7cdc240eaa41067e0feb6e78397f52 Mon Sep 17 00:00:00 2001 From: gitgitwi Date: Thu, 3 Dec 2020 16:24:21 +0900 Subject: [PATCH 117/138] =?UTF-8?q?[FEAT]=20Sidebar=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=20=EC=9C=84=ED=95=9C=20action/reducer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Feature/Element 선택 내용에 대한 상태 저장 --- src/store/sidebar/action.ts | 46 ++++++++++++++++++++++++++ src/store/sidebar/reducer.ts | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 src/store/sidebar/action.ts create mode 100644 src/store/sidebar/reducer.ts diff --git a/src/store/sidebar/action.ts b/src/store/sidebar/action.ts new file mode 100644 index 0000000..58c00d3 --- /dev/null +++ b/src/store/sidebar/action.ts @@ -0,0 +1,46 @@ +import { + FeatureNameType, + ElementNameType, + SubElementNameType, +} from '../common/type'; + +export const SET_FEATURE = 'SET_FEATURE' as const; +export const SET_SUBFEATURE = 'SET_SUBFEATURE' as const; +export const SET_ELEMENT = 'SET_ELEMENT' as const; +export const SET_SUBELEMENT = 'SET_SUBELEMENT' as const; + +export interface SidebarActionType { + type: + | typeof SET_FEATURE + | typeof SET_SUBFEATURE + | typeof SET_ELEMENT + | typeof SET_SUBELEMENT; + payload: { + feature?: FeatureNameType; + subFeature?: string; + element?: ElementNameType; + subElement?: SubElementNameType; + }; +} + +export const setFeature = (feature: FeatureNameType): SidebarActionType => ({ + type: SET_FEATURE, + payload: { feature }, +}); + +export const setSubFeature = (subFeature: string): SidebarActionType => ({ + type: SET_SUBFEATURE, + payload: { subFeature }, +}); + +export const setElement = (element: ElementNameType): SidebarActionType => ({ + type: SET_ELEMENT, + payload: { element }, +}); + +export const setSubElement = ( + subElement: SubElementNameType +): SidebarActionType => ({ + type: SET_SUBELEMENT, + payload: { subElement }, +}); diff --git a/src/store/sidebar/reducer.ts b/src/store/sidebar/reducer.ts new file mode 100644 index 0000000..90193d2 --- /dev/null +++ b/src/store/sidebar/reducer.ts @@ -0,0 +1,63 @@ +import { + FeatureNameType, + ElementNameType, + SubElementNameType, + SidebarActionType, +} from '../common/type'; +import { + SET_FEATURE, + SET_SUBFEATURE, + SET_ELEMENT, + SET_SUBELEMENT, +} from './action'; + +interface SidebarStateType { + feature?: FeatureNameType | ''; + subFeature?: string; + element?: ElementNameType | ''; + subElement?: SubElementNameType | ''; +} + +const initialState: SidebarStateType = { + feature: '', + subFeature: '', + element: '', + subElement: '', +}; + +function sidebarReducer( + state: SidebarStateType = initialState, + action: SidebarActionType +): SidebarStateType { + switch (action.type) { + case SET_FEATURE: { + return { + ...state, + feature: action.payload.feature, + }; + } + case SET_SUBFEATURE: { + return { + ...state, + subFeature: action.payload.subFeature, + }; + } + case SET_ELEMENT: { + return { + ...state, + element: action.payload.element, + }; + } + case SET_SUBELEMENT: { + return { + ...state, + + subElement: action.payload.subElement, + }; + } + default: + return state; + } +} + +export default sidebarReducer; From 5cb852cbb8864deb1096b2586958618953088c4d Mon Sep 17 00:00:00 2001 From: gitgitwi Date: Thu, 3 Dec 2020 16:25:21 +0900 Subject: [PATCH 118/138] =?UTF-8?q?[FEAT]=20Sidebar=20action=20type=20?= =?UTF-8?q?=EC=A7=80=EC=A0=95=20=EB=B0=8F=20store=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/common/type.ts | 11 +++++++++++ src/store/index.ts | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 1b7dc90..3ff2e9a 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,4 +1,10 @@ import { init, setStyle } from '../style/action'; +import { + setFeature, + setSubFeature, + setElement, + setSubElement, +} from '../sidebar/action'; export type hello = 'landmark'; @@ -70,6 +76,11 @@ export interface ElementPropsType extends FeaturePropsType { } export type ActionType = ReturnType | ReturnType; +export type SidebarActionType = + | ReturnType + | ReturnType + | ReturnType + | ReturnType; export interface ActionPayload extends ElementPropsType { style: StyleType; diff --git a/src/store/index.ts b/src/store/index.ts index 439bf20..b18bbb9 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -10,6 +10,7 @@ import { waterReducer as water, markerReducer as marker, } from './style/reducer'; +import sidebar from './sidebar/reducer'; const rootReducer = combineReducers({ map, @@ -20,6 +21,7 @@ const rootReducer = combineReducers({ transit, water, marker, + sidebar, }); const store = createStore(rootReducer); From 4f60cdeb7645d7ae0fbe6427fe42071fa858dbcb Mon Sep 17 00:00:00 2001 From: gitgitwi Date: Thu, 3 Dec 2020 16:28:07 +0900 Subject: [PATCH 119/138] =?UTF-8?q?[FEAT]=20sidebar=20hooks=EC=97=90=20act?= =?UTF-8?q?ion=20=EB=B0=8F=20dispatcher=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각각의 Type/SubType에 맞는 action 등록 --- src/hooks/sidebar/useSidebarType.ts | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/hooks/sidebar/useSidebarType.ts b/src/hooks/sidebar/useSidebarType.ts index 835c1a8..12fa538 100644 --- a/src/hooks/sidebar/useSidebarType.ts +++ b/src/hooks/sidebar/useSidebarType.ts @@ -1,4 +1,16 @@ import { useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { + setFeature, + setSubFeature, + setElement, + setSubElement, +} from '../../store/sidebar/action'; +import { + FeatureNameType, + ElementNameType, + SubElementNameType, +} from '../../store/common/type'; export interface SidebarHookType { sidebarTypeClickHandler: (name: string) => void; @@ -10,15 +22,33 @@ export interface SidebarHookType { function useSidebarType(): SidebarHookType { const [sidebarTypeName, setSidebarTypeName] = useState(''); const [sidebarSubTypeName, setSidebarSubTypeName] = useState(''); + const dispatch = useDispatch(); const sidebarTypeClickHandler = (name: string) => { if (name !== sidebarTypeName) { + if ( + name === ElementNameType.section || + name === ElementNameType.labelText || + name === ElementNameType.labelIcon + ) { + dispatch(setElement(name as ElementNameType)); + } else { + dispatch(setFeature(name as FeatureNameType)); + } setSidebarTypeName(name); } }; const sidebarSubTypeClickHandler = (name: string) => { if (name !== sidebarSubTypeName) { + if ( + name === SubElementNameType.fill || + name === SubElementNameType.stroke + ) { + dispatch(setSubElement(name)); + } else { + dispatch(setSubFeature(name)); + } setSidebarSubTypeName(name); } }; From c9db4ad4c582eb1b9eaa797a6a6aa3cf58e01d7f Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 18:52:30 +0900 Subject: [PATCH 120/138] =?UTF-8?q?[feat]=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EC=95=A1=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 스토어 상태와 동일한 형태로 전달받도록 하였고, 대신 변경되는 값만 전달되도록 하였음 --- src/store/common/type.ts | 32 ++++++++++++++++++++++++++++++-- src/store/style/action.ts | 15 ++++++++++++++- src/store/style/getReducer.ts | 2 +- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 1b7dc90..5b5ebe0 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,4 +1,4 @@ -import { init, setStyle } from '../style/action'; +import { init, setStyle, setWholeStyle } from '../style/action'; export type hello = 'landmark'; @@ -69,12 +69,40 @@ export interface ElementPropsType extends FeaturePropsType { subElement?: SubElementNameType; } -export type ActionType = ReturnType | ReturnType; +export type ActionType = + | ReturnType + | ReturnType + | ReturnType; export interface ActionPayload extends ElementPropsType { style: StyleType; } +export interface StyleActionPayload { + isChanged?: boolean; + visibility?: string; + color?: string; + weight?: number; + saturation?: number; + lightness?: number; +} +export interface SubElementActionPayload { + [SubElementNameType.fill]?: StyleActionPayload; + [SubElementNameType.stroke]?: StyleActionPayload; +} + +export interface ElemetnActionPayload { + [ElementNameType.section]?: SubElementActionPayload; + [ElementNameType.labelText]?: SubElementActionPayload; + [ElementNameType.labelIcon]?: StyleActionPayload; +} + +export type WholeStyleActionPayload = { + [featureName in FeatureNameType]?: { + [subFeatureName: string]: ElemetnActionPayload; + }; +}; + export interface StylePropsType { [SubElementNameType.fill]: string; [SubElementNameType.stroke]: string; diff --git a/src/store/style/action.ts b/src/store/style/action.ts index 441ec31..23ae85f 100644 --- a/src/store/style/action.ts +++ b/src/store/style/action.ts @@ -1,13 +1,19 @@ -import { ActionPayload } from '../common/type'; +import { ActionPayload, WholeStyleActionPayload } from '../common/type'; export const INIT = 'INIT' as const; export const SET = 'SET' as const; +export const SET_WHOLE = 'SET_WHOLE' as const; export interface SetType { type: typeof SET; payload: ActionPayload; } +export interface SetWholeType { + type: typeof SET_WHOLE; + payload: WholeStyleActionPayload; +} + export const init = (): { type: typeof INIT } => ({ type: INIT, }); @@ -22,3 +28,10 @@ export const setStyle = ({ type: SET, payload: { feature, subFeature, element, subElement, style }, }); + +export const setWholeStyle = ( + wholeStyle: WholeStyleActionPayload +): SetWholeType => ({ + type: SET_WHOLE, + payload: wholeStyle, +}); diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts index b835704..1315887 100644 --- a/src/store/style/getReducer.ts +++ b/src/store/style/getReducer.ts @@ -10,7 +10,7 @@ import { SubElementType, } from '../common/type'; import { getDefaultFeature } from './properties'; -import { INIT, SET } from './action'; +import { INIT, SET, SET_WHOLE } from './action'; import { checkStyleIsChanged, checkFeatureIsChanged } from './compareStyle'; interface ReducerType { From 7dc5f8484886384a961fb94bbaababa01d3fde07 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 18:53:25 +0900 Subject: [PATCH 121/138] =?UTF-8?q?[feat]=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=88=98=EC=A0=95=20reducer=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 변경된 사항만 업데이트 하기 위해서 combine 함수 만들었음 --- src/store/style/getReducer.ts | 19 +++++++++++++ src/store/style/manageCategories.ts | 41 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/store/style/manageCategories.ts diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts index 1315887..9ea77d9 100644 --- a/src/store/style/getReducer.ts +++ b/src/store/style/getReducer.ts @@ -12,6 +12,7 @@ import { import { getDefaultFeature } from './properties'; import { INIT, SET, SET_WHOLE } from './action'; import { checkStyleIsChanged, checkFeatureIsChanged } from './compareStyle'; +import { combineCategory } from './manageCategories'; interface ReducerType { (state: FeatureState, action: ActionType): FeatureState; @@ -72,6 +73,24 @@ export default function getReducer(IDX: number): ReducerType { return newState; } + case SET_WHOLE: { + const inputStyle = action.payload[renderingData[IDX].typeKey]; + const updateStyle = JSON.parse(JSON.stringify(initialState)); + + if (!inputStyle) return updateStyle; + + const categories = Object.keys(updateStyle); + categories.forEach((category) => { + combineCategory({ + element: category as ElementNameType, + elementStyle: inputStyle[category], + initialElementStyle: updateStyle[category], + }); + }); + + return updateStyle; + } + default: return state; } diff --git a/src/store/style/manageCategories.ts b/src/store/style/manageCategories.ts new file mode 100644 index 0000000..6703daf --- /dev/null +++ b/src/store/style/manageCategories.ts @@ -0,0 +1,41 @@ +import { + ElementNameType, + SubElementActionPayload, + StyleActionPayload, + ElemetnActionPayload, +} from '../common/type'; + +interface combineCatecoryProps { + element: ElementNameType; + elementStyle: ElemetnActionPayload; + initialElementStyle: ElemetnActionPayload; +} + +export function combineCategory({ + element, + elementStyle, + initialElementStyle, +}: combineCatecoryProps): ElemetnActionPayload { + const update = initialElementStyle; + if (element === 'labelIcon' && elementStyle[element]) { + (update[element] as StyleActionPayload) = { + ...update[element], + ...elementStyle[element], + }; + return update; + } + + const verifiedCategory = elementStyle[element] as SubElementActionPayload; + const verifiedUpdate = update[element] as SubElementActionPayload; + + if (verifiedCategory.fill) { + verifiedUpdate.fill = { ...verifiedUpdate.fill, ...verifiedCategory.fill }; + } + if (verifiedCategory.stroke) { + verifiedUpdate.stroke = { + ...verifiedUpdate.stroke, + ...verifiedCategory.stroke, + }; + } + return update; +} From 6fccd90a1aec1071b67a56a4514d11ba0072eebc Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 18:55:08 +0900 Subject: [PATCH 122/138] =?UTF-8?q?[feat]=20=EC=A0=84=EC=B2=B4=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD=20hook=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - input으로 전달된 값을 스토어로 dispatch 하도록 하였음. - 아직 맵 스타일도 수정하는 부분이 빠져있음 - 전체 레이어 업데이트 시간이 걸릴 것 같아서.. 논의가 필요함 --- src/hooks/common/useWholeStyle.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/hooks/common/useWholeStyle.ts diff --git a/src/hooks/common/useWholeStyle.ts b/src/hooks/common/useWholeStyle.ts new file mode 100644 index 0000000..6f1297c --- /dev/null +++ b/src/hooks/common/useWholeStyle.ts @@ -0,0 +1,26 @@ +import mapboxgl from 'mapbox-gl'; +import { useSelector, useDispatch } from 'react-redux'; +import { RootState } from '../../store'; +import { setWholeStyle } from '../../store/style/action'; +import { WholeStyleActionPayload } from '../../store/common/type'; +import * as mapStyling from '../../utils/map-styling'; + +interface WholeStyleHook { + changeStyle: (inputStyle: WholeStyleActionPayload) => void; +} + +function useWholeStyle(): WholeStyleHook { + const dispatch = useDispatch(); + + const map = useSelector((state) => state.map.map) as mapboxgl.Map; + + const changeStyle = (inputStyle: WholeStyleActionPayload): void => { + dispatch(setWholeStyle(inputStyle)); + }; + + return { + changeStyle, + }; +} + +export default useWholeStyle; From d7b84b316992d614e2e8ccfda4017f14397465c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 21:17:11 +0900 Subject: [PATCH 123/138] =?UTF-8?q?[feat]=20redux=EC=97=90=20sidebar?= =?UTF-8?q?=EC=9D=98=20=EC=83=81=ED=83=9C=EB=A5=BC=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=20state=EB=A5=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sidebar의 상태를 관리하기 위하여 reducer와 action을 추가하였다 - feature subFeature element SubElement의 네개의 상태를 저장한다 --- src/store/common/type.ts | 22 ++++++------- src/store/sidebar/action.ts | 61 ++++++++++++++---------------------- src/store/sidebar/reducer.ts | 59 +++++++++------------------------- 3 files changed, 49 insertions(+), 93 deletions(-) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 3ff2e9a..094d7e0 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -1,10 +1,5 @@ import { init, setStyle } from '../style/action'; -import { - setFeature, - setSubFeature, - setElement, - setSubElement, -} from '../sidebar/action'; +import { setSidebarProperties, initSidebarProperties } from '../sidebar/action'; export type hello = 'landmark'; @@ -77,15 +72,20 @@ export interface ElementPropsType extends FeaturePropsType { export type ActionType = ReturnType | ReturnType; export type SidebarActionType = - | ReturnType - | ReturnType - | ReturnType - | ReturnType; - + | ReturnType + | ReturnType; export interface ActionPayload extends ElementPropsType { style: StyleType; } +export interface PayloadPropsType { + key: 'feature' | 'subFeature' | 'element' | 'subElement'; + feature: FeatureNameType | null; + subFeature: string | null; + element: ElementNameType | null; + subElement: SubElementNameType | null; +} + export interface StylePropsType { [SubElementNameType.fill]: string; [SubElementNameType.stroke]: string; diff --git a/src/store/sidebar/action.ts b/src/store/sidebar/action.ts index 58c00d3..cb040f7 100644 --- a/src/store/sidebar/action.ts +++ b/src/store/sidebar/action.ts @@ -1,46 +1,31 @@ -import { - FeatureNameType, - ElementNameType, - SubElementNameType, -} from '../common/type'; +import { PayloadPropsType } from '../common/type'; -export const SET_FEATURE = 'SET_FEATURE' as const; -export const SET_SUBFEATURE = 'SET_SUBFEATURE' as const; -export const SET_ELEMENT = 'SET_ELEMENT' as const; -export const SET_SUBELEMENT = 'SET_SUBELEMENT' as const; +export const SET_SIDEBAR_PROPERTIES = 'SET_SIDEBAR_PROPERTIES' as const; +export const INIT_SIDEBAR_PROPERTIES = 'INIT_SIDEBAR_PROPERTIES' as const; export interface SidebarActionType { - type: - | typeof SET_FEATURE - | typeof SET_SUBFEATURE - | typeof SET_ELEMENT - | typeof SET_SUBELEMENT; - payload: { - feature?: FeatureNameType; - subFeature?: string; - element?: ElementNameType; - subElement?: SubElementNameType; - }; + type: typeof SET_SIDEBAR_PROPERTIES | typeof INIT_SIDEBAR_PROPERTIES; + payload: PayloadPropsType; } -export const setFeature = (feature: FeatureNameType): SidebarActionType => ({ - type: SET_FEATURE, - payload: { feature }, +export const setSidebarProperties = ({ + key, + feature, + subFeature, + element, + subElement, +}: PayloadPropsType): SidebarActionType => ({ + type: SET_SIDEBAR_PROPERTIES, + payload: { key, feature, subFeature, element, subElement }, }); -export const setSubFeature = (subFeature: string): SidebarActionType => ({ - type: SET_SUBFEATURE, - payload: { subFeature }, -}); - -export const setElement = (element: ElementNameType): SidebarActionType => ({ - type: SET_ELEMENT, - payload: { element }, -}); - -export const setSubElement = ( - subElement: SubElementNameType -): SidebarActionType => ({ - type: SET_SUBELEMENT, - payload: { subElement }, +export const initSidebarProperties = ({ + key, + feature, + subFeature, + element, + subElement, +}: PayloadPropsType): SidebarActionType => ({ + type: INIT_SIDEBAR_PROPERTIES, + payload: { key, feature, subFeature, element, subElement }, }); diff --git a/src/store/sidebar/reducer.ts b/src/store/sidebar/reducer.ts index 90193d2..bc79666 100644 --- a/src/store/sidebar/reducer.ts +++ b/src/store/sidebar/reducer.ts @@ -1,58 +1,29 @@ -import { - FeatureNameType, - ElementNameType, - SubElementNameType, - SidebarActionType, -} from '../common/type'; -import { - SET_FEATURE, - SET_SUBFEATURE, - SET_ELEMENT, - SET_SUBELEMENT, -} from './action'; +import { PayloadPropsType, SidebarActionType } from '../common/type'; +import { SET_SIDEBAR_PROPERTIES, INIT_SIDEBAR_PROPERTIES } from './action'; -interface SidebarStateType { - feature?: FeatureNameType | ''; - subFeature?: string; - element?: ElementNameType | ''; - subElement?: SubElementNameType | ''; -} - -const initialState: SidebarStateType = { - feature: '', - subFeature: '', - element: '', - subElement: '', +const initialState: PayloadPropsType = { + key: 'feature', + feature: null, + subFeature: null, + element: null, + subElement: null, }; function sidebarReducer( - state: SidebarStateType = initialState, + state: PayloadPropsType = initialState, action: SidebarActionType -): SidebarStateType { +): PayloadPropsType { switch (action.type) { - case SET_FEATURE: { - return { - ...state, - feature: action.payload.feature, - }; - } - case SET_SUBFEATURE: { + case SET_SIDEBAR_PROPERTIES: { return { ...state, - subFeature: action.payload.subFeature, + [action.payload.key]: action.payload[action.payload.key], }; } - case SET_ELEMENT: { + case INIT_SIDEBAR_PROPERTIES: { return { - ...state, - element: action.payload.element, - }; - } - case SET_SUBELEMENT: { - return { - ...state, - - subElement: action.payload.subElement, + ...initialState, + [action.payload.key]: action.payload[action.payload.key], }; } default: From 382da11f9f6719f98125b06d3f65456014330c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 21:19:20 +0900 Subject: [PATCH 124/138] =?UTF-8?q?[refactor]=20feature=20Component?= =?UTF-8?q?=EC=99=80=20hook=EC=9D=84=20sidebarProperties=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=9B=EC=95=84=EC=98=A4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useSidebar에서 feature / subFeature를 다루었었지만, 상태로 뺌으로서 store에서 필요한 항목을 가져오는 것으로 변경하였다 - 따라서 props로 feature / subFeature를 전달하는 것이 사라지게 된다 --- .../SidebarContentMore/FeatureType.tsx | 21 ++--- .../SidebarContentMore/FeatureTypeItem.tsx | 24 +++--- src/hooks/sidebar/useFeatureTypeItem.ts | 13 ++- src/hooks/sidebar/useSidebarType.ts | 84 +++++++++++-------- 4 files changed, 84 insertions(+), 58 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/FeatureType.tsx b/src/components/Sidebar/SidebarContentMore/FeatureType.tsx index 9818601..4bba352 100644 --- a/src/components/Sidebar/SidebarContentMore/FeatureType.tsx +++ b/src/components/Sidebar/SidebarContentMore/FeatureType.tsx @@ -6,12 +6,12 @@ import DetailType from './DetailType'; import useSidebarType, { SidebarHookType, } from '../../../hooks/sidebar/useSidebarType'; -import { FeatureNameType } from '../../../store/common/type'; +// import { FeatureNameType } from '../../../store/common/type'; import FeatureTypeItem from './FeatureTypeItem'; interface WrapperProps { - isFeatureName: string; + isFeatureName: string | null; } const FeatureTypeWrapper = styled.ul` @@ -29,33 +29,28 @@ const FeatureTypeTitle = styled.h2` function FeatureType(): React.ReactElement { const { + feature, + // subFeature, sidebarTypeClickHandler, sidebarSubTypeClickHandler, - sidebarTypeName, - sidebarSubTypeName, }: SidebarHookType = useSidebarType(); return ( <> - + 기능 유형 - {data.map(({ typeKey, typeName, features }) => ( + {data.map(({ typeKey, typeName, subFeatures }) => ( ))} - + ); } diff --git a/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx b/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx index cf44206..f80d752 100644 --- a/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx +++ b/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx @@ -1,7 +1,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { FeaturesType } from '../../../utils/rendering-data/featureTypeData'; -import { FeatureNameType } from '../../../store/common/type'; +import { FeatureNameType, FeatureState } from '../../../store/common/type'; import useFeatureTypeItemHook from '../../../hooks/sidebar/useFeatureTypeItem'; interface ListProps { @@ -60,9 +60,9 @@ const Pointer = styled.span` interface FeatureTypeItemProps { typeKey: FeatureNameType; typeName: string; - features: FeaturesType[]; - sidebarTypeName: string; - sidebarSubTypeName: string; + subFeatures: FeaturesType[]; + // sidebarTypeName: string; + // sidebarSubTypeName: string; sidebarTypeClickHandler: (name: string) => void; sidebarSubTypeClickHandler: (name: string) => void; } @@ -70,18 +70,20 @@ interface FeatureTypeItemProps { function FeatureTypeItem({ typeKey, typeName, - features, - sidebarTypeName, - sidebarSubTypeName, + subFeatures, + // sidebarTypeName, + // sidebarSubTypeName, sidebarTypeClickHandler, sidebarSubTypeClickHandler, }: FeatureTypeItemProps): React.ReactElement { - const { featureList } = useFeatureTypeItemHook({ featureName: typeKey }); + const { featureList, feature, subFeature } = useFeatureTypeItemHook({ + featureName: typeKey, + }); return ( <> { sidebarTypeClickHandler(typeKey); sidebarSubTypeClickHandler('all'); @@ -91,10 +93,10 @@ function FeatureTypeItem({ {typeName} {'>'} - {features?.map(({ key, name }) => ( + {subFeatures.map(({ key, name }) => ( { sidebarTypeClickHandler(typeKey); sidebarSubTypeClickHandler(key); diff --git a/src/hooks/sidebar/useFeatureTypeItem.ts b/src/hooks/sidebar/useFeatureTypeItem.ts index 994c51a..eb510d3 100644 --- a/src/hooks/sidebar/useFeatureTypeItem.ts +++ b/src/hooks/sidebar/useFeatureTypeItem.ts @@ -1,9 +1,15 @@ import { useSelector } from 'react-redux'; import { RootState } from '../../store'; -import { FeatureState, FeatureNameType } from '../../store/common/type'; +import { + FeatureState, + FeatureNameType, + PayloadPropsType, +} from '../../store/common/type'; interface useFeatureTypeItemType { featureList: FeatureState; + feature: FeatureNameType | null; + subFeature: string | null; } export interface useFeatureTypeItemProps { @@ -13,12 +19,17 @@ export interface useFeatureTypeItemProps { function useFeatureTypeItem({ featureName, }: useFeatureTypeItemProps): useFeatureTypeItemType { + const { feature, subFeature } = useSelector( + (state) => state.sidebar + ) as PayloadPropsType; const featureList = useSelector( (state) => state[featureName] ) as FeatureState; return { featureList, + feature, + subFeature, }; } diff --git a/src/hooks/sidebar/useSidebarType.ts b/src/hooks/sidebar/useSidebarType.ts index 12fa538..26f2b72 100644 --- a/src/hooks/sidebar/useSidebarType.ts +++ b/src/hooks/sidebar/useSidebarType.ts @@ -1,63 +1,81 @@ -import { useState } from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { - setFeature, - setSubFeature, - setElement, - setSubElement, + setSidebarProperties, + initSidebarProperties, } from '../../store/sidebar/action'; +import { RootState } from '../../store/index'; import { FeatureNameType, ElementNameType, SubElementNameType, + PayloadPropsType, } from '../../store/common/type'; export interface SidebarHookType { sidebarTypeClickHandler: (name: string) => void; sidebarSubTypeClickHandler: (name: string) => void; - sidebarTypeName: string; - sidebarSubTypeName: string; + feature: FeatureNameType | null; + subFeature: string | null; + element: ElementNameType | null; + subElement: SubElementNameType | null; } function useSidebarType(): SidebarHookType { - const [sidebarTypeName, setSidebarTypeName] = useState(''); - const [sidebarSubTypeName, setSidebarSubTypeName] = useState(''); const dispatch = useDispatch(); + const sidebarStates = useSelector( + (state) => state.sidebar + ) as PayloadPropsType; + const { feature, subFeature, element, subElement } = sidebarStates; const sidebarTypeClickHandler = (name: string) => { - if (name !== sidebarTypeName) { - if ( - name === ElementNameType.section || - name === ElementNameType.labelText || - name === ElementNameType.labelIcon - ) { - dispatch(setElement(name as ElementNameType)); - } else { - dispatch(setFeature(name as FeatureNameType)); - } - setSidebarTypeName(name); + if ([feature, element].includes(name as any)) return; + if (Object.keys(ElementNameType).includes(name)) { + dispatch( + setSidebarProperties({ + ...sidebarStates, + element: name as ElementNameType, + key: 'element', + }) + ); + } else { + dispatch( + initSidebarProperties({ + ...sidebarStates, + feature: name as FeatureNameType, + key: 'feature', + }) + ); } }; const sidebarSubTypeClickHandler = (name: string) => { - if (name !== sidebarSubTypeName) { - if ( - name === SubElementNameType.fill || - name === SubElementNameType.stroke - ) { - dispatch(setSubElement(name)); - } else { - dispatch(setSubFeature(name)); - } - setSidebarSubTypeName(name); + if ([subFeature, subElement].includes(name)) return; + if (Object.keys(SubElementNameType).includes(name)) { + dispatch( + setSidebarProperties({ + ...sidebarStates, + subElement: name as SubElementNameType, + key: 'subElement', + }) + ); + } else { + dispatch( + setSidebarProperties({ + ...sidebarStates, + subFeature: name, + key: 'subFeature', + }) + ); } }; return { sidebarTypeClickHandler, sidebarSubTypeClickHandler, - sidebarTypeName, - sidebarSubTypeName, + feature, + subFeature, + element, + subElement, }; } From 7b040a6ec014d8602c6aa458050c77c310a095c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 21:21:51 +0900 Subject: [PATCH 125/138] =?UTF-8?q?[refactor]=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=EB=B0=94=EC=97=90=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EB=A0=8C=EB=8D=94=EB=A7=81=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EC=9D=98=20key=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - feature / subFeature로 명명법을 변경하였어서, 맞추어서 데이터의 key값을 변경 --- src/utils/rendering-data/featureTypeData.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/utils/rendering-data/featureTypeData.ts b/src/utils/rendering-data/featureTypeData.ts index 7e6a4f7..b79a290 100644 --- a/src/utils/rendering-data/featureTypeData.ts +++ b/src/utils/rendering-data/featureTypeData.ts @@ -7,14 +7,14 @@ export interface FeaturesType { export interface DataType { typeKey: FeatureNameType; typeName: string; - features: FeaturesType[]; + subFeatures: FeaturesType[]; } const data: DataType[] = [ { typeKey: FeatureNameType.poi, typeName: 'POI', - features: [ + subFeatures: [ { key: 'landmark', name: '랜드마크' }, { key: 'business', name: '상업시설' }, { key: 'government', name: '공공시설' }, @@ -29,7 +29,7 @@ const data: DataType[] = [ { typeKey: FeatureNameType.road, typeName: '도로', - features: [ + subFeatures: [ { key: 'arterial', name: '주요도로' }, { key: 'local', name: '일반도로' }, { key: 'sidewalk', name: '인도' }, @@ -38,7 +38,7 @@ const data: DataType[] = [ { typeKey: FeatureNameType.administrative, typeName: '행정구역', - features: [ + subFeatures: [ { key: 'country', name: '국가' }, { key: 'state', name: '도/주' }, { key: 'locality', name: '그외' }, @@ -47,7 +47,7 @@ const data: DataType[] = [ { typeKey: FeatureNameType.landscape, typeName: '경관', - features: [ + subFeatures: [ { key: 'human-made', name: '인공물' }, { key: 'building', name: '건물' }, { key: 'natural', name: '자연물' }, @@ -58,15 +58,15 @@ const data: DataType[] = [ { typeKey: FeatureNameType.transit, typeName: '교통', - features: [ + subFeatures: [ { key: 'airport', name: '공항' }, { key: 'bus', name: '버스' }, { key: 'rail', name: '철도' }, { key: 'subway', name: '지하철' }, ], }, - { typeKey: FeatureNameType.water, typeName: '물', features: [] }, - { typeKey: FeatureNameType.marker, typeName: '마크', features: [] }, + { typeKey: FeatureNameType.water, typeName: '물', subFeatures: [] }, + { typeKey: FeatureNameType.marker, typeName: '마크', subFeatures: [] }, ]; export default data; From 8febfdcfd78cbc520c5b76e2349657ee07e640f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 21:23:03 +0900 Subject: [PATCH 126/138] =?UTF-8?q?[refactor]=20DetailType=20Component?= =?UTF-8?q?=EC=99=80=20hook=EC=9D=84=20sidebarProperties=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=9B=EC=95=84=EC=98=A4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이전의 커밋과 동일하게 현재 선택된 사이드바의 정보들을 스토어에서 받아온다. - 따라서 props로 전달하는 양이 줄어든다 --- .../Sidebar/SidebarContentMore/DetailType.tsx | 30 +++++++---------- src/hooks/sidebar/useDetailType.ts | 32 ++++++++----------- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 4f147b2..3734c10 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -62,13 +62,12 @@ const CheckRight = styled.div` color: ${(props) => props.theme.GREEN}; `; -function DetailType({ - feature, - subFeature, -}: FeaturePropsType): React.ReactElement { +function DetailType(): React.ReactElement { const { - sidebarTypeName, - sidebarSubTypeName, + feature, + subFeature, + element, + subElement, sidebarTypeClickHandler, sidebarSubTypeClickHandler, }: SidebarHookType = useSidebarType(); @@ -80,10 +79,6 @@ function DetailType({ }: UseDetailHookType = useDetailType({ sidebarTypeClickHandler, sidebarSubTypeClickHandler, - sidebarTypeName, - sidebarSubTypeName, - feature, - subFeature, }); if (!feature) { @@ -115,8 +110,8 @@ function DetailType({ {section?.stroke.isChanged ? : <>} { @@ -193,12 +188,11 @@ function DetailType({ )} - + {element ? : <>} + {/* // feature={feature} + // subFeature={subFeature} + // element={element as ElementNameType} + // subElement={subElement as SubElementNameType} */} ); } diff --git a/src/hooks/sidebar/useDetailType.ts b/src/hooks/sidebar/useDetailType.ts index 4fdfb51..5cf67aa 100644 --- a/src/hooks/sidebar/useDetailType.ts +++ b/src/hooks/sidebar/useDetailType.ts @@ -1,17 +1,13 @@ import { useSelector } from 'react-redux'; import { RootState } from '../../store'; import { - FeatureNameType, ElementNameType, SubElementNameType, FeatureType, + PayloadPropsType, } from '../../store/common/type'; interface UseDetailTypeProps { - feature: FeatureNameType; - subFeature: string; - sidebarTypeName: string; - sidebarSubTypeName: string; sidebarTypeClickHandler: (name: string) => void; sidebarSubTypeClickHandler: (name: string) => void; } @@ -34,15 +30,15 @@ const dummyDetail = { }; function useDetailType({ - feature, - subFeature, - sidebarTypeName, - sidebarSubTypeName, sidebarTypeClickHandler, sidebarSubTypeClickHandler, }: UseDetailTypeProps): UseDetailHookType { + const { feature, subFeature, element, subElement } = useSelector( + (state) => state.sidebar + ) as PayloadPropsType; + const detail = useSelector((state) => { - if (!feature) { + if (!feature || !subFeature) { return dummyDetail; } @@ -50,19 +46,19 @@ function useDetailType({ }) as FeatureType; const styleClickHandler = ( - element: ElementNameType, - subElement?: SubElementNameType + elementName: ElementNameType, + subElementName?: SubElementNameType ) => { - sidebarTypeClickHandler(element); - if (subElement) sidebarSubTypeClickHandler(subElement); + sidebarTypeClickHandler(elementName); + if (subElementName) sidebarSubTypeClickHandler(subElementName); }; const checkIsSelected = ( - element: ElementNameType, - subElement?: SubElementNameType + elementName: ElementNameType, + subElementName?: SubElementNameType ) => { - if (!subElement) return sidebarTypeName === element; - return sidebarTypeName === element && sidebarSubTypeName === subElement; + if (!subElementName) return elementName === element; + return elementName === element && subElementName === subElement; }; return { From 6a487590df4db8e93cfd98353ad881ab98a6b49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 21:24:39 +0900 Subject: [PATCH 127/138] =?UTF-8?q?[refactor]=20Style=20Component=EC=99=80?= =?UTF-8?q?=20hook=EC=9D=84=20sidebarProperties=EC=83=81=ED=83=9C=EB=A5=BC?= =?UTF-8?q?=20=EB=B0=9B=EC=95=84=EC=98=A4=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이전 커밋과 마찬가지로 현재 선택한 사이드바의 요소들을 스토어에서 가져온다 - 따라서 props로 전달받는 것이 줄어들게 된다. --- .../Sidebar/SidebarContentMore/Styler.tsx | 15 ++-------- src/hooks/sidebar/useStyleType.ts | 29 ++++++++++++++----- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/Styler.tsx b/src/components/Sidebar/SidebarContentMore/Styler.tsx index 34d2ed0..c096380 100644 --- a/src/components/Sidebar/SidebarContentMore/Styler.tsx +++ b/src/components/Sidebar/SidebarContentMore/Styler.tsx @@ -1,6 +1,6 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; -import { ElementPropsType } from '../../../store/common/type'; +// import { ElementPropsType } from '../../../store/common/type'; import useStyleType, { UseStyleHookType, } from '../../../hooks/sidebar/useStyleType'; @@ -35,21 +35,12 @@ const Hr = styled.hr` color: ${(props) => props.theme.GREY}; `; -function Styler({ - feature, - subFeature, - element, - subElement, -}: ElementPropsType): React.ReactElement { +function Styler(): React.ReactElement { const { styleElement: { visibility, color, weight, saturation, lightness }, onStyleChange, - }: UseStyleHookType = useStyleType({ - feature, - subFeature, element, - subElement, - }); + }: UseStyleHookType = useStyleType(); if (!element) { return <>; diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index 6f610e5..f3c4870 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -8,6 +8,8 @@ import { ElementNameType, SubElementNameType, ElementPropsType, + PayloadPropsType, + FeatureNameType, } from '../../store/common/type'; import { setStyle } from '../../store/style/action'; import { getDefaultStyle } from '../../store/style/properties'; @@ -16,26 +18,34 @@ import * as mapStyling from '../../utils/map-styling'; export interface UseStyleHookType { styleElement: StyleType; onStyleChange: (key: StyleKeyType, value: string | number) => void; + element: ElementNameType | null; } -function useStyleType({ - feature, - subFeature, - element, - subElement, -}: ElementPropsType): UseStyleHookType { +function useStyleType(): UseStyleHookType { + // { + // feature, + // subFeature, + // element, + // subElement, + // }: ElementPropsType const dispatch = useDispatch(); + const { feature, subFeature, element, subElement } = useSelector( + (state) => state.sidebar + ) as PayloadPropsType; const map = useSelector((state) => state.map.map) as mapboxgl.Map; const styleElement = useSelector((state) => { - if (!element) { + if (!feature || !subFeature || !element) { + // ?!? return getDefaultStyle({ feature, subFeature, element, subElement, - }); + } as ElementPropsType); } + // if (!feature || !subFeature || !element) return null; + console.log(state, feature, subFeature); const newFeature = state[feature][subFeature]; if (element === ElementNameType.labelIcon) return newFeature[element]; return newFeature[element][subElement as SubElementNameType]; @@ -43,6 +53,8 @@ function useStyleType({ const onStyleChange = useCallback( (key: StyleKeyType, value: string | number) => { + if (!feature || !subFeature || !element || !subElement) return; + mapStyling[feature]({ map, subFeature, @@ -74,6 +86,7 @@ function useStyleType({ return { styleElement, onStyleChange, + element, }; } From 8b039e0bf45c2802ebf7423359b71e94101c4e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 21:26:29 +0900 Subject: [PATCH 128/138] =?UTF-8?q?[fix]=20getReducer=EC=9D=98=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 렌더링 데이터의 key값 변경으로 알맞게 바꾸어줌 --- src/store/style/getReducer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts index b835704..b9ecb80 100644 --- a/src/store/style/getReducer.ts +++ b/src/store/style/getReducer.ts @@ -20,7 +20,7 @@ interface ReducerType { export default function getReducer(IDX: number): ReducerType { const subFeatures = [ 'all', - ...(renderingData[IDX].features?.map((v) => v.key) as string[]), + ...(renderingData[IDX].subFeatures?.map((v) => v.key) as string[]), ]; const initialState = subFeatures.reduce((acc: FeatureState, cur: string) => { From e82d174d2d58482999d2a66a0a8c0da91c7e9f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 21:39:15 +0900 Subject: [PATCH 129/138] =?UTF-8?q?[refactor]=20useStyleType=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=82=AD=EC=A0=9C=ED=9B=84=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EB=A1=9C=EC=A7=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 주석을 삭제 - getDefaultStyle은 더이상 필요없다. 사이드바의 상태관리를 스토어에서 해주고, 그것을 필요한 컴포넌트들이 받아쓰기 때문이다 --- src/hooks/sidebar/useStyleType.ts | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/hooks/sidebar/useStyleType.ts b/src/hooks/sidebar/useStyleType.ts index f3c4870..756eafb 100644 --- a/src/hooks/sidebar/useStyleType.ts +++ b/src/hooks/sidebar/useStyleType.ts @@ -7,12 +7,9 @@ import { StyleKeyType, ElementNameType, SubElementNameType, - ElementPropsType, PayloadPropsType, - FeatureNameType, } from '../../store/common/type'; import { setStyle } from '../../store/style/action'; -import { getDefaultStyle } from '../../store/style/properties'; import * as mapStyling from '../../utils/map-styling'; export interface UseStyleHookType { @@ -22,12 +19,6 @@ export interface UseStyleHookType { } function useStyleType(): UseStyleHookType { - // { - // feature, - // subFeature, - // element, - // subElement, - // }: ElementPropsType const dispatch = useDispatch(); const { feature, subFeature, element, subElement } = useSelector( @@ -36,16 +27,9 @@ function useStyleType(): UseStyleHookType { const map = useSelector((state) => state.map.map) as mapboxgl.Map; const styleElement = useSelector((state) => { if (!feature || !subFeature || !element) { - // ?!? - return getDefaultStyle({ - feature, - subFeature, - element, - subElement, - } as ElementPropsType); + return null; } - // if (!feature || !subFeature || !element) return null; - console.log(state, feature, subFeature); + const newFeature = state[feature][subFeature]; if (element === ElementNameType.labelIcon) return newFeature[element]; return newFeature[element][subElement as SubElementNameType]; From 2010bc1904f3aa5f55b2c9d2751b5d72286f935a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 21:40:55 +0900 Subject: [PATCH 130/138] =?UTF-8?q?[refactor]=20=EC=9D=BC=EB=B6=80=20hook?= =?UTF-8?q?=EB=93=A4=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0=ED=9B=84=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 주석을 제거 - sidebarTypeClickHandler를 사용하는 함수의 input type을 string대신 좀 더 명시적으로 지정해주었다 --- .../Sidebar/SidebarContentMore/FeatureTypeItem.tsx | 8 ++------ src/hooks/sidebar/useDetailType.ts | 3 ++- src/hooks/sidebar/useSidebarType.ts | 6 +++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx b/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx index f80d752..95f193b 100644 --- a/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx +++ b/src/components/Sidebar/SidebarContentMore/FeatureTypeItem.tsx @@ -1,7 +1,7 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; import { FeaturesType } from '../../../utils/rendering-data/featureTypeData'; -import { FeatureNameType, FeatureState } from '../../../store/common/type'; +import { FeatureNameType, ElementNameType } from '../../../store/common/type'; import useFeatureTypeItemHook from '../../../hooks/sidebar/useFeatureTypeItem'; interface ListProps { @@ -61,9 +61,7 @@ interface FeatureTypeItemProps { typeKey: FeatureNameType; typeName: string; subFeatures: FeaturesType[]; - // sidebarTypeName: string; - // sidebarSubTypeName: string; - sidebarTypeClickHandler: (name: string) => void; + sidebarTypeClickHandler: (name: FeatureNameType | ElementNameType) => void; sidebarSubTypeClickHandler: (name: string) => void; } @@ -71,8 +69,6 @@ function FeatureTypeItem({ typeKey, typeName, subFeatures, - // sidebarTypeName, - // sidebarSubTypeName, sidebarTypeClickHandler, sidebarSubTypeClickHandler, }: FeatureTypeItemProps): React.ReactElement { diff --git a/src/hooks/sidebar/useDetailType.ts b/src/hooks/sidebar/useDetailType.ts index 5cf67aa..76cb81d 100644 --- a/src/hooks/sidebar/useDetailType.ts +++ b/src/hooks/sidebar/useDetailType.ts @@ -1,6 +1,7 @@ import { useSelector } from 'react-redux'; import { RootState } from '../../store'; import { + FeatureNameType, ElementNameType, SubElementNameType, FeatureType, @@ -8,7 +9,7 @@ import { } from '../../store/common/type'; interface UseDetailTypeProps { - sidebarTypeClickHandler: (name: string) => void; + sidebarTypeClickHandler: (name: FeatureNameType | ElementNameType) => void; sidebarSubTypeClickHandler: (name: string) => void; } diff --git a/src/hooks/sidebar/useSidebarType.ts b/src/hooks/sidebar/useSidebarType.ts index 26f2b72..971f5d0 100644 --- a/src/hooks/sidebar/useSidebarType.ts +++ b/src/hooks/sidebar/useSidebarType.ts @@ -12,7 +12,7 @@ import { } from '../../store/common/type'; export interface SidebarHookType { - sidebarTypeClickHandler: (name: string) => void; + sidebarTypeClickHandler: (name: FeatureNameType | ElementNameType) => void; sidebarSubTypeClickHandler: (name: string) => void; feature: FeatureNameType | null; subFeature: string | null; @@ -27,8 +27,8 @@ function useSidebarType(): SidebarHookType { ) as PayloadPropsType; const { feature, subFeature, element, subElement } = sidebarStates; - const sidebarTypeClickHandler = (name: string) => { - if ([feature, element].includes(name as any)) return; + const sidebarTypeClickHandler = (name: FeatureNameType | ElementNameType) => { + if ([feature, element].includes(name)) return; if (Object.keys(ElementNameType).includes(name)) { dispatch( setSidebarProperties({ From 75878bcd19f2345c5b6124715f53ae94cfde790d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 21:41:50 +0900 Subject: [PATCH 131/138] =?UTF-8?q?[refactor]=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 불필요한 주석들을 제거 --- src/components/Sidebar/SidebarContentMore/DetailType.tsx | 2 -- src/components/Sidebar/SidebarContentMore/FeatureType.tsx | 2 -- src/components/Sidebar/SidebarContentMore/Styler.tsx | 1 - 3 files changed, 5 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 3734c10..b859bcf 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -11,7 +11,6 @@ import Styler from './Styler'; import { ElementNameType, SubElementNameType, - FeaturePropsType, } from '../../../store/common/type'; interface PaddingProp { @@ -65,7 +64,6 @@ const CheckRight = styled.div` function DetailType(): React.ReactElement { const { feature, - subFeature, element, subElement, sidebarTypeClickHandler, diff --git a/src/components/Sidebar/SidebarContentMore/FeatureType.tsx b/src/components/Sidebar/SidebarContentMore/FeatureType.tsx index 4bba352..7cc1b16 100644 --- a/src/components/Sidebar/SidebarContentMore/FeatureType.tsx +++ b/src/components/Sidebar/SidebarContentMore/FeatureType.tsx @@ -6,7 +6,6 @@ import DetailType from './DetailType'; import useSidebarType, { SidebarHookType, } from '../../../hooks/sidebar/useSidebarType'; -// import { FeatureNameType } from '../../../store/common/type'; import FeatureTypeItem from './FeatureTypeItem'; @@ -30,7 +29,6 @@ const FeatureTypeTitle = styled.h2` function FeatureType(): React.ReactElement { const { feature, - // subFeature, sidebarTypeClickHandler, sidebarSubTypeClickHandler, }: SidebarHookType = useSidebarType(); diff --git a/src/components/Sidebar/SidebarContentMore/Styler.tsx b/src/components/Sidebar/SidebarContentMore/Styler.tsx index c096380..92fb730 100644 --- a/src/components/Sidebar/SidebarContentMore/Styler.tsx +++ b/src/components/Sidebar/SidebarContentMore/Styler.tsx @@ -1,6 +1,5 @@ import React from 'react'; import styled from '../../../utils/styles/styled'; -// import { ElementPropsType } from '../../../store/common/type'; import useStyleType, { UseStyleHookType, } from '../../../hooks/sidebar/useStyleType'; From fbd15cf687bb35869680148204dd1b761e08d499 Mon Sep 17 00:00:00 2001 From: tejava Date: Thu, 3 Dec 2020 22:25:24 +0900 Subject: [PATCH 132/138] =?UTF-8?q?[feat]=20DetailType.tsx=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이전 상태는 isChanged 등 이벤트 발생 조건이 각각 함수 literal로 지정되어있었습니다. 이를 줄이고자 각각의 단계에 따라서 변화하는 공통의 DetailTypeSubList을 만들었습니다. 항목이 2단계 1단계 등 다양하게 나뉘어져있어 일반화하는 대신 객체 형태로 전달하는 걸 고려해봤습니다. 각각은 내부에 하위 항목을 값으로 가지고 있고, 이 내부 값이 빈 경우 최하위 항목으로 설정되도록 작동합니다. --- .../Sidebar/SidebarContentMore/DetailType.tsx | 168 ++++++------------ .../SidebarContentMore/DetailTypeSubList.tsx | 114 ++++++++++++ 2 files changed, 172 insertions(+), 110 deletions(-) create mode 100644 src/components/Sidebar/SidebarContentMore/DetailTypeSubList.tsx diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index 4f147b2..0dbd5b7 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -3,7 +3,7 @@ import styled from '../../../utils/styles/styled'; import useSidebarType, { SidebarHookType, } from '../../../hooks/sidebar/useSidebarType'; -import ListItem, { paddingStepType, paddingStep } from './DetailTypeItem'; +import ListItem from './DetailTypeItem'; import useDetailType, { UseDetailHookType, } from '../../../hooks/sidebar/useDetailType'; @@ -14,9 +14,7 @@ import { FeaturePropsType, } from '../../../store/common/type'; -interface PaddingProp { - padding: paddingStepType; -} +import DetailTypeSubList from './DetailTypeSubList'; const DetailWrapper = styled.div` width: 230px; @@ -39,14 +37,6 @@ const List = styled.ul` margin-bottom: 30px; `; -const Text = styled.h3` - margin: 10px 0; - padding-left: ${(props) => paddingStep[props.padding]}; - font-size: 1.7rem; - font-weight: 600; - color: ${(props) => props.theme.GREY}; -`; - const Check = styled.div` position: absolute; font-size: 1.2rem; @@ -54,14 +44,6 @@ const Check = styled.div` color: ${(props) => props.theme.GREEN}; `; -const CheckRight = styled.div` - position: absolute; - left: 10px; - font-size: 1.2rem; - font-weight: 600; - color: ${(props) => props.theme.GREEN}; -`; - function DetailType({ feature, subFeature, @@ -95,103 +77,69 @@ function DetailType({ 세부 유형 {section ? ( + + ) : null} + {labelText ? ( + + ) : null} + {labelIcon ? ( - 구역 - {section?.fill.isChanged ? : <>} - { - styleClickHandler( - ElementNameType.section, - SubElementNameType.fill - ); - }} - name="채우기" - /> - {section?.stroke.isChanged ? : <>} + {labelIcon.isChanged ? : <>} { - styleClickHandler( - ElementNameType.section, - SubElementNameType.stroke - ); + styleClickHandler(ElementNameType.labelIcon); }} - name="윤곽선" + name="아이콘" /> - ) : ( - <> - )} - - {labelText ? ( - <> - 라벨 - - 텍스트 - {labelText?.fill.isChanged ? : <>} - { - styleClickHandler( - ElementNameType.labelText, - SubElementNameType.fill - ); - }} - name="채우기" - /> - {labelText?.stroke.isChanged ? ( - - ) : ( - <> - )} - { - styleClickHandler( - ElementNameType.labelText, - SubElementNameType.stroke - ); - }} - name="윤곽선" - /> - - - ) : ( - <> - )} - {labelIcon ? ( - <> - {labelIcon.isChanged ? : <>} - { - styleClickHandler(ElementNameType.labelIcon); - }} - name="아이콘" - /> - - ) : ( - <> - )} - + ) : null} ` + margin: 10px 0; + padding-left: ${(props) => paddingStep[props.padding]}; + font-size: 1.7rem; + font-weight: 600; + color: ${(props) => props.theme.GREY}; +`; + +export const Check = styled.div` + position: absolute; + font-size: 1.2rem; + font-weight: 600; + color: ${(props) => props.theme.GREEN}; +`; + +interface ChildrenType { + isChanged?: boolean; + title: string; + elementName?: string; + subElementName?: string; + childrenProps: ChildrenType[]; +} + +interface PropsType { + title: string; + checkIsSelected: ( + elementName: ElementNameType, + subElementName?: SubElementNameType | undefined + ) => boolean; + styleClickHandler: ( + elementName: ElementNameType, + subElementName?: SubElementNameType | undefined + ) => void; + childrenProps: ChildrenType[]; +} + +function DetailTypeSubList({ + title, + checkIsSelected, + styleClickHandler, + childrenProps, +}: PropsType): React.ReactElement { + const childComponent = childrenProps.map((child: ChildrenType) => { + return child.childrenProps.length !== 0 ? ( +

+ ) : ( +
+ {child.isChanged ? : null} + { + styleClickHandler( + child.elementName as ElementNameType, + child.subElementName as SubElementNameType + ); + }} + name={child.title} + /> +
+ ); + }); + return ( + + {title} + {childComponent} + + ); +} + +export default DetailTypeSubList; From d63b7de82985c116ab53a6d0d3b9351427f7302d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 22:36:21 +0900 Subject: [PATCH 133/138] =?UTF-8?q?[feat]=20react-router=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=ED=9B=84=20=EB=A1=9C=EA=B3=A0=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - react-router추가 - 라우팅으로 로고 화면을 보여주도록 함 --- package-lock.json | 115 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + src/App.tsx | 9 +++- src/index.tsx | 15 +++--- src/pages/Entry.tsx | 5 +- 5 files changed, 138 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index e34ecd1..79101d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2491,6 +2491,12 @@ "@types/node": "*" } }, + "@types/history": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==", + "dev": true + }, "@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -2619,6 +2625,27 @@ "redux": "^4.0.0" } }, + "@types/react-router": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz", + "integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.6.tgz", + "integrity": "sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, "@types/redux": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@types/redux/-/redux-3.6.0.tgz", @@ -7469,6 +7496,19 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -10452,6 +10492,15 @@ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" }, + "mini-create-react-context": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", + "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", + "requires": { + "@babel/runtime": "^7.12.1", + "tiny-warning": "^1.0.3" + } + }, "mini-css-extract-plugin": { "version": "0.11.3", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz", @@ -12938,6 +12987,52 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==" }, + "react-router": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", + "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.4.0", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + } + } + } + }, + "react-router-dom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", + "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, "react-scripts": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.0.tgz", @@ -13550,6 +13645,11 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, + "resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, "resolve-protobuf-schema": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", @@ -15312,6 +15412,16 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, "tinyqueue": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", @@ -15780,6 +15890,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 7f40996..121fc91 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-redux": "^7.2.2", + "react-router-dom": "^5.2.0", "react-scripts": "4.0.0", "redux": "^4.0.5", "redux-devtools-extension": "^2.13.8", @@ -52,6 +53,7 @@ "devDependencies": { "@types/mapbox-gl": "^1.12.7", "@types/react-redux": "^7.1.11", + "@types/react-router-dom": "^5.1.6", "@types/redux": "^3.6.0", "@typescript-eslint/eslint-plugin": "^4.8.0", "@typescript-eslint/parser": "^4.8.0", diff --git a/src/App.tsx b/src/App.tsx index 7aeff5d..0d03ff9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,16 @@ import React from 'react'; +import { Route, Switch } from 'react-router-dom'; import Main from './pages/Main'; +import Entry from './pages/Entry'; /** 추후에 라우팅해야되면 수정할 예정 */ function App(): React.ReactElement { - return
; + return ( + + + + + ); } export default App; diff --git a/src/index.tsx b/src/index.tsx index 1293c73..b5c7f69 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { BrowserRouter } from 'react-router-dom'; import { ThemeProvider } from 'emotion-theming'; import App from './App'; @@ -11,12 +12,14 @@ import store from './store'; ReactDOM.render( - - - - - - + + + + + + + + , document.getElementById('root') ); diff --git a/src/pages/Entry.tsx b/src/pages/Entry.tsx index 412aea8..b13b996 100644 --- a/src/pages/Entry.tsx +++ b/src/pages/Entry.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Link } from 'react-router-dom'; import Canvas from '../components/Canvas'; import styled from '../utils/styles/styled'; @@ -44,7 +45,9 @@ function Entry(): React.ReactElement { - 시작하기 + + 시작하기 + ); From c86affd645bd82e6cc54a9d1ee1ba71d1222edfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Thu, 3 Dec 2020 22:36:54 +0900 Subject: [PATCH 134/138] =?UTF-8?q?[fix]=20=EB=A0=8C=EB=8D=94=EB=A7=81=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기능유형 feature > feature이동 시 세부유형이 렌더링이 되지않았음 - 기존 조건 검사가 막고있었어서 제거하는 것으로 해결 - enum으로 묶어서 key를 관리할 수 있도록 하고 넘길 떄 enum을 사용해서 넘김 --- src/hooks/sidebar/useSidebarType.ts | 10 +++++----- src/store/common/type.ts | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/hooks/sidebar/useSidebarType.ts b/src/hooks/sidebar/useSidebarType.ts index 971f5d0..d38c2f9 100644 --- a/src/hooks/sidebar/useSidebarType.ts +++ b/src/hooks/sidebar/useSidebarType.ts @@ -9,6 +9,7 @@ import { ElementNameType, SubElementNameType, PayloadPropsType, + SidebarProperties, } from '../../store/common/type'; export interface SidebarHookType { @@ -34,7 +35,7 @@ function useSidebarType(): SidebarHookType { setSidebarProperties({ ...sidebarStates, element: name as ElementNameType, - key: 'element', + key: SidebarProperties.element, }) ); } else { @@ -42,20 +43,19 @@ function useSidebarType(): SidebarHookType { initSidebarProperties({ ...sidebarStates, feature: name as FeatureNameType, - key: 'feature', + key: SidebarProperties.feature, }) ); } }; const sidebarSubTypeClickHandler = (name: string) => { - if ([subFeature, subElement].includes(name)) return; if (Object.keys(SubElementNameType).includes(name)) { dispatch( setSidebarProperties({ ...sidebarStates, subElement: name as SubElementNameType, - key: 'subElement', + key: SidebarProperties.subElement, }) ); } else { @@ -63,7 +63,7 @@ function useSidebarType(): SidebarHookType { setSidebarProperties({ ...sidebarStates, subFeature: name, - key: 'subFeature', + key: SidebarProperties.subFeature, }) ); } diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 094d7e0..065dda1 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -33,6 +33,13 @@ export enum FeatureNameType { marker = 'marker', } +export enum SidebarProperties { + feature = 'feature', + subFeature = 'subFeature', + element = 'element', + subElement = 'subElement', +} + export interface objType { // eslint-disable-next-line @typescript-eslint/no-explicit-any [name: string]: any; From e9755b892a1fefc33089e47f4361fb9575b9245e Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Thu, 3 Dec 2020 23:04:40 +0900 Subject: [PATCH 135/138] =?UTF-8?q?[chore]=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/common/type.ts | 4 ++-- src/store/style/manageCategories.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/store/common/type.ts b/src/store/common/type.ts index 5b5ebe0..59e9d83 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -91,7 +91,7 @@ export interface SubElementActionPayload { [SubElementNameType.stroke]?: StyleActionPayload; } -export interface ElemetnActionPayload { +export interface ElementActionPayload { [ElementNameType.section]?: SubElementActionPayload; [ElementNameType.labelText]?: SubElementActionPayload; [ElementNameType.labelIcon]?: StyleActionPayload; @@ -99,7 +99,7 @@ export interface ElemetnActionPayload { export type WholeStyleActionPayload = { [featureName in FeatureNameType]?: { - [subFeatureName: string]: ElemetnActionPayload; + [subFeatureName: string]: ElementActionPayload; }; }; diff --git a/src/store/style/manageCategories.ts b/src/store/style/manageCategories.ts index 6703daf..9b06f6c 100644 --- a/src/store/style/manageCategories.ts +++ b/src/store/style/manageCategories.ts @@ -2,20 +2,20 @@ import { ElementNameType, SubElementActionPayload, StyleActionPayload, - ElemetnActionPayload, + ElementActionPayload, } from '../common/type'; interface combineCatecoryProps { element: ElementNameType; - elementStyle: ElemetnActionPayload; - initialElementStyle: ElemetnActionPayload; + elementStyle: ElementActionPayload; + initialElementStyle: ElementActionPayload; } export function combineCategory({ element, elementStyle, initialElementStyle, -}: combineCatecoryProps): ElemetnActionPayload { +}: combineCatecoryProps): ElementActionPayload { const update = initialElementStyle; if (element === 'labelIcon' && elementStyle[element]) { (update[element] as StyleActionPayload) = { From fa85583b7290fee1b07313c5713c5af59629d98e Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Fri, 4 Dec 2020 01:34:56 +0900 Subject: [PATCH 136/138] =?UTF-8?q?[fix]=20=EA=B8=B0=EC=A1=B4=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 상태 객체의 subFeature와 element가 혼동되어서 함수를 잘못 구현했음 - 기존 combine 함수를 2개로 분리해서 element(section, labelText, labelIcon) 별로 처리하고 subElement가 있는 경우를 분리된 함수에서 처리하도록 하였음 --- src/store/style/getReducer.ts | 14 +++---- src/store/style/manageCategories.ts | 64 ++++++++++++++++++----------- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/store/style/getReducer.ts b/src/store/style/getReducer.ts index 9ea77d9..64a0e60 100644 --- a/src/store/style/getReducer.ts +++ b/src/store/style/getReducer.ts @@ -12,7 +12,7 @@ import { import { getDefaultFeature } from './properties'; import { INIT, SET, SET_WHOLE } from './action'; import { checkStyleIsChanged, checkFeatureIsChanged } from './compareStyle'; -import { combineCategory } from './manageCategories'; +import { combineElement } from './manageCategories'; interface ReducerType { (state: FeatureState, action: ActionType): FeatureState; @@ -79,15 +79,13 @@ export default function getReducer(IDX: number): ReducerType { if (!inputStyle) return updateStyle; - const categories = Object.keys(updateStyle); - categories.forEach((category) => { - combineCategory({ - element: category as ElementNameType, - elementStyle: inputStyle[category], - initialElementStyle: updateStyle[category], + const subFeatures = Object.keys(inputStyle); + subFeatures.forEach((subFeature) => { + updateStyle[subFeature] = combineElement({ + elementStyle: inputStyle[subFeature], + initialElementStyle: updateStyle[subFeature], }); }); - return updateStyle; } diff --git a/src/store/style/manageCategories.ts b/src/store/style/manageCategories.ts index 9b06f6c..b2d5709 100644 --- a/src/store/style/manageCategories.ts +++ b/src/store/style/manageCategories.ts @@ -1,41 +1,59 @@ import { - ElementNameType, SubElementActionPayload, - StyleActionPayload, ElementActionPayload, + FeatureType, + SubElementType, } from '../common/type'; -interface combineCatecoryProps { - element: ElementNameType; +interface combineElementProps { elementStyle: ElementActionPayload; - initialElementStyle: ElementActionPayload; + initialElementStyle: FeatureType; } -export function combineCategory({ - element, +export const combineElement = ({ elementStyle, initialElementStyle, -}: combineCatecoryProps): ElementActionPayload { +}: combineElementProps): FeatureType => { const update = initialElementStyle; - if (element === 'labelIcon' && elementStyle[element]) { - (update[element] as StyleActionPayload) = { - ...update[element], - ...elementStyle[element], + if (elementStyle.labelIcon && update.labelIcon) { + update.labelIcon = { + ...update.labelIcon, + ...elementStyle.labelIcon, }; - return update; } - const verifiedCategory = elementStyle[element] as SubElementActionPayload; - const verifiedUpdate = update[element] as SubElementActionPayload; - - if (verifiedCategory.fill) { - verifiedUpdate.fill = { ...verifiedUpdate.fill, ...verifiedCategory.fill }; + if (elementStyle.labelText && update.labelText) { + update.labelText = combineSubElement({ + subElementStyle: elementStyle.labelText, + initialSubElementStyle: update.labelText, + }); } - if (verifiedCategory.stroke) { - verifiedUpdate.stroke = { - ...verifiedUpdate.stroke, - ...verifiedCategory.stroke, - }; + + if (elementStyle.section && update.section) { + update.section = combineSubElement({ + subElementStyle: elementStyle.section, + initialSubElementStyle: update.section, + }); } + return update; +}; + +interface combineSubElementProps { + subElementStyle: SubElementActionPayload; + initialSubElementStyle: SubElementType; } + +const combineSubElement = ({ + subElementStyle, + initialSubElementStyle, +}: combineSubElementProps): SubElementType => { + const update = initialSubElementStyle; + if (subElementStyle.fill) { + update.fill = { ...update.fill, ...subElementStyle.fill }; + } + if (subElementStyle.stroke) { + update.stroke = { ...update.stroke, ...subElementStyle.stroke }; + } + return update as SubElementType; +}; From f2155e25a17e477d381c9ed9d680d08df8d46935 Mon Sep 17 00:00:00 2001 From: eunsol0410 Date: Fri, 4 Dec 2020 01:36:45 +0900 Subject: [PATCH 137/138] =?UTF-8?q?[feat]=20import=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=EC=B0=BD=EC=97=90=20useWholeStyle=20Hook=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - textarea 값이 json 파싱 문제가 있다고 생각했는데, 그게 아니라 json 형식 자체의 문제였음 - 따라서 전달된 json의 형식이 알맞게 왔는지 validation하는 함수가 필요해보임 --- src/components/Sidebar/SidebarModal/ImportModal.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/Sidebar/SidebarModal/ImportModal.tsx b/src/components/Sidebar/SidebarModal/ImportModal.tsx index 47403da..5ffd2a2 100644 --- a/src/components/Sidebar/SidebarModal/ImportModal.tsx +++ b/src/components/Sidebar/SidebarModal/ImportModal.tsx @@ -8,6 +8,8 @@ import useInputText, { InputTextHookType, } from '../../../hooks/common/useInputText'; import CloseIcon from '../../Icon/CloseIcon'; +import useWholeStyle from '../../../hooks/common/useWholeStyle'; +import { WholeStyleActionPayload } from '../../../store/common/type'; const Overlay = styled.div` position: fixed; @@ -116,6 +118,7 @@ function ImportModal({ } ); const { inputText, onInputChange }: InputTextHookType = useInputText(); + const { changeStyle } = useWholeStyle(); return ( <> @@ -133,6 +136,14 @@ function ImportModal({ onChange={onInputChange} /> 지도 가져오기 + ); From 39be470227b5877cdc993f7d85190160ea6c9f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9D=80=EC=8B=9D?= Date: Fri, 4 Dec 2020 10:38:11 +0900 Subject: [PATCH 138/138] =?UTF-8?q?[fix]=20github=EC=97=90=EC=84=9C=20pr?= =?UTF-8?q?=20merge=20=EC=9D=B4=ED=9B=84=20=EB=B0=9C=EC=83=9D=ED=95=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - github에서 pr merge하고 받아온 뒤 확인해보니 문제가 있어서 해결하였다 - --- src/components/Sidebar/SidebarContentMore/DetailType.tsx | 6 +----- src/store/common/type.ts | 3 --- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/components/Sidebar/SidebarContentMore/DetailType.tsx b/src/components/Sidebar/SidebarContentMore/DetailType.tsx index ed78125..a602989 100644 --- a/src/components/Sidebar/SidebarContentMore/DetailType.tsx +++ b/src/components/Sidebar/SidebarContentMore/DetailType.tsx @@ -43,14 +43,10 @@ const Check = styled.div` color: ${(props) => props.theme.GREEN}; `; -function DetailType({ - feature, - subFeature, -}: FeaturePropsType): React.ReactElement { +function DetailType(): React.ReactElement { const { feature, element, - subElement, sidebarTypeClickHandler, sidebarSubTypeClickHandler, }: SidebarHookType = useSidebarType(); diff --git a/src/store/common/type.ts b/src/store/common/type.ts index edaef75..f1593b5 100644 --- a/src/store/common/type.ts +++ b/src/store/common/type.ts @@ -77,7 +77,6 @@ export interface ElementPropsType extends FeaturePropsType { subElement?: SubElementNameType; } - export type SidebarActionType = | ReturnType | ReturnType; @@ -87,12 +86,10 @@ export type ActionType = | ReturnType | ReturnType; - export interface ActionPayload extends ElementPropsType { style: StyleType; } - export interface PayloadPropsType { key: 'feature' | 'subFeature' | 'element' | 'subElement'; feature: FeatureNameType | null;