上次在动态里面提到了我需要接下来会继续完善模块。这里我会写下总体的技术规划。
实现目标与思路
大致把思路理一下吧。我们先假设建筑物和道路是固定的——毕竟这是公交编辑器,大而全的编辑器我暂时无力驾驭。
那么我们假设引入编辑模式的概念,先定义一个公交编辑模式,在逻辑上(虽然底层实现会对道路进行编辑操作)是在现有的路径和建筑的的基础上进行操作,是一个新增新信息而不是修改的过程。我们需要实现以下功能:
新建公交线路。需要的操作为:
- 手动选择或者新建一些站点,得到纯站点公交线路
- 手动按顺序选择一定的路径,得到公交线路。这里可能需要用到一些绘制的操作,即通过鼠标指定控制点等方式灵活选择。可能需要处理:双向公交。
- 自动按照站点生成公交线路。暂时还没想好怎么实现
- 对线路元信息的修改,包括名字,运营单位等等
编辑公交线路。即对于已经存在的公交进行编辑
- 对站点进行新增,删除,重新排序
- 对路径进行拓展,收缩,删除和重新连接。
- 对线路元信息的修改,包括名字,运营单位等等
删除公交线路
- 直接删除即可,后续可能会考虑拼接线路的需求
以上对于单条线路而言的。
同时,我们还需要方便地检索线路信息。目前的大纲还算还行,可以保留。
在以上的本职工作完成了之后,我们还得保证这个编辑器理论上拥有可以不离开编辑器就能进行完备的 OSM 编辑功能。这里我们可以把现有的功能作为一个“模式”保留,虽然很难用很抽象,但是起码功能是完备的,毕竟 Happy Path 可能可以 cover 80% 以上的需求,但是剩下的如果用到要去别的编辑器倒来倒去那就很难绷了我估计会被喷死
初步的构思是新增“编辑模式”的抽象,把现有的逻辑全部干掉变成 fallback 的一个模式。再在新的模式上面写新的好的功能。希望可以彻底摆脱半成品的尴尬地位,至少可以达到生产力的需求——至少也要达到 JOSM 的水平,吧。
技术路线
目前的技术实现我不是很满意,所以我会列出很多需要改进的地方。
经过了一番思考我决定吧原来的一些写法推翻重写,所以原先的技术实现如果在这里没有提及,一般来说就是被“优化”掉了。这里可以看作是一个相对完整的技术路线
视图的抽象
这次会将 Outline 和 Property 规范为“视图”,就是一组自适应长度的规范的元素,功能一定,但是理论上可以在任何地方打开。这里可以方便为右键菜单做同一个抽象形式。
考虑到 React 的 Functianal Component, 只需要规定他们的 props 相同 Type 即可。
右键菜单
右键菜单的本体应该是视图,不过它表现上可以作为一个类似 modal 定位的 component,可以在制定的 px 下面显示出来。调用者给他一个视图作为 child 就行,类似一个工具类吧。可以作为 Map View 的子 util
Outline 视图
视图当中应该有一个可以用来列出和快速检索当前的所有 metadata 的一个模式,需求:
- 能够完整得到当前获取的数据,包括 Relation, Way, Node
- 能够得到我们关心的某一类数据,比如公共交通数据
- 能够得到我们编辑中的数据,比如 Selected
这里我们可以用 Tabs 来实现。然后在对应的 Click Button (比如 Select )里面去做。
Property 视图
显示当前的 Active 元素。Select 元素可以有多个, Active 属于 Select 且唯一。
最好根据类型做判断。已知类型专门显示,最后来个 fallback 纯手工视图。
重构 Zustand 状态管理
首先修复 persist state
其次,需要拆分 state 了,完全不同的状态需要不同的 slice, 比如 zoom 和 lat lon 可以用 URL store 中间件,而编辑数据需要存在 zundo 中间件。最后在这些 middleware 暴露的接口上面组成一个完整的全局 store 以供使用。
编辑模式的抽象
编辑模式是新引入的一个抽象,属于地图视图,是地图视图的子模式。
模式需要展示数据和编辑数据,数据的展示拥有一组图层(Layer)来显示组件。图层的 JSX 最外层就是一个 PIXI Container, 在 Container 里面根据数据状态绘制各种图像。
对于编辑功能需要一个 Controller, 接受用户的输入,对数据进行操作。由于数据是 Zustand 提供的本地全局状态,因此可以自动和数据展示解耦。
Controller 的实现会比较复杂,所以会给 Controller 一个状态机(stateMachine)来做抽象,归纳用户行为在对应状态下对数据的影响,可以理解成状态机状态转换的输出。
State Machine 实现
现在有 State Machine 的实现,就是不够模块化。虽然不需要搞出行为树这种高级抽象(虽然已本质上没啥差别,主要体现在外围 function 上面),搞点 substate 的抽象还是可以的,这样就可以写类似“按下按钮之后画一个点”这样的抽象。
因为需要模块化,就需要在运行时 new 一些 instance 出来,这样就不会出现不同的 substate 打架的可能性(如果后续拓展的话)。现有的代码都需要一次彻底的重构。
以及,将状态机操作数据看作一种输出,应该也算能够理解的行为,毕竟至少数据还是单向流动的。所以暂时不对这个部分开到,毕竟可以将 zustand 提供的接口视为 edit system, data model 和 view model 的复合,至少在目前还没有局限,后续根据 zustand 的灵活性也能定制拓展。
新建公交相关业务逻辑编写
现在有了新的抽象,就可以对这个抽象去写出新业务了。
新建空公交线路
按下新建公交按钮,新建空线路,进入公交编辑模式。
这里就新建一个 Relation,并且需要一个编辑 metadata 的引导:使用 property view 。那么这个 property view 的实现暂时放着,我们后续根据需求重新调整抽象。
总之,名字什么的还是需要提示的,但是也支持高级的关系编辑直接加 tags
新建和添加站点
这个应该算一个子编辑模式?反正是由 State Machine 控制的,属于 Mode 抽象内部的私事,耦合就耦合吧。
在这里可以做三件事:
- 按下新建简易站,绘制一个点,自动添加到站点中
- 按下新建大站(?换个名字吧),绘制一个闭合的多边形,自动添加到站点中
- 选择一个公交站,右键,打开菜单,将其加入关系。
然后还可以在 property view 里面给站点的顺序进行调整。
手动新建线路
这里需要引入新的,复杂的业务了。
具体参考我的手稿,大体就是通过指定控制点,通过简单的最短路算法计算并且规划线路。这个为了编辑实时性得本地计算,暂时写个简单的抽象进去。就算寻路全烂了,大不了每过一个路口打一个控制点。
自动新建线路
研究 OSRM 的 API, 看看他们怎么规划,然后直接无脑丢。
编辑公交线路
需求类似上方,引入一个编辑线路流程
选择线路
这个确实需要。就是说地图上要有一个显示已有线路的样子,然后可以右键线路进入编辑。
然后呢,还有一个 Outline View 应该也可以点进去
局部重绘
在已有线路上确定一个开始点和一个结束点,参考之前的逻辑
全部重开
同前
手动指定关系的路径
这个得和 property 里面搞配合了。我想到的是 blender 的行为,就是把关系编辑器搞个吸管去 select,但是不清楚怎么抽象好。可能会开洞。
暂时不做也行,相信纯手工模式
fallback 纯手动
这个我们可以把剩下的功能没做进去的全部打包成一个纯手工模式,嗯,非常好。
一些跨 View 的关系管理
这很重要,比如典中典 select 应该怎么处理。就是说一些需要开洞实现的功能怎么操作。
我怎么做到按下 Property View 中的吸管的时候把 Map View 的状态给切了去 select, 而且能 select 到的是当前显示的实体中可以兼容的部分。这个还需要思考,可能还会推翻现在的草稿设计。
评论