diff --git a/src/components/LegendCategories/demos/default.tsx b/src/components/LegendCategories/demos/default.tsx
new file mode 100644
index 00000000..4a9d72a7
--- /dev/null
+++ b/src/components/LegendCategories/demos/default.tsx
@@ -0,0 +1,31 @@
+import { LegendCategories } from '@antv/larkmap';
+import React from 'react';
+
+export default () => {
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/LegendCategories/demos/map-default.tsx b/src/components/LegendCategories/demos/map-default.tsx
new file mode 100644
index 00000000..33e625e8
--- /dev/null
+++ b/src/components/LegendCategories/demos/map-default.tsx
@@ -0,0 +1,25 @@
+import { LegendCategories, LarkMap, CustomControl } from '@antv/larkmap';
+import React from 'react';
+
+const config = {
+ mapType: 'GaodeV1' as const,
+ mapOptions: {
+ style: 'light',
+ center: [120.210792, 30.246026] as [number, number],
+ zoom: 9,
+ // token: 'xxxx - token',
+ },
+};
+export default () => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/src/components/LegendCategories/index.less b/src/components/LegendCategories/index.less
new file mode 100644
index 00000000..cf0ac42b
--- /dev/null
+++ b/src/components/LegendCategories/index.less
@@ -0,0 +1,30 @@
+@cls-prefix: larkmap-legend-category;
+
+.@{cls-prefix} {
+ &__content {
+ display: flex;
+ align-items: center;
+ margin-bottom: 5px;
+ &__labels {
+ letter-spacing: 2;
+ text-transform: uppercase;
+ font-family: PingFangSC;
+ line-height: 2;
+ }
+
+ &__shape {
+ margin-right: 15px;
+ }
+
+ &__square {
+ width: 32px;
+ height: 15px
+ }
+
+ &__circle {
+ width: 14px;
+ height: 14px;
+ border-radius: 50%;
+ }
+ }
+}
diff --git a/src/components/LegendCategories/index.md b/src/components/LegendCategories/index.md
new file mode 100644
index 00000000..b51686fa
--- /dev/null
+++ b/src/components/LegendCategories/index.md
@@ -0,0 +1,28 @@
+---
+toc: content
+order: 5
+group:
+ title: 分析组件
+ order: 3
+nav:
+ title: 组件
+ path: /components
+---
+
+# 分类图例 - LegendCategories
+
+## 介绍
+
+分类图例
+
+## 代码演示
+
+### 默认演示
+
+
+
+### 在地图中展示
+
+
+
+
diff --git a/src/components/LegendCategories/index.tsx b/src/components/LegendCategories/index.tsx
new file mode 100644
index 00000000..336f36d6
--- /dev/null
+++ b/src/components/LegendCategories/index.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import classnames from 'classnames';
+import { getGradientColors } from './../../utils/color';
+import './index.less';
+import type { LegendCategoriesProps } from './types';
+
+export const CLS_PREFIX = 'larkmap-legend-category';
+
+export function LegendCategories(props: LegendCategoriesProps) {
+ const { labels, colors, geometryType = 'circle', isStrokeColor, style, className: cls_name } = props;
+
+ function getColor(item: string) {
+ return isStrokeColor ? { border: `2px solid ${item}` } : { background: item };
+ }
+
+ function Conent(color: string[]) {
+ return (
+
+ {labels.map((item, index) => (
+
+ ))}
+
+ );
+ }
+ function Renders() {
+ if (Array.isArray(colors)) {
+ return Conent(colors);
+ }
+ const colorGradient = getGradientColors(colors.startColor, colors.endColor, labels.length);
+ return Conent(colorGradient);
+ }
+ return ;
+}
+
+LegendCategories.defaultProps = {
+ geometryType: 'circle',
+ isStrokeColor: false,
+};
diff --git a/src/components/LegendCategories/types.ts b/src/components/LegendCategories/types.ts
new file mode 100644
index 00000000..8d7cb123
--- /dev/null
+++ b/src/components/LegendCategories/types.ts
@@ -0,0 +1,18 @@
+import type { CommonProps } from '../../types/common';
+
+export interface LegendCategoriesProps extends CommonProps {
+ /**
+ * 图形形状
+ * @default "circle"
+ */
+ geometryType?: 'circle' | 'square';
+ /** 图例项名称 */
+ labels: string[];
+ /** 图例项颜色 */
+ colors: string[] | { startColor: string; endColor: string };
+ /**
+ * 是否颜色填充
+ * @default false
+ */
+ isStrokeColor?: boolean;
+}
diff --git a/src/components/LegendIcon/demos/default.tsx b/src/components/LegendIcon/demos/default.tsx
new file mode 100644
index 00000000..3022ea01
--- /dev/null
+++ b/src/components/LegendIcon/demos/default.tsx
@@ -0,0 +1,17 @@
+import { LegendIcon } from '@antv/larkmap';
+import React from 'react';
+
+export default () => {
+ return (
+
+
+
+ );
+};
diff --git a/src/components/LegendIcon/demos/map-default.tsx b/src/components/LegendIcon/demos/map-default.tsx
new file mode 100644
index 00000000..e4cccb86
--- /dev/null
+++ b/src/components/LegendIcon/demos/map-default.tsx
@@ -0,0 +1,29 @@
+import { LegendIcon, LarkMap, CustomControl } from '@antv/larkmap';
+import React from 'react';
+
+const config = {
+ mapType: 'GaodeV1' as const,
+ mapOptions: {
+ style: 'light',
+ center: [120.210792, 30.246026] as [number, number],
+ zoom: 9,
+ // token: 'xxxx - token',
+ },
+};
+export default () => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/src/components/LegendIcon/index.less b/src/components/LegendIcon/index.less
new file mode 100644
index 00000000..804ddc6d
--- /dev/null
+++ b/src/components/LegendIcon/index.less
@@ -0,0 +1,15 @@
+@cls-prefix: larkmap-legend-icon;
+
+.@{cls-prefix} {
+ &__content {
+ display: flex;
+ align-items: center;
+ line-height: 2;
+
+ &__icon {
+ width: 16px;
+ height: 16px;
+ margin-right: 10px
+ }
+ }
+}
diff --git a/src/components/LegendIcon/index.md b/src/components/LegendIcon/index.md
new file mode 100644
index 00000000..4ab63493
--- /dev/null
+++ b/src/components/LegendIcon/index.md
@@ -0,0 +1,28 @@
+---
+toc: content
+order: 6
+group:
+ title: 分析组件
+ order: 3
+nav:
+ title: 组件
+ path: /components
+---
+
+# 图标图例 - LegendIcon
+
+## 介绍
+
+图标图例
+
+## 代码演示
+
+### 默认演示
+
+
+
+### 在地图中展示
+
+
+
+
diff --git a/src/components/LegendIcon/index.tsx b/src/components/LegendIcon/index.tsx
new file mode 100644
index 00000000..ea1f9b61
--- /dev/null
+++ b/src/components/LegendIcon/index.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import classnames from 'classnames';
+import type { LegendIconProps } from './types';
+import './index.less';
+
+export const CLS_PREFIX = 'larkmap-legend-icon';
+
+export const LegendIcon = (props: LegendIconProps) => {
+ const { labels, icons, className: cls, style } = props;
+ return (
+
+ {labels.map((item, index) => (
+
+
+
{item}
+
+ ))}
+
+ );
+};
diff --git a/src/components/LegendIcon/types.ts b/src/components/LegendIcon/types.ts
new file mode 100644
index 00000000..1764d07c
--- /dev/null
+++ b/src/components/LegendIcon/types.ts
@@ -0,0 +1,8 @@
+import type { CommonProps } from '../../types/common';
+
+export interface LegendIconProps extends CommonProps {
+ /** 图例项名称 */
+ labels: string[];
+ /** 图例项图标 */
+ icons: string[];
+}
diff --git a/src/components/LegendProportion/demos/default.tsx b/src/components/LegendProportion/demos/default.tsx
new file mode 100644
index 00000000..d02491f2
--- /dev/null
+++ b/src/components/LegendProportion/demos/default.tsx
@@ -0,0 +1,12 @@
+import { LegendProportion } from '@antv/larkmap';
+import React from 'react';
+import './index.less';
+
+export default () => {
+ return (
+
+
+
+
+ );
+};
diff --git a/src/components/LegendProportion/demos/index.less b/src/components/LegendProportion/demos/index.less
new file mode 100644
index 00000000..cca0e075
--- /dev/null
+++ b/src/components/LegendProportion/demos/index.less
@@ -0,0 +1,6 @@
+.demo_cls{
+ background-color: #fff;
+ border-radius: 4px;
+ padding: 5px;
+ width: 160px;
+}
diff --git a/src/components/LegendProportion/demos/map-default.tsx b/src/components/LegendProportion/demos/map-default.tsx
new file mode 100644
index 00000000..1c840a3b
--- /dev/null
+++ b/src/components/LegendProportion/demos/map-default.tsx
@@ -0,0 +1,22 @@
+import { LegendProportion, LarkMap, CustomControl } from '@antv/larkmap';
+import React from 'react';
+import './index.less';
+
+const config = {
+ mapType: 'GaodeV1' as const,
+ mapOptions: {
+ style: 'light',
+ center: [120.210792, 30.246026] as [number, number],
+ zoom: 9,
+ // token: 'xxxx - token',
+ },
+};
+export default () => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/src/components/LegendProportion/index.less b/src/components/LegendProportion/index.less
new file mode 100644
index 00000000..675c50a3
--- /dev/null
+++ b/src/components/LegendProportion/index.less
@@ -0,0 +1,24 @@
+@cls-prefix: larkmap-legend-proportion;
+
+.@{cls-prefix} {
+ display: flex;
+ align-items: flex-end;
+
+ &__circlebox {
+ position: relative;
+ display: flex;
+ justify-content: end;
+ align-items: end;
+ margin-right: 10px;
+
+ &__item {
+ border-radius: 50%;
+ position: absolute;
+ border: 1px solid #e1e3e4;
+ }
+ }
+
+ &__labelitem{
+ font-size: 14px;
+ }
+}
diff --git a/src/components/LegendProportion/index.md b/src/components/LegendProportion/index.md
new file mode 100644
index 00000000..917559eb
--- /dev/null
+++ b/src/components/LegendProportion/index.md
@@ -0,0 +1,28 @@
+---
+toc: content
+order: 7
+group:
+ title: 分析组件
+ order: 3
+nav:
+ title: 组件
+ path: /components
+---
+
+# 面积图例 - LegendProportion
+
+## 介绍
+
+面积图例
+
+## 代码演示
+
+### 默认演示
+
+
+
+### 在地图中展示
+
+
+
+
diff --git a/src/components/LegendProportion/index.tsx b/src/components/LegendProportion/index.tsx
new file mode 100644
index 00000000..16bc30ef
--- /dev/null
+++ b/src/components/LegendProportion/index.tsx
@@ -0,0 +1,88 @@
+import React from 'react';
+import { isNumber, isNaN, uniqueId } from 'lodash';
+import classnames from 'classnames';
+import type { LegendProportionProp } from './types';
+import './index.less';
+
+export const CLS_PREFIX = 'larkmap-legend-proportion';
+
+export const LegendProportion = (props: LegendProportionProp) => {
+ const { labels, fillColor = '#f9f9f9', className: cls, style } = props;
+ const [min, max] = labels;
+ const circleSizes = [86, 62, 38, 24];
+ /**
+ * 计算跨度
+ * @param min 最小值
+ * @param max 最大值
+ * @returns
+ */
+ function calculateSteps(newMin: number, newMax: number) {
+ const gap = (newMax - newMin) / 4;
+ const step1 = Math.floor(newMin + gap);
+ const step2 = Math.floor(newMax - gap);
+ return [step1, step2];
+ }
+
+ /**
+ * 生成label范围
+ * @returns
+ */
+ function generateRange() {
+ let newMin = min,
+ newMax = max;
+ if (!isNumber(newMin) || !isNumber(newMin)) {
+ newMin = newMax = 0;
+ }
+ if (isNaN(newMin)) {
+ newMin = 0;
+ }
+
+ if (isNaN(newMax)) {
+ newMax = 0;
+ }
+
+ const steps = calculateSteps(newMin, newMax);
+ const range = [{ prefix: 'MIN:', val: newMin }, ...steps, { prefix: 'MAX:', val: newMax }];
+ return range;
+ }
+
+ function CircleBox() {
+ return (
+
+ {circleSizes.map((item) => (
+
+ ))}
+
+ );
+ }
+
+ function LabelBox() {
+ return (
+
+ {generateRange()
+ .reverse()
+ .map((item: any) => (
+
+ {item?.prefix}
+ {typeof item === 'number' ? item : item.val}
+
+ ))}
+
+ );
+ }
+
+ return (
+
+
+
+
+ );
+};
+
+LegendProportion.defaultProps = {
+ fillColor: '#f9f9f9',
+};
diff --git a/src/components/LegendProportion/types.ts b/src/components/LegendProportion/types.ts
new file mode 100644
index 00000000..e08d25ac
--- /dev/null
+++ b/src/components/LegendProportion/types.ts
@@ -0,0 +1,11 @@
+import type { CommonProps } from '../../types/common';
+
+export interface LegendProportionProp extends CommonProps {
+ /**图例项名称 */
+ labels: number[];
+ /**
+ * 填充颜色
+ * @default "#f9f9f9"
+ */
+ fillColor?: string;
+}
diff --git a/src/components/LegendRamp/demos/default.tsx b/src/components/LegendRamp/demos/default.tsx
new file mode 100644
index 00000000..39e93ebb
--- /dev/null
+++ b/src/components/LegendRamp/demos/default.tsx
@@ -0,0 +1,34 @@
+import { LegendRamp } from '@antv/larkmap';
+import React from 'react';
+
+export default () => {
+ return (
+
+
+
+
+ );
+};
diff --git a/src/components/LegendRamp/demos/map-default.tsx b/src/components/LegendRamp/demos/map-default.tsx
new file mode 100644
index 00000000..1153f36d
--- /dev/null
+++ b/src/components/LegendRamp/demos/map-default.tsx
@@ -0,0 +1,33 @@
+import { LegendRamp, LarkMap, CustomControl } from '@antv/larkmap';
+import React from 'react';
+
+const config = {
+ mapType: 'GaodeV1' as const,
+ mapOptions: {
+ style: 'light',
+ center: [120.210792, 30.246026] as [number, number],
+ zoom: 9,
+ // token: 'xxxx - token',
+ },
+};
+export default () => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/src/components/LegendRamp/index.less b/src/components/LegendRamp/index.less
new file mode 100644
index 00000000..b1d862df
--- /dev/null
+++ b/src/components/LegendRamp/index.less
@@ -0,0 +1,32 @@
+@cls-prefix: larkmap-legend-ramp;
+
+.@{cls-prefix} {
+ &__labelbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: 8px;
+ }
+
+ &__continuousBar {
+ height: 10px;
+ border-radius: 6px
+ }
+
+
+ &__equidistantBar {
+ display: flex;
+
+ &__bar {
+ height: 10px;
+ }
+
+ &__bar:first-child {
+ border-radius: 6px 0 0 6px;
+ }
+
+ &__bar:last-child {
+ border-radius: 0 6px 6px 0;
+ }
+ }
+}
diff --git a/src/components/LegendRamp/index.md b/src/components/LegendRamp/index.md
new file mode 100644
index 00000000..6d313434
--- /dev/null
+++ b/src/components/LegendRamp/index.md
@@ -0,0 +1,28 @@
+---
+toc: content
+order: 8
+group:
+ title: 分析组件
+ order: 3
+nav:
+ title: 组件
+ path: /components
+---
+
+# 色带图例 - LegendRamp
+
+## 介绍
+
+色带图例
+
+## 代码演示
+
+### 默认演示
+
+
+
+### 在地图中展示
+
+
+
+
diff --git a/src/components/LegendRamp/index.tsx b/src/components/LegendRamp/index.tsx
new file mode 100644
index 00000000..31cb8c0f
--- /dev/null
+++ b/src/components/LegendRamp/index.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import classnames from 'classnames';
+import type { LegendRampProps } from './types';
+import './index.less';
+import { getGradientColors } from './../../utils/color';
+
+export const CLS_PREFIX = 'larkmap-legend-ramp';
+
+export function LegendRamp(props: LegendRampProps) {
+ const { isContinuous, labels, colors, lableUnit, className: cls, style, barWidth = 200 } = props;
+
+ function Continuous({ gradient }: Record) {
+ return (
+
+ );
+ }
+
+ function Equidistant({ color }: Record) {
+ return (
+
+ {color.map((item: string) => (
+
+ ))}
+
+ );
+ }
+
+ function Conent(color: string[]) {
+ const gradient = color.join(',');
+ const [min, max] = [labels[0], labels[labels.length - 1]];
+ return (
+
+ {isContinuous ?
:
}
+
+
{`${min}${lableUnit ?? ''} ${isContinuous ? '' : '<'}`}
+
{`${isContinuous ? '' : '≥'} ${max}${lableUnit ?? ''}`}
+
+
+ );
+ }
+
+ function Renders() {
+ if (Array.isArray(colors)) {
+ return Conent(colors);
+ }
+ const colorGradient = getGradientColors(colors.startColor, colors.endColor, labels.length);
+ return Conent(colorGradient);
+ }
+
+ return ;
+}
+
+LegendRamp.defaultProps = {
+ isContinuous: false,
+ barWidth: 200,
+};
diff --git a/src/components/LegendRamp/types.ts b/src/components/LegendRamp/types.ts
new file mode 100644
index 00000000..355cec74
--- /dev/null
+++ b/src/components/LegendRamp/types.ts
@@ -0,0 +1,20 @@
+import type { CommonProps } from '../../types/common';
+
+export interface LegendRampProps extends CommonProps {
+ /** 图例项名称 */
+ labels: string[] | number[];
+ /** 图例项单位 */
+ lableUnit: string;
+ /**图例项颜色 */
+ colors: { startColor: string; endColor: string } | string[];
+ /**
+ * 是否连续
+ * @default false
+ */
+ isContinuous?: boolean;
+ /**
+ * 图例项宽度
+ * @default 200
+ */
+ barWidth?: number;
+}
diff --git a/src/index.ts b/src/index.ts
index 27ac4bd8..1bcaf892 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -38,6 +38,17 @@ export { Marker } from './components/Marker';
export { MarkerProps } from './components/Marker/types';
export { Popup } from './components/Popup';
export { PopupProps } from './components/Popup/types';
+/**
+ * 图例组件
+ */
+export { LegendIcon } from './components/LegendIcon';
+export { LegendIconProps } from './components/LegendIcon/types';
+export { LegendProportion } from './components/LegendProportion';
+export { LegendProportionProp } from './components/LegendProportion/types';
+export { LegendCategories } from './components/LegendCategories';
+export { LegendCategoriesProps } from './components/LegendCategories/types';
+export { LegendRamp } from './components/LegendRamp';
+export { LegendRampProps } from './components/LegendRamp/types';
/**
* 绘制组件
diff --git a/src/utils/color.ts b/src/utils/color.ts
new file mode 100644
index 00000000..0e35bb90
--- /dev/null
+++ b/src/utils/color.ts
@@ -0,0 +1,33 @@
+import Color from 'color';
+import { forEach, round } from 'lodash-es';
+
+export const getGradientColors = (colorStart: string, colorEnd: string, count = 5): string[] => {
+ const singleColors: string[] = [colorStart]; // 初始颜色
+
+ const colorMinInt = Color(colorStart).object();
+ const colorMaxInt = Color(colorEnd).object();
+
+ if (count < 2) {
+ return count < 1 ? [] : singleColors;
+ }
+
+ const diffNumber = {
+ r: (colorMinInt.r - colorMaxInt.r) / (count - 1),
+ g: (colorMinInt.g - colorMaxInt.g) / (count - 1),
+ b: (colorMinInt.b - colorMaxInt.b) / (count - 1),
+ };
+
+ forEach(new Array(count - 2), (item, index) =>
+ singleColors.push(
+ Color({
+ r: round(colorMinInt.r - diffNumber.r * (index + 1), 0),
+ g: round(colorMinInt.g - diffNumber.g * (index + 1), 0),
+ b: round(colorMinInt.b - diffNumber.b * (index + 1), 0),
+ }).hex(),
+ ),
+ );
+
+ singleColors.push(colorEnd);
+
+ return singleColors;
+};