数据可视化数据分析【免费下载链接】kepler.glKepler.gl is a powerful open source geospatial analysis tool for large-scale data sets.项目地址https://gitcode.com/gh_mirrors/ke/kepler.gl点击查看免费下载本文以 kepler.gl 官方示例 examples/open-modal 为核心讲解如何将 kepler.gl 地图实例挂载进react-modal弹窗并通过KeplerGl组件的mint属性控制地图状态的两种生命周期行为弹窗每次重开都重置为全新状态默认还是跨开关周期保留既有状态。读完本文你将掌握弹窗场景下 kepler.gl 的挂载方式、Redux 状态注册与销毁机制以及如何用wrapTo定向派发数据加载动作可直接复用到自己的 React Redux 应用中。示例背景与核心诉求在真实的 Web GIS 应用中地图很少始终占据整块页面更多时候是以弹窗、抽屉Drawer、向导Wizard等形式按需出现。把地图放进弹窗会引入一个此前整页布局中不存在的生命周期问题弹窗关闭即组件卸载再打开时地图实例是否应该回到初始状态kepler.gl 示例open-modal专门回答这个问题。它的页面提供了两个按钮Show Kepler.gl id: foo—— 打开后地图每次都从零开始加载属于全新状态fresh state行为Show Kepler.gl id: bar—— 打开后地图保留上次关闭前的所有操作图层、滤镜、视角、配色属于保存状态saved state行为。这两种行为在同一个示例里并存差异仅来自KeplerGl组件的一个布尔属性mint行为mint 取值说明Fresh statemint: true默认值每次弹窗重新打开都会重挂载地图卸载时销毁其在 Redux 中的状态Saved statemint: false卸载时保留 Redux 中的状态重新打开时地图恢复到上次的样子快速启动示例示例位于仓库 examples/open-modal是一个独立于 kepler.gl monorepo 的 Yarn 工程依赖通过kepler.gl/*的 npm 包引入见 package.json。第一步安装依赖touch yarn.lock yarn首次运行必须先执行touch yarn.lock仓库根目录本身是一个 Yarn workspacemonorepo手动创建空的yarn.lock后Yarn 才会把examples/open-modal识别为独立项目而不是 kepler.gl monorepo 的子包从而避免与根目录锁文件互相干扰。第二步启动应用yarn start该命令通过node esbuild.config.mjs --start启动开发服务器esbuild 会在http://localhost:8080上提供页面并开启 watch 模式代码改动即时热更新同时自动打开浏览器。启动细节可以查看 esbuild.config.mjs构建入口是src/main.js产物输出到dist/bundle.jsindex.html会被拷贝到dist/作为服务入口。如果希望从仓库根目录的.env文件注入环境变量如 Mapbox 令牌可以使用yarn start-local关于 Mapbox 令牌示例在 app.js 中通过process.env.MapboxAccessToken读取令牌esbuild 构建时用环境变量或根目录.env进行替换见 esbuild.config.mjs。未配置时回退为占位字符串底图样式将无法正常加载。请自行前往 Mapbox 官网注册账号并申请访问令牌后将其配置为环境变量或写入仓库根目录的.env文件。入口与 Redux Store 结构示例的入口文件 main.js 非常精简创建 React 根节点用react-redux的Provider包裹App /接入通过createStore构建的 Redux store。Store 的构造在 store.js 中它清晰地展示了 kepler.gl 在 Redux 应用中的标准集成方式import {createStore, combineReducers, applyMiddleware, compose} from redux; import keplerGlReducer, {enhanceReduxMiddleware} from kepler.gl/reducers; import appReducer from ./app-reducer; const reducers combineReducers({ keplerGl: keplerGlReducer, app: appReducer }); const middlewares enhanceReduxMiddleware([]); const enhancers applyMiddleware(...middlewares); const composeEnhancers window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; export default createStore(reducers, initialState, composeEnhancers(enhancers));几个要点keplerGlReducer被挂在keplerGl键下所有地图实例的状态都存放在state.keplerGl下以实例id为键区分例如state.keplerGl.foo、state.keplerGl.barenhanceReduxMiddleware([])用于包装你已有的 Redux 中间件列表让 kepler.gl 的实例转发动作forward action机制可以正常工作示例自己维护了一个极简的appReducer见 app-reducer.js只负责一件事用SHOW_MODALaction 记录当前打开的是哪个弹窗modal字段以及用INIT标记应用加载完成。弹窗的开关完全由应用自身控制与 kepler.gl 无关。弹窗开关app.js 中的双实例编排核心页面组件 app.js 同时挂载了两个react-modal弹窗每个弹窗内部放一个独立的 kepler.gl 实例const _closeModal () dispatch(showModal(null)); const _openModal id dispatch(showModal(id)); return ( div style{{position: absolute, width: 100%, height: 100%}} button onClick{() _openModal(foo)}Show Kepler.gl id: foo/button button onClick{() _openModal(bar)}Show Kepler.gl id: bar/button Modal isOpen{modal foo} ... FreshMap mapboxApiAccessToken{MAPBOX_TOKEN} idfoo / /Modal Modal isOpen{modal bar} ... SavedMap mapboxApiAccessToken{MAPBOX_TOKEN} idbar / /Modal /div );showModal是应用自定义 action把当前弹窗 id 写入state.app.modal两个弹窗分别渲染FreshMapid 为foo和SavedMapid 为barreact-modal的isOpen由state.app.modal驱动。弹窗关闭时react-modal会卸载其中的子树这正是测试生命周期行为的前提。除了弹窗本身app.js还有一个值得注意的细节对bar实例在挂载后立即用wrapTo定向派发一次addDataToMap将示例数据加载进地图app.jsuseEffect(() { if (barMap) { dispatch( wrapTo( bar, addDataToMap({ datasets: sampleData, options: {centerMap: true}, config: {mapStyle: {styleType: dark-matter}} }) ) ); } }, [!!barMap]);这里体现了 kepler.gl 多实例场景下的一个关键约定所有针对具体实例的 action 都必须用wrapTo(id, action)包装否则 action 只会作用于默认实例map。关于wrapTo的底层机制见后文。两种地图组件的实现差异mint 属性弹窗内的地图组件是理解整个示例的核心。两个文件结构几乎一致差异只有一个 prop。FreshMap默认的 mint: truecomponents/fresh-map.js 中KeplerGl组件没有显式传入mintKeplerGl mapboxApiAccessToken{mapboxApiAccessToken} id{id} width{dimensions.width} height{dimensions.height} /根据源码mint的默认值是true见 container.tsx。它承担两件事在组件挂载时通过dispatch(addDataToMap({...}))加载sampleData和config同样用wrapTo(id, ...)定向到本实例用ResizeObserver监听容器尺寸变化把宽高同步给KeplerGl确保弹窗尺寸变化时地图可以自适应。弹窗关闭、FreshMap卸载后kepler.gl 会删除该实例在 Redux 中的状态下次打开时重新注册一个全新 entry 并再次加载数据因此每次打开看到的都是初始视角和初始图层。SavedMap显式的 mint: falsecomponents/saved-map.js 与FreshMap的唯一实质性差异就是多了一行KeplerGl mapboxApiAccessToken{mapboxApiAccessToken} id{id} width{dimensions.width} height{dimensions.height} mint{false} /mint: false告诉 kepler.gl卸载时不要删除该实例的状态。于是弹窗关闭再打开时Redux 里state.keplerGl.bar的数据依然存在组件重新挂载后直接复用它用户上一次调整的图层、滤镜、配色、相机视角全部保留。也正因为状态在卸载后仍然存活示例才在app.js中只对bar实例做了挂载后数据已就绪时用wrapTo补发数据的处理——如果不用mint: false卸载后实例状态被清空重新打开时这段补发逻辑就成了唯一能恢复数据的方式。提示mint: false会带来状态常驻内存的开销适合弹窗/抽屉频繁开关、希望保留用户操作现场的场景如果地图数据量大且每次打开都应重新查询默认的mint: true更合适。源码纵深mint 背后的状态注册与销毁要理解mint为什么能决定状态存留需要看KeplerGl组件的容器实现 container.tsxconst { id map, getState state state.keplerGl, mint true, ... } props; useEffect(() { // add a new entry to reducer dispatch( registerEntry({ id, mint, mapboxApiAccessToken, mapboxApiUrl, mapStylesReplaceDefault, initialUiState }) ); // cleanup return () { if (mint ! false) { // delete entry in reducer dispatch(deleteEntry(id)); } }; }, [id, dispatch, initialUiState, mapStylesReplaceDefault, mapboxApiAccessToken, mapboxApiUrl, mint]);从源码可以提炼出完整的生命周期语义挂载mount组件挂载时向 kepler.gl reducer 派发registerEntry在state.keplerGl[id]下初始化一个实例条目。如果多次用同一个id挂载后面的实例会覆盖前面实例的空白状态官方文档中称之为blank state卸载unmounteffect 的 cleanup 中有一个判断——mint ! false时才派发deleteEntry(id)销毁状态。mint: false会跳过销毁让状态留在 reducer 中id 变更rename如果idprop 从旧值变为新值会派发renameEntry把旧 id 下的状态整体迁移到新 id 下避免状态丢失状态就绪只有当state.keplerGl[id]存在时容器才会真正渲染内部的地图 UI否则渲染空 div这也解释了mint: false时重新打开弹窗能立即显示旧状态的原因——条目从未被删除。registerEntry/deleteEntry/renameEntry这几个 action 均来自kepler.gl/actions示例 store.js 中挂载的keplerGlReducer负责响应它们。如果你需要在弹窗关闭后主动回收某个mint: false实例的状态可以在应用侧显式派发deleteEntry(id)而不必等待组件卸载。wrapTo多实例下定向派发动作的底层机制示例中反复出现的wrapTo是 kepler.gl 多实例架构的基石其实现位于 action-wrapper.tsfunction wrapToImpl(id: string, action: IKeplerGlAction) { return { // keep original action.type type: action.type, // actual action payload: { ...action, meta: { ...action.meta, _id_: id } }, // add forward signature to meta meta: { ...(action.meta || {}), _forward_: FORWARD, _addr_: getActionForwardAddress(id) } }; }wrapTo(id, action)会把原始 action 包装成转发动作在meta中写入_forward_: redux-forward/FORWARD和_addr_: KG_ id.toUpperCase()在 payload 的meta中写入目标实例 id_id_。kepler.gl reducer 会检查 action 中的这些签名命中转发标记后把动作路由到指定 id 对应的实例子 reducer从而保证addDataToMap、图层配置修改等动作只影响目标实例不会串到其他实例详细说明可继续阅读仓库中的 forward-actions.md。wrapTo支持柯里化可以先const wrapToBar wrapTo(bar)再复用适用于在多个地方向同一实例派发动作的场景。在示例中wrapTo配合useSelector(state state.keplerGl.bar)使用app.js监听bar实例状态是否存在一旦就绪就补发一次addDataToMap保证bar弹窗首次打开即有数据。示例数据与配置一次加载两个图层的完整演示FreshMap挂载时加载的数据与配置分别位于data/sample-data.js旧金山城市树木数据集San Francisco Trees包含TreeID、Species、Address、Location_latitude、Location_longitude等字段作为datasets传入configurations/config.js一份标准 kepler.gl v1 schema 配置同时定义了两个图层point 图层id1s8r5mdlabellocation以Location_latitude/Location_longitude为经纬度列用Species做序数颜色编码colorScale: ordinal用Age做sqrt尺寸编码配色采用 Uber 的 Ice And Fire 色带hexagon 图层id7otjdzlabelDensity聚合六边形密度图层worldUnitSize: 0.8、resolution: 8、enable3d: false颜色与尺寸聚合方式均为average配色采用 ColorBrewer 的 GnBu-6。配置还包含交互设置tooltip 显示TreeID等字段、brush、geocoder、地图状态中心点约在旧金山37.7654, -122.4629缩放级别12.03以及地图样式styleType: dark-matter暗色底图。这份配置本身就是一个很好的参考模板你可以替换datasets与config在弹窗中展示自己的数据与预置图层。自适应尺寸弹窗内地图的宽高处理弹窗中地图的常见坑是尺寸为 0 导致空白。两个地图组件都通过ResizeObserver解决这个问题见 fresh-map.jsuseEffect(() { if (!containerRef.current) return; const observer new ResizeObserver(entries { const {width, height} entries[0].contentRect; setDimensions({width, height}); }); observer.observe(containerRef.current); return () observer.disconnect(); }, []);容器 div 占满弹窗width: 100%、height: 100%观测到尺寸变化后通过 state 把实际宽高传给KeplerGlwidth/height是必填 props。这样无论是弹窗初次打开、窗口缩放还是弹窗动画导致的尺寸变化地图都能正确重绘。在自己的项目中若弹窗有自定义动画或延迟渲染建议在弹窗完全展示后再挂载地图或再观测一次尺寸避免拿到 0 宽高。小结通过open-modal示例可以看到在弹窗类场景中嵌入 kepler.gl 的完整实践路径Store 集成用keplerGlReducerenhanceReduxMiddleware构造 Redux store实例状态按id存放于state.keplerGl弹窗编排用react-modal控制挂载/卸载每个弹窗放一个带唯一id的KeplerGl生命周期控制mint: true默认保证每次打开都是全新地图mint: false跨开关周期保留用户操作现场——这是示例最核心的取舍点定向派发用wrapTo(id, action)把addDataToMap等动作路由到指定实例多实例共存互不干扰尺寸自适应用ResizeObserver把容器实际尺寸同步给地图组件。这套模式同样适用于抽屉、标签页切换、向导流程等一切挂载—卸载—再挂载的场景。相关配套资料可继续阅读仓库中的 get-started 示例Redux 集成入门、custom-reducer 示例自定义 reducer 与实例交互以及 kepler.gl 官方文档组件 props 与 actions 完整说明。赞分享数据可视化数据分析【免费下载链接】kepler.glKepler.gl is a powerful open source geospatial analysis tool for large-scale data sets.项目地址https://gitcode.com/gh_mirrors/ke/kepler.gl点击查看免费下载相关推荐Ant Design Modal 嵌套弹框Nested Modal完全指南多层弹窗层级、遮罩与静态方法实践Ant Design Modal 嵌套弹框Nested Modal完全指南多层弹窗层级、遮罩与静态方法实践 本文基于 ant design 仓库中 com前端UI组件设计系统antd Modal 手动更新与销毁实战基于 instance.update / instance.destroy 控制对话框生命周期antd Modal 手动更新与销毁实战基于 instance.update / instance.destroy 控制对话框生命周期 Modal 的静态方法前端UI组件设计系统antd 实战在 Modal 弹窗中内嵌 Form用弹窗 表单模式实现列表页的新建项操作antd 实战在 Modal 弹窗中内嵌 Form用弹窗 表单模式实现列表页的新建项操作 本指南聚焦 ant design https://link前端UI组件设计系统上一篇如何快速构建Node.js单文件可执行程序Nexe完整指南下一篇最实用的JavaScript取色工具Color Thief使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
