Skip to content

Commit

Permalink
feat: 新增文本图层控件 (#39)
Browse files Browse the repository at this point in the history
* fix: 修复 tabs icon 间距问题

* feat: 新增文本图层配置控件

* fix: 调整国际化文案,各类文档补充 (#38)

Co-authored-by: syb01094648 <[email protected]>

* feat: 新增文本图层配置控件

* fix: 调整国际化文案,各类文档补充 (#38)

Co-authored-by: syb01094648 <[email protected]>

---------

Co-authored-by: suyubin <[email protected]>
Co-authored-by: syb01094648 <[email protected]>
  • Loading branch information
3 people authored Dec 15, 2023
1 parent e6d7e92 commit 2ce3be7
Show file tree
Hide file tree
Showing 22 changed files with 243 additions and 79 deletions.
4 changes: 3 additions & 1 deletion docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ title: API
| mapControl | 控件显隐 | [MapControlProps](#mapcontrolprops) | `-` |
| toolbar | 头部组件显隐 | [ToolbarProps](#toolbarprops) | `-` |
| tabItems | 侧面版标签页选项卡内容 | [TabItemType](https://ant-design.antgroup.com/components/tabs-cn#tabitemtype) | `-` |
| showIndex | 是否展示元素序号 | `boolean` | `false` |
| showTextLayer | 是否展示元素文本 | `boolean` | `false` |
| textLayerFields | 展示元素文本的字段,不选则展示元素序号 | `string[] &#124; undefined` | `undefined` |
| wasmPath | sam 组件的 wasm 路径 | `string` | `\` |

#### `tabItems`
Expand Down Expand Up @@ -152,6 +153,7 @@ LngLat 文本编辑器,可以通过输入 LngLat 数据实现数据展示(目
| administrativeSelectControl | 行政区域选择控件 |
| mapAdministrativeControl | 查看当前行政区域控件 |
| logoControl | Logo 控件 |
| textLayerControl | 文本图层 控件 |

#### toolbar

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@antv/l7-editor",
"version": "1.1.8",
"version": "1.1.9",
"description": "Geographic data editing tool based on L7",
"files": [
"lib",
Expand Down
25 changes: 2 additions & 23 deletions src/components/app-header/btn/setting-btn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,8 @@ import { useGlobal } from '../../../recoil';

export const SettingBtn = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const {
popupTrigger,
setPopupTrigger,
autoFitBounds,
setAutoFitBounds,
showIndex,
setShowIndex
} = useGlobal();
const { popupTrigger, setPopupTrigger, autoFitBounds, setAutoFitBounds } =
useGlobal();
const { t } = useTranslation();
const [form] = Form.useForm();

Expand Down Expand Up @@ -55,14 +49,12 @@ export const SettingBtn = () => {
initialValues={{
popupTrigger,
autoFitBounds,
showIndex,
}}
style={{ textAlign: 'right' }}
onFinish={(e) => {
setIsModalOpen(false);
setPopupTrigger(e.popupTrigger);
setAutoFitBounds(e.autoFitBounds);
setShowIndex(e.showIndex)
}}
>
<Form.Item
Expand All @@ -86,19 +78,6 @@ export const SettingBtn = () => {
>
<Switch />
</Form.Item>
<Form.Item
name="showIndex"
label={t('btn.setting_btn.shiFouZhanShiYuan')}
>
<Radio.Group>
<Radio.Button value={true}>
{t('btn.setting_btn.kaiQi')}
</Radio.Button>
<Radio.Button value={false}>
{t('btn.setting_btn.guanBi')}
</Radio.Button>
</Radio.Group>
</Form.Item>
</Form>
</Modal>
</>
Expand Down
5 changes: 5 additions & 0 deletions src/components/app-header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ export const AppHeader: React.FC<AppHeaderProps> = ({ toolbar }) => {
description: t('app_header.constants.keXuanZeBuTong'),
target: () => document.getElementById('l7-editor-aMap')!,
},
{
title: t('text-layer-control_wenBenTuCengPeiZhi'),
description: t('text-layer-control_description'),
target: () => document.getElementById('text-layer-control')!,
},
{
title: t('app_header.constants.gEOJS'),
description: t('app_header.constants.keYiTongGuoBian'),
Expand Down
4 changes: 3 additions & 1 deletion src/components/layer-list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import { FeatureKey, LayerId, LayerZIndex } from '../../constants';
import { useFilterFeatures } from '../../hooks';
import { useFeature, useGlobal } from '../../recoil';
import { getPointImage } from '../../utils/change-image-color';
import { EditorTextLayer } from '../text-layer';

export const LayerList: React.FC = () => {
const scene = useScene();
const [isMounted, setIsMounted] = useState(false);
const { layerColor, coordConvert, baseMap } = useGlobal();
const { layerColor, coordConvert, baseMap, showTextLayer } = useGlobal();
const { transformCoord } = useFeature();
const { features: newFeatures } = useFilterFeatures();
const [features, setFeatures] = useState<Feature[]>([]);
Expand Down Expand Up @@ -111,6 +112,7 @@ export const LayerList: React.FC = () => {
state={{ active: { color: activeColor } }}
zIndex={LayerZIndex}
/>
{showTextLayer && <EditorTextLayer />}
</>
) : null;
};
5 changes: 4 additions & 1 deletion src/components/map-control-group/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import MapThemeControl from './map-theme-control';
import { OfficialLayerControl } from './official-layer-control';
import { SamControl } from './sam-control';
import useStyles from './styles';
import { TextLayerControl } from './text-layer-control';

type MapControlGroupProps = {
mapControl?: MapControlProps;
Expand All @@ -43,6 +44,7 @@ const DefaultMapControl: MapControlProps = {
administrativeSelectControl: true,
mapAdministrativeControl: true,
logoControl: true,
textLayerControl: true,
};
export const MapControlGroup: React.FC<MapControlGroupProps> = ({
mapControl,
Expand Down Expand Up @@ -70,7 +72,7 @@ export const MapControlGroup: React.FC<MapControlGroupProps> = ({
{isControlGroupState.drawControl && <DrawControl />}
{isControlGroupState.clearControl && <ClearControl />}
{isControlGroupState.zoomControl && (
<ZoomControl className={styles.zoom} showZoom />
<ZoomControl className={styles.zoom} showZoom position="rightbottom" />
)}
{isControlGroupState.mapAdministrativeControl && (
<MapAdministrativeControl />
Expand All @@ -96,6 +98,7 @@ export const MapControlGroup: React.FC<MapControlGroupProps> = ({
/>
)}
{layerType.includes(OfficeLayerEnum.GoogleSatellite) && <SamControl />}
{isControlGroupState.textLayerControl && <TextLayerControl />}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export function OfficialLayerControl() {
return (
<>
<CustomControl position="bottomleft">
<div className={styles.mapTab}>
<div className={styles.mapTab} id="l7-editor-aMap">
<div
className={styles.hideOfficeLayerBtn}
onClick={() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const useStyle = () => {
`,
hideOfficeLayerBtn: css`
height: 127px;
width: 20px;
width: 28px;
display: flex;
align-items: center;
justify-content: center;
Expand Down
80 changes: 80 additions & 0 deletions src/components/map-control-group/text-layer-control/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { CustomControl } from '@antv/larkmap';
import { Form, Popover, Select, Switch, Tooltip } from 'antd';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useFeature, useGlobal } from '../../../recoil';
import { IconFont } from '../../iconfont';
import useStyles from '../styles';
import useStyle from './style';

export type TextLayerControlProps = {};

export const TextLayerControl: React.FC = () => {
const styles = useStyles();
const style = useStyle();
const {
showTextLayer,
setShowTextLayer,
textLayerFields,
setTextLayerFields,
} = useGlobal();
const { features } = useFeature();
const [fields, setFields] = useState<string[]>([]);
const { t } = useTranslation();

const refreshFields = () => {
const newFieldSet = new Set<string>();
features.forEach((feature) => {
const properties = feature.properties;
if (properties) {
Object.keys(properties).forEach((key) => {
newFieldSet.add(key);
});
}
});
setFields(Array.from(newFieldSet));
};

return (
<CustomControl position="bottomleft">
<Popover
title={t('text-layer-control_wenBenBiaoZhu')}
overlayStyle={{ width: 300 }}
content={
<Form size="small" className={style.textLayerForm}>
<Form.Item label={t('text-layer-control_shiFouZhanShiTuCeng')}>
<Switch value={showTextLayer} onChange={setShowTextLayer} />
</Form.Item>

<Form.Item label={t('text-layer-control_zhanShiZiDuan')}>
<Select
value={textLayerFields}
onChange={setTextLayerFields}
placeholder={t('text-layer-control_buXuan')}
mode="multiple"
options={fields.map((item) => {
return { label: item, value: item };
})}
/>
</Form.Item>
</Form>
}
trigger="click"
onOpenChange={(visible) => {
if (visible) {
refreshFields();
}
}}
>
<Tooltip
placement="left"
overlay={t('text-layer-control_wenBenTuCengPeiZhi')}
>
<button className={styles.L7EditorControl} id="text-layer-control">
<IconFont type="icon-wenbenkuang" />
</button>
</Tooltip>
</Popover>
</CustomControl>
);
};
12 changes: 12 additions & 0 deletions src/components/map-control-group/text-layer-control/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { css } from '@emotion/css';

const useStyle = () => {
return {
textLayerForm: css`
.ant-form-item {
margin-bottom: 12px !important;
}
`,
};
};
export default useStyle;
86 changes: 59 additions & 27 deletions src/components/text-layer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,92 @@
import type { TextLayerProps } from '@antv/larkmap';
import { TextLayer } from '@antv/larkmap';
import type { Feature, LineString } from '@turf/turf';
import { center } from '@turf/turf';
import { cloneDeep } from 'lodash-es';
import React, { useMemo } from 'react';
import { FeatureKey } from '../../constants';
import { useFilterFeatures } from '../../hooks';
import { useFeature, useGlobal } from '../../recoil';
import { centerOfLine } from '../../utils';

export const EditorTextLayer = () => {
const { transformCoord } = useFeature();
const { features: newFeatures } = useFilterFeatures();
const { layerColor } = useGlobal();
const { layerColor, textLayerFields } = useGlobal();

const layerOptions: Omit<TextLayerProps, 'source'> = useMemo(() => {
const textOptions: Omit<TextLayerProps, 'source'> = useMemo(() => {
return {
zIndex: 101,
field: 'name',
field: 'text',
style: {
fill: `${layerColor}`,
opacity: 1,
fontSize: 18,
fontSize: 16,
stroke: '#fff',
strokeWidth: 2,
textAllowOverlap: true,
padding: [10, 10] as [number, number],
textOffset: [0, -18],
},
};
}, [layerColor]);

const pointTextOptions: Omit<TextLayerProps, 'source'> = useMemo(() => {
const newLayerOptions = cloneDeep(textOptions);
newLayerOptions.style!.textOffset = [0, -20];
return newLayerOptions;
}, [textOptions]);

const sourceData = useMemo(() => {
const transformData = transformCoord(newFeatures).map((item) => {
return {
data: center(item),
//@ts-ignore
featureIndex: item.properties?.[FeatureKey.Index],
};
});
const data = transformData.map((item) => {
const data = transformCoord(newFeatures).map((item) => {
// @ts-ignore
const featureIndex = item.properties?.[FeatureKey.Index];
const [x, y] = (() => {
if (item.geometry.type === 'LineString') {
return centerOfLine(item as Feature<LineString>).geometry.coordinates;
}
if (item.geometry.type === 'Point') {
return item.geometry.coordinates;
} else {
return center(item).geometry.coordinates;
}
})();

let text = `${featureIndex + 1}`;

if (textLayerFields?.length) {
text = textLayerFields
.map((field) => {
return String(item.properties?.[field] ?? '');
})
.join(' ');
}

return {
//@ts-ignore
x: item.data.geometry.coordinates[0],
//@ts-ignore
y: item.data.geometry.coordinates[1],
name: `${item.featureIndex + 1}`,
x,
y,
text,
type: item.geometry.type,
};
});
return data;
}, [transformCoord, newFeatures]);
}, [transformCoord, newFeatures, textLayerFields]);

return (
<TextLayer
{...layerOptions}
source={{
data: sourceData,
parser: { type: 'json', x: 'x', y: 'y' },
}}
/>
<>
<TextLayer
{...textOptions}
source={{
data: sourceData.filter((item) => item.type !== 'Point'),
parser: { type: 'json', x: 'x', y: 'y' },
}}
/>
<TextLayer
{...pointTextOptions}
source={{
data: sourceData.filter((item) => item.type === 'Point'),
parser: { type: 'json', x: 'x', y: 'y' },
}}
/>
</>
);
};
2 changes: 1 addition & 1 deletion src/constants/iconfont.js

Large diffs are not rendered by default.

Loading

0 comments on commit 2ce3be7

Please sign in to comment.