Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 新增文本图层控件 #39

Merged
merged 6 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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[] | 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
Loading