简介:这篇文章属于技术选型的参考性文章,主要以一个一个的案例分析,来带各位快速的了解到LarkMap能帮助我们完成哪些需求。希望阅读完之后您对LarkMap有一个大致的印象,若后续有地图需求的话,可以考虑使用LarkMap。
本文是在语雀中写的,编辑器不一样,可能在语雀阅读体验更好,戳这直达。
引入
提问
某一天在我们的群里看到有一位同学提出了一个地图相关的问题,简单来说就是要拿到全国各省的围栏信息(coordinates)。其他同学也是积极的给出了他们的答案,有说json存本地的;有说用前端平台配置的;也有说必须要后端同学提供来保证数据及时更新,也利于维护。
确实大家说的都没错,但是以我的视角,以上的答案都无法解决这位同学的问题(声明:只是这个场景下这个同学的问题无法解决,其他case下可以根据实际业务诉求来定):
- JSON存本地:不利于维护
- 前端配置:维护起来方便了,改配置就好,但是也需要及时的有人去维护数据,很麻烦
- 后端同学提供:大部分情况下,后端同学并不会提供地理围栏数据(这位同学也一样,包括我做的业务也是一样,后端同学根本就无法提供这些数据),他们只会提供各省市的对应要展示的数据
所以从这一个小事情我感觉大家可能有一段时间没有接触过地图相关业务了,其实现在在L7已经有维护了一份地理围栏数据,借着这个契机也向大家介绍一下LarkMap,一个非常好用的地图组件。
目的
写本文一个目的是简单介绍一下LarkMap是什么让大家了解一下这个工具。最重要的目的是我个人在写地图需求的时候,需要通过发散性的思维,借助已有的工具和内容去完成一些特殊的功能点,希望借助这篇文章分享一下我是怎么思考和解决一些问题的。
啥是LarkMap
LarkMap 是AntV系列的地图容器组件,新一代 React 地图可视分析组件库,提供丰富/高效/专业/易用的可视化组件,一站式满足地理可视化需求。
A React toolkit for geospatial visualization based on L7.(基于 L7 的地理空间可视化 React 工具包。)
非常好的一篇介绍文档:戳这里
LarkMap上下游生态(图来源云极的分享,链接于文章末尾)
LarkMap首页介绍
根据个人的需求,选择你需要的图层/控件,理论上可以完成所有的需求:
地图绘制示例(Mock数据)
提供了什么能力
如果你问我AntV系列的看文档能学会咋用吗?我不敢保证。
但是LarkMap,它真的可以!戳这里直达文档
常用控件一览(图来源云极的分享,链接于文章末尾)
LarkMap将L7的核心概念拆分得非常的合理,分为几种:
- 容器组件:LarkMap,useScene和useLayer
- 场景对象 scene 是包含地图、地图控件、组件、加载资源的全局对象,通过 scene 场景对象,我们可以获取到操作地图需要的所有内容
- layers是获取图层实例的Hooks
- 基础图层:点、线、面、热力图层等等,包含了我们对于地图基础诉求的几乎所有基础组件
- 复合图层:气泡图层、区域图层(也就是中国地图)等等,结合了基础图层的一些复合图层
- 中国地图(区域图层)= 面图层+线图层+文本图层+..... 内部合成了一起,更方便我们使用
- 控件组件:比例尺、全屏、定位、地图主题等等,包含了地图所需的一些常用控件,也支持自定义控件(这个非常重要)
- 图例组件:分类、图标、面积图例等等,地图上数据展示是少不了要用到图例的
- 分析组件:标注、popup、右键菜单等等,也非常的常用哦
- 绘制组件:用于在地图上绘制,本质是用L7Draw实现的,LarkMap这边封装了一层hooks。对于基础的绘制诉求,够满足了,但是特别复杂的,可能实现不了。切记在使用之前,需要进行深入的技术评估。
读到这里,你可能还不会使用,请前往官方文档查看demo使用即可(阅读难度跟看antd的demo一样,API都在下面写的很清楚)。这里你只需要知道有这几种分类,按需选择使用。也可以查看下面的一些案例分析来学会怎么使用。
案例分析
很多人可能觉得,二次封装的组件,肯定会很多做不了吧,肯定有很多限制吧
请你花一点时间,看一下我下面提到的一些案例,是否能满足您的需求
声明:以下案例,数据全随机生成或从高德API中免费获取,或者已经打码
地图+自定义控件+地图上卷下钻+图例
需求描述
需求是绘制一个中国地图,显示全国各省市区的交易信息,需要有图例来告诉用户什么颜色对应什么数值范围区间,我还要知道该地区占比是多少。
在此基础上,为了照顾不太会用地图的人和不同用户的习惯,我需要用到地图控件来帮助用户更好的使用地图(放大缩小,全屏,比例尺,主题切换)。
我还需要有一个按钮(在右上角),点开之后弹出一个列表,用于展示该地区的一些详细信息(交易金额、支付顾客数、支付笔数、客单价)。
我还需要左上角有一个内容,用于切换当前查看数据的场景(包含交易场景,用户场景,门店场景)。
最重要的是,我要地图能够下钻和上卷,即双击某一个区域可以下钻到这一区域的下一层级,双击空白区域可以上卷回去,单击某一区域,我前面提到的列表要切换展示那个区域的列表。
需求示例图
技术实现
地图
无论需要实现什么地图细节,第一步永远是地图本身,地图本身渲染非常的简单,只需要一行代码+配置即可
import type { LarkMapProps } from '@antv/larkmap';
import { LarkMap } from '@antv/larkmap';
import React from 'react';
const config: LarkMapProps = {
mapType: 'Gaode',
mapOptions: {
style: 'light',
// token: '你申请的 Key',
},
};
export default () => (
<LarkMap {...config} style={{ height: '300px' }}></LarkMap>
);
Q&A
1.为什么官网的demo打开地图背景是白的?
因为地图展示必须要申请一个token,在LarkMap官网用codesandbox打开看不到底图是因为没有token,你需要自己申请token填入才可以。如果您是地图开发小白,请查阅这篇文档,做好准备。
2.为什么我加了token,地图还是展示不出来,甚至还有报错?
第一个问题中的那篇文档请仔细查阅,这边建议您在script中补充这样一段代码逻辑,再尝试一下,如果还是不行,请提供复现代码到issue中
<script type="text/javascript">
window._AMapSecurityConfig = {
securityJsCode: "您申请的安全密钥"
// 或者:serviceHost:'您的代理服务器域名或地址/_AMapService',
}
</script>
中国地图
ChoroplethLayer 是一个复合图层,用于面数据展示,支持描边、文本标注、多选等功能。符合中国地图的需求
注意:ChoroplethLayer可以实现中国地图,不代表它只能做中国地图,其他的都可以,因为它本质上只是多种图层的组合体。如果了解这个就可以利用它做很多别的需求
我们使用 ChoroplethLayer 来实现中国地图,基础使用非常简单,按照官网的来就好。
import type { LarkMapProps } from '@antv/larkmap';
import { LarkMap } from '@antv/larkmap';
import React from 'react';
/** 地图配置 */
const mapLayerOptions: Omit<ChoroplethLayerProps, 'source'> = {
autoFit: true,
strokeColor: 'blue',
lineWidth: 0.5,
label: {
field: 'name',
visible: true,
style: { fill: 'black', fontSize: 12, stroke: '#fff', strokeWidth: 1.5 },
},
fillColor: {
field: 'shopCount',
value: ['#B8E1FF', '#9AC5FF', '#7DAAFF', '#5B8FF9', '#3D76DD'],
scale: { type: 'quantile' },
},
};
/** 这里数据以北京为例,经过抽稀 */
const mapData = {
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
117.396754,
40.229364
],
[
117.377539,
40.218559
],
// 省略其他点位
]
]
},
"properties": {
"name": "北京市",
"province_adcode": 110000,
// 省略其他配置信息
}
}
],
"type": "FeatureCollection"
}
export default () => (
<LarkMap {...config} style={{ height: '300px' }}>
<ChoroplethLayer
{...mapLayerOptions}
source={{
data: mapData,
parser: { type: 'geojson' },
transforms: [],
}}
/>
</LarkMap>
);
Q&A
1. 边界信息数据从哪里拿?
推荐使用L7官网的地图工具——行政区划数据
2. 感觉渲染起来很卡,数据都还没填充进去,加载要很久?
这是因为全国的地理信息数据非常庞大,拿到上面的行政区域规划数据打印一下,你会发现每个省的点位非常的多,所以渲染起来非常的慢。解决方案是——数据抽稀(turf抽稀使用的是 Ramer–Douglas–Peucker 算法)
个人经验:1️⃣ 省级数据需要抽稀,但是单独的市/区通常不需要;2️⃣ 抽稀需要考虑沿海地区的城市,抽稀后会丢失精度(甚至直接丢失) ,需要根据你们的需求和精细程度来抽稀哦
抽稀前
抽稀后
3. 为什么渲染不出来,明明都按照例子一个一个配置好了啊?
90%以上概率是您的数据格式source不对,还有可能是您的source与parser没有对应上,当然也有可能是应该是bug,欢迎提issue。
source的数据格式应该是什么呢?—— Source(务必仔细对比,点和面的coordinates里面传递的数组嵌套层级重点关注一下)
4.为什么我用的RDBSource请求接口时好时坏,到后面直接不行了?
RDBSource请求接口就是我前面Q&A 第一个问题用到的行政区划数据
如果您用的是0.0.12以前的版本,可能会出现这个问题。0.0.13版本会解决这个问题,但是会非常的卡顿。0.0.15这个问题会得到解决。先检查一下您当前使用的版本,建议使用0.0.14之后的版本。
5. 我有权限要求,有些省市区是没有权限的,就没有颜色了怎么办?
我觉得您的写法应该是下面这样的:
const config = {
// ...
fillColor: {
field: 'shopCount',
value: ['#B8E1FF', '#9AC5FF', '#7DAAFF', '#5B8FF9', '#3D76DD'],
scale: { type: 'quantile'},
},
}
这样的写法无法处理数据为0的图层,只需要加这样一条数据即可:unknown: '#fff'
:
const config = {
// ...
fillColor: {
field: 'shopCount',
value: ['#B8E1FF', '#9AC5FF', '#7DAAFF', '#5B8FF9', '#3D76DD'],
scale: { type: 'quantile',unknown: '#fff'},
},
}
图例
图例使用非常简单,直接复制官网代码就出来了。注意要包裹在<LarkMap> </LarkMap>
里面
<LegendCategories
isStrokeColor
labels={['Category a', 'Category B', 'Category C']}
colors={{ startColor: 'rgb(176, 242, 188)', endColor: 'rgb(37, 125, 152)' }}
/>
Q&A
1.图例使用确实简单,但是中国地图对应区间的范围和颜色的映射我咋知道呢?
这里需要结合容器组件的能力去实现,L7本身提供了这部分的能力,在LarkMap
里面,我们只需要使用容器组件就可以拿到对应的图层实例,就能实现这个需求了。
注意点:1️⃣ 这里加useCallback
是因为️该方法会影响组件的重渲染,导致地图组件原先操作过的内容全部丢失(比如点击效果,select样式,会丢失);2️⃣ 这里加setTimeout
是因为下钻上卷操作时有异步问题,您使用过程中如果没有这类问题可以不加
// 1. 在中国地图的图层中获取到对应的数据
<ChoroplethLayer
// 1.1 拿到图层ref
ref={layerRef}
onDataUpdate={useCallback(() => {
setTimeout(() => {
// 1.2 通过layer.getLegend 能够拿到地图图层的图例数据
const items =
layerRef.current?.layer?.getLegend('color')?.items ||
undefined;
const color = items?.map((item: any) => item.color);
const value = items?.map((item: any) => item.value);
if (items) {
setLegendItems({ color, value });
}
}, 0);
}, [level])}
//...
></ChoroplethLayer>
// 2. 拿到图例数据后使用
<LegendCategories
geometryType="circle"
labels={legendItems.value}
colors={legendItems.color}
/>
控件
一般的控件和图例一样,直接复制一行代码就可以使用,我这边不再多说,主要说一下自定义控件。
在地图的各个方位都可以放置自定义控件,换句话说就是需要地图结合外界的一些内容和操作都要用到自定义控件来完成。
<CustomControl position="topleft">
<h1>自定义内容</h1>
</CustomControl>
Q&A
1.为什么我写在下面的控件,反而在上面渲染?
是的,地图引擎就是这样设计的。
2.官方的控件都是内置样式和位置,我们设计师要求的不一样怎么办?
首先,如果你只是位置要求不一样,可以直接设置position
,每个内置控件都有position
。如果你要求的样式不一样,那么可以采用自定义控件,然后去L7官网找对应的文档去手动触发事件。下面的例子实现了自定义放大和缩小:
<CustomControl position="rightbottom">
<img
// scene怎么获取? - https://larkmap.antv.antgroup.com/components/lark-map/hooks/use-scene/use-scene
onClick={() => scene?.zoomOut()}
src="xxx"
/>
<img
onClick={() => scene?.zoomIn()}
src="xxx"
/>
</CustomControl>
3.自带的全屏控件样式我不想要,在L7我也没找到手动触发全屏的事件,怎么办?
推荐使用ahooks中的useFullscreen
,再补充一些常用的键盘事件。原生的全屏事件也可以。
// 这里的ref只是地图外层容器的ref,不需要地图的ref
const [isFullscreen, { toggleFullscreen }] = useFullscreen(divRef);
useEffect(() => {
const listener = (event: any) => {
if (event.code === 'Escape') {
toggleFullscreen();
}
};
if (isFullscreen) {
window.addEventListener('keydown', listener);
} else {
window.removeEventListener('keydown', listener);
}
});
下钻、上卷和单击
在L7Plot里面有区域钻取的功能,在LarkMap中没有内置,需要自己做。具体怎么做呢?
- 熟悉数据源,我们发现L7的数据SDK中提供了各层级数据获取的方法,既然有这个方法那就好办了
// 获取浙江省围栏
source.getChildrenData({
parentAdcode:330000
parentLevel:'province',
childrenLevel:'province'
});
// 获取浙江省所有的市
source.getChildrenData({
parentAdcode:330000
parentLevel:'province',
childrenLevel:'city'
});
// 获取浙江省所有县
source.getChildrenData({
parentAdcode:330000
parentLevel:'province',
childrenLevel:'county'
});
- 监听地图的双击事件,双击需要下钻
<ChoroplethLayer
onDblClick={useCallback(
(item: any) => {
const properties = item.feature.properties;
// getLevel 用于获取下一层级围栏信息
const levelMsg = getLevel(properties);
setLevel(levelMsg);
},
[level],
)}
/>
useEffect(()=>{
// 获取到新层级的数据,然后设置地图的source
getMapData();
},[level])
- 监听地图外的双击事件,实现上卷
<ChoroplethLayer
onUndblclick={useCallback(
() => {
// getParentLevel 用于获取父层级的数据
const parentLevel = getParentLevel(level);
setLevel(parentLevel);
},
[level]
)}
/>
useEffect(()=>{
// 获取到新层级的数据,然后设置地图的source
getMapData();
},[level])
- 监听单击事件,触发列表切换
<ChoroplethLayer
onClick={useCallback(
(item: any) => {
// 获取点击的要素的属性
const properties = item.feature.properties;
// 获取城市名称
const name = properties.name;
// 设置选中的城市
setSelectedCity({ name });
// 显示折叠面板
setShowFold(true);
},
[level],
)}
/>
Q&A
1.为什么在市和区之间操作还挺快的,上卷到省就卡的不行?
这个问题和前面【中国地图中Q&A】的第二个问题一致,因为省级的点位数据实在是太多了,渲染起来太久了,所以建议您按需抽稀,会加快渲染速度。
绘制+复杂自定义组件+热力图+点位图+绘制撤回+高德API接入
需求描述
我需要一个全国地图,支持搜索(搜索能力要支持到门店级别,也支持省市区级别),搜索出结果自动跳转过去并且那个区域要外围圈选高亮。允许在地图上绘制AOI商圈,刚进来获取用户的位置后用等距圆绘制。
- 等距圆:默认是1km为一个等距,需要3km。用户绘制是只需要选中一个点即可自动圈选该点位3km的商圈(具体参考下图)
- 画圆:用户可自定义的绘制圆
- 画多边形:用户可自己绘制多边形
无论是绘制什么商圈,右侧都需要自动识别该商圈的名称(离中心点最近的一个大型门店名称即可),然后获取该商圈附近的一些配套信息数据。
左下角有一个热力图和点位图的切换能力,可以看该商圈的热力信息和点位信息。
系统内置三环
支持自己绘制
技术实现
基础绘制(画圆、画多边形)
在地图上想要绘制,无论是点、线、面都可以通过LarkMap的useDraw实现。注意:该 Hook 需放到容器组件内部才能使用。
示例我就不写了,直接看文档就会。重点讲几个问题。
Q&A
1.为什么我按照官方文档cv下来没有生效,不能在地图上画画?
因为官方文档为了方便我们看,把这个hooks放在了LarkMap的同一层级上。仔细看代码中有一行注释——useDraw 必须在 LarkMap 的子孙组件中调用。 请务必将官方例子中过的CustomDraw
,单独抽一个组件,再引入到LarkMap的文件中。
2.怎么根据上面的radio切换然后换形状绘制?之前绘制的内容要删除吗?
有一个API是type,支持绘制多种形状,只需要修改type即可。之前绘制的内容会直接清空。
const CustomDraw = () => {
const { enable } = useDraw({
type: 'polygon',
options: {},
});
useEffect(() => {
enable();
}, [enable]);
return null;
};
-
point :点
-
lint:线
-
polygon:多边形
-
rect:矩形
-
circle:圆
3.第二个问题的基础上再问,如果切换了type就会导致之前的内容清空,我想要保留怎么做?
感兴趣也可以参考L7-Editor的实现方式:链接
这个问题就比较复杂了,原生的绘制并不支持,需要自己实现。代码量有点大,在这里展开说有点多,所以我这边简单画了一下实现思路,如果确实有这块需求,感兴趣的可以私聊我提供源码。
简单理解就是绘制组件只绘制当前选中的形状,其他的图形交给LarkMap用面图层去绘制。
这里还有一个小问题,圆要怎么画?实际上我们绘制的圆转换为coordinates围栏信息的时候,是64个点组成的,将64个点的数据传递到面图层中,自然就会形成一个圆(不是完美圆)。
4.怎么被动触发已绘制图形的状态?比如我外面有个表格,我期望点击表格地图上的绘制图形也会激活
// TIPS:注意Q&A的第一个问题,useDraw要放在单独的子孙组件里面
// 1. 从useDraw中获取draw实例本身
const { draw, enable, disable, setDrawData } = useDraw({
type: 'circle',
options: {},
});
// 2. 将draw的方法丢出去给LarkMap层使用
React.useImperativeHandle(ref, () => ({
setActive: draw.setActiveFeature,
}));
4.1. setActive是传出去了,但是需要给一个id,这个id怎么获取啊?
首先,如果你是通过现场绘制的图,绘制完成的图自带id,直接传那个id即可。如果是你画了图,数据存储到后端了,也务必让后端同学将这个id原封不动的回传给你。
如果你是后端计算的路径,没有通过前端绘制获得,后端那边通过经纬度算得的形状,此时也不需要担心。你可以通过source拿到id,再去设置。source拿Id是根据properties里面的参数匹配的。
export const CustomLayer = () => {
const myBubbleLayer = useLayer('myBlockLayer');
useEffect(() => {
const source = myBubbleLayer.source;
const id = source.getFeatureId('guid', 'test1');
if (id) {
myBubbleLayer.setSelect(id);
}
}, [myBubbleLayer]);
return null;
};
5.我想要监听绘制的事件,怎么监听?
多种多样,任君挑选。想要绘制完将数据传回,用Add或者Change都可以。
/**
* Drawer事件枚举
*/
export declare enum DrawEvent {
Init = "init",
Destroy = "destroy",
Enable = "enable",
Disable = "disable",
Add = "add",
Edit = "edit",
Remove = "remove",
Clear = "clear",
Change = "change",
DragStart = "dragStart",
Dragging = "dragging",
DragEnd = "dragEnd",
Select = "select",
AddNode = "addNode",
RemoveNode = "removeNode"
}
useEffect(() => {
draw.on(DrawEvent.Add, () => {});
draw.on(DrawEvent.Edit, () => {});
draw.on(DrawEvent.Remove, () => {});
draw.on(DrawEvent.Select, () => {});
}, [draw]);
6.我想要控制允许和禁止绘制、控制绘制个数、控制颜色、控制展示文案等等,我该怎么做?
参考L7Draw文档:l7draw.antv.vision/docs/super/…
绘制等距圆
等距圆用useDraw和L7Draw都无法直接绘制,无论是等距圆也好,或者其他自定义的图也罢,这一类无法通过原生能力直接支持,但是又属于绘制类的,则需要利用原生能力+自定义能力来做。这一方面用LarkMap就很方便。
就以这个需求为例,我们可以看到用户的操作只有一个,就是点击,点击之后以鼠标点击位置为圆心绘制三个圆和一些附带信息。
1️⃣ 我们很容易就能想到——绘制点,绘制点的能力是useDraw可以做的,用户每次点击画一个点我们还是能做的。那我们就先绘制点。
const { draw, enable, disable, setDrawData } = useDraw({
type: 'point',
options: {},
});
2️⃣ 绘制完以后,我们就可以获取到画完的点了(具体获取方式就是前面提到的监听绘制事件,然后把绘制完的点传回来),绘制完的点信息应该是{lng: xxx, lat: xxx}
,代表经纬度信息。
useEffect(() => {
draw.on(DrawEvent.Change, handleDrawChange);
}, [draw]);
3️⃣ 接下来我们就要画三个圆圈+三根线+三个xkm显示tag:
- 三个圆圈:其实就是三个面图层
- 三根线:其实就是三个线图层
- 三个xkm:其实就是三个标注Marker
我们利用turf的circle来帮我们生成,这样生成的数据格式同时符合线、圆的绘制要求。
三个marker也非常简单,一个圆总共是64个点位,我们根据当前是第几个来取即可:(64 / steps) * (i + 1)
「steps: 当前拆分几个圆,我们这边是3个;i: 当前第几个圆」
注意:我下面这个例子并非最优解,无论是面的绘制还是线的绘制,都支持一个source画好多个线和面。这种写法会存在问题,在后面的Q&A中我会提到。
const getCircleList = () => {
const options = {
/** 圆的默认steps 是64个点位 */
steps: 64,
/** 单位:km */
units: 'kilometers',
};
const circleList = [];
/** steps:当前拆分几个圆 */
for (let i = 0; i < steps; i++) {
/** 生成圆的coordinates */
const circleProps = circle(center, i + 1, {
...options,
properties: {
area: Math.PI * Math.pow(i + 1, 2),
},
});
/** 将64个点位根据steps平均拆分点位,用于设置几km的文案 */
const stepCount = Math.floor((64 / steps) * (i + 1));
const markerCoordinates = circleProps.geometry.coordinates[0][stepCount];
circleList.push({
...circleProps,
properties: {
id: i + 1,
area: Math.PI * Math.pow(i + 1, 2),
rightPos: {
lng: markerCoordinates[0],
lat: markerCoordinates[1],
},
},
});
}
return circleList;
};
{/* 遍历所有圆 */}
{getCircleList().map((item) => {
const commonSource = {
data: {
type: 'FeatureCollection',
features: item ? [item] : [],
},
parser: { type: 'geojson' },
};
return (
<React.Fragment key={item.properties.id}>
<PolygonLayer source={commonSource} {...polygonLayerOption} />
<LineLayer {...LineLayerOption} source={commonSource} />
<Marker lngLat={item.properties.rightPos} anchor="center">
<div
className={styles[`${CLASSNAME}-marker`]}
>{`${item.properties.id}km`}</div>
</Marker>
</React.Fragment>
);
})}
至此就完成了等距圆的实现,无论是等距圆也好,还是有其他诉求的圆也罢,都可以按照这个实现思路去做。
4️⃣ 然后再画中间的地区名称,其实中点位置已经知道了,再自定义画一个marker就可以实现了。唯一的问题可能就是怎么获取到这个地方的地区名称?——这就要用到高德地图的API了,具体的就参考高德地图的文档来操作吧。
5️⃣ 还有一个小红点,读者读到这应该不会有人不会做吧,点图层就可以实现了~
Q&A
1.我在画的时候发现切换其他的点位,原先的点位有残留面图层的阴影,怎么回事?
这就是我前面提到的这个例子并非最优解,并且还存在的问题。因为我们是遍历生成的多个面图层,每个面图层都给了id,重新绘制的时候L7会根据id清除掉之前的图层(removeLayer),而我们遍历的时候给面图层配置的options其实是一样的,所以就导致id一致,清除的时候,L7没有清理干净。
解决方案:1️⃣ 删除掉面图层的id 2️⃣ 不要遍历生成一模一样的多个面图层,而是将多个面图层组合到一起放在一个面图层里面渲染推荐
保留的残影
热力图&点位图&高德api接入
这两个都是LarkMap的基础图层,直接使用即可。唯一的问题就是如何根据当前圈定的商圈范围,在范围内显示对应热力信息和点位信息呢?
我们用高德的周边搜索即可,具体的API可以直接点击跳转过去看非常的简单。
自己绘制的不规则图用高德的多边形区域搜索即可。
搜索
LarkMap自带地点搜索——LocationSearch,本质上是高德的地点搜索
LarkMap支持的搜索是单纯的select搜索,然而要支持省市区的搜索,需要自己写。如果不知道怎么写好,这里给一个L7Editor的github参考链接,可以cv下来改一改使用。
搜索问题解决了,搜索结果跳转高亮怎么做呢?
搜索跳转
1️⃣ 跳转到搜索后的区域
scene.setZoomAndCenter(9, [lng, lat]);
2️⃣ 绘制圈选区域高亮(线图层)
<LineLayer
source={{
data: featureCollection(districtFeature ? [districtFeature] : []),
}}
{...DistrictLayerOptions}
/>
Q&A
1.我们有权限控制需求,想要按照自己后端接口返回的数据作为搜索结果,怎么设置?
建议使用customControl,自定义控件,位置定在lefttop,使用antd的select自己写。
其他问题
开发中如果还有遇到什么问题都可以在评论区提,会实时更新到这里来~
1. 页面比较拥挤,地图下面还有别的东西,鼠标滚动到地图上就下不去了怎么办?
地图滚动场景
1.1. 基础解决方案
这个你去看L7的官方文档很难找,但是也是找得到的在这里:
禁止地图交互
- 初始化的时候可以在
map
配置项设置
-
- 高德地图可查看 lbs.amap.com/api/javascr…
- mapbox docs.mapbox.com/mapbox-gl-j…
- 加载完成后设置 调用
scene
的 setMapStatus 方法
你需要用scrollWheel
来阻止地图的滚动事件,这样就可以正常的滚动到下面看列表了,并且地图的scene.ZoomIn()
可以正常的触发(注意:仅限高德地图)。
<LarkMap
onSceneLoaded={(scene) => {
scene.setMapStatus({
// @ts-ignore
scrollWheel: true,
});
}}
>
</LarkMap>
为什么加@ts-ignore
?因为官方只支持几个通用的,不过它本质上是调用各种地图本身的setMapStatus
:
setMapStatus
还有别的方案吗?- 有,scene.setMapStatus
底层会调用 map.setStatus
const myMap = new Map({
pitch:0,
style:'light',
center:[],
zoom:5,
// ...others
})
<LarkMap
id="larkmap"
// 1. 首先,mapType移除
mapType="Gaode"
// 2. 自己实例化一个map
map = {myMap}
// ...others
/>
map.scene.setMapStatus({...})
<LarkMap
id="larkmap"
mapType="Gaode"
onSceneLoaded={(scene) => {
// 通过scene.map 拿到map的实例
scene.map.setStatus({...})
}}
// ...others
/>
直接打印scene.map
看它的原型链,就找到了这个方法,如下支持的一些配置,仅供参考,具体的看官方文档:
高德setMapStatus
1.2. 进阶需求及解决方案
1.1 确实解决了这个问题,但是啊,我们PD不满意。把地图的滚动取消了,用户体验可能又不好了,只能通过右下角的加减放大。我又要用户能滚动到下面表格,我还要用户支持在地图上面滚动放大缩小。PD给出的新的交互:用户鼠标hover到地图后2秒之内地图不可以通过鼠标滚动缩放,2秒之后地图就可以了(因为用户此时进入地图专注模式「就这么解释的」)。
解决方案:借助ahooks的useHover,判断鼠标是否在地图上hover,再利用定时器判断是否停留2秒,如果停留了,则记录状态到status里面。利用useEffect实时监听hover状态的变化。下面的代码是我问GPT给出的示例,还算比较简单,将setIsHoverdOver2Seconds
替换成scene.setMapStatus
就可以了。
import React, { useState, useRef } from 'react';
import { useHover } from 'ahooks';
const HoverComponent = () => {
const [isHoveredOver2Seconds, setIsHoveredOver2Seconds] = useState(false);
const hoverRef = useRef(null);
const isHovering = useHover(hoverRef);
// 用来重置或设置计时器的 ref
const timerRef = useRef(null);
useEffect(() => {
// 当鼠标悬停状态变化时,开始或清除计时器
if (isHovering) {
// 重置状态
setIsHoveredOver2Seconds(false);
// 设置一个新计时器
timerRef.current = setTimeout(() => {
setIsHoveredOver2Seconds(true);
}, 2000);
} else {
// 清除之前的计时器(如果鼠标离开)
if (timerRef.current) {
clearTimeout(timerRef.current);
}
// 重置状态
setIsHoveredOver2Seconds(false);
}
// 组件卸载时清除计时器
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
};
}, [isHovering]);
return (
<div ref={hoverRef}>
Hover over me!
{isHoveredOver2Seconds && <div>You've hovered for over 2 seconds!</div>}
</div>
);
};
export default HoverComponent;
我什么时候选择用LarkMap?
技术栈选择(图来源云极的分享,链接于文章末尾)
我相信各位读者读到这边对LarkMap也是有了一定的了解,前面的案例里面包含了市面上部分通用的基础地图业务诉求。但是同样的一定还是会保留一定的疑虑:万一有问题咋办,有需求不满足呢? 有BUG找谁修啊? 肯定没有L7这么稳定吧?万一过几天没人维护了呢? 好像用的人不是很多诶,百度搜问题都搜不到。
首先LarkMap确实目前用的人还不算太多,截止我写这篇文章,Github上只有69个star,如果您确实不太放心当然完全建议使用L7/L7Plot去完成你的需求。但是如果你的项目是React,你肯定会知道L7的写法会有点麻烦,更推荐你尝试LarkMap,在开发之前做好技术调研,看看文档是否满足你的诉求,如果满足非常推荐使用LarkMap,会有极大的提效哦。对于LarkMap的稳定性和后续问题的担忧,其实不必过于担心,因为L7的核心同学也是LarkMap的核心开发同学。有问题也可以提issue,也推荐大家积极参与共建🙋🏻♂️
与其担心不敢尝试,不如现在就动手尝试写一个demo!体验一下LarkMap!
LarkMap的贡献者们
L7的贡献者们
必备工具
天地图【权威】
全国行政区划信息查询平台【权威】
Turf 地理空间分析库
阿里云数据可视化平台
参考链接
www.yuque.com/antv/l7/oa9…(如何设计与实现 LarkMap,作者:云极)