npm package: @qqsl/qqsl-utils
Description: 这个库是一个纯工具库，为业务的不同部分提供相应的工具方法。 src目录下的每一个子目录都是独立的一部分，子目录中既有工具方法的实现，也有对应的单元测试。 再次开发时也应以此为准
Latest version: 0.0.13

# QqslUtils

这个库是一个纯工具库，为业务的不同部分提供相应的工具方法。
src目录下的每一个子目录都是独立的一部分，子目录中既有工具方法的实现，也有对应的单元测试。
再次开发时也应以此为准

## 更新历史

0.0.10

1. MapboxGeoJsonSource新增配置项sourceOptions?: mapboxgl.GeoJSONSourceOptions。可用来配置promoteId等属性

0.0.9

1. 新增装饰器AutoUpdate和工具函数AutoBatch

0.0.8

1. fix bugs

0.0.7

1. 修改创建气泡时参数online为onPoint
2. 新增工具函数centerAndZoom

````.js
// 当指定zoom时，如果当前zoom小于指定zoom，则不设置zoom。
// 该方法可在地图实例化之前调用，会等待地图实例化完成，如果在地图实例化之前多次调用，则只触发最后一次。
centerAndZoom(coors: [number, number], zoom?: number): void
````

0.0.6

1. 若气泡由地图创建，则当静态气泡打开时，默认禁止打开临时气泡。(对应方法修改如下)

````.js
createTemporaryPop<T>(
    createContent: (data: T) => HTMLElement | string,
    onPoint = false,
    disabledWhenStaticPopActived = true // 当静态气泡激活时，是否禁止临时气泡
  ):TemporaryPopControl<T>
````

0.0.5

1. 修复filter新旧语法在cluster中不兼容的问题
2. 修复气泡未打开时，调用close报错

0.0.4

1. 支持在将气泡绑定至图层时（调用bingPopControl）的时候，指定参数update、destroy，来配置气泡的同步行为。（update为true则会自动更新气泡，destroy为true则会自动关闭气泡）

0.0.3

1. 向外暴露函数isNormalAreaMapPaint，该函数用于判断样式配置是否是pattern
2. 新增函数getAreaConfig，用于用户自己便捷构建面图层配置，该函数返回AutoCreatedFillpaintConfig，如果面配置为pattern，则返回值中包含fill-pattern，否则返回值包含fill-color
3. 修复面图层数据未与自动边界outline绑定
4. 修复面图层修改visiblity后未同步至自动边界的visibility

0.0.2

1. MapboxGeoJsonSource支持传入Observable<mapboxgl.Map>作为参数，异步初始化数据源
2. BaseMapLayer.bindPopControl 支持气泡与图层绑定，当图层所依赖的数据源变化时，自动对比该气泡与新的数据源，如果气泡所依赖的数据项被移除，则自动关闭气泡。由于数据对比是通过features里的properties，所以请确保properties中可以取得对应的用来与气泡对比的唯一值

## 目录结构

```` js
- src
  -- mapbox-utils // mapbox相关的工具方法
  -- store // 配合Store使用的相关工具
  // 其他。。。
````

### mapbox-utils相关方法介绍

该目录下主要包含：

1. 针对mapbox地图初始化与地图实例控制的类（ MapboxControl ）
2. 针对mapbox数据源初始化与控制的类 （ MapboxGeoJsonSource ）
3. 针对不同类型图层初始化与控制的类 （ BaseMapLayer、PointMapLayer、PointClusterMapLayer、LineMapLayer、FillMapLayer ）
4. 针对不同类型气泡的控制类（StaticPopControl、TemporaryPopControl、PopControl）
5. 辅助控制图层的相关类 （LayerGather、LayerGroup、LayerArray）
6. 特殊情境下控制气泡的相关类 （raceFactory）

#### MapboxControl

##### useNotes

````js
class App {
  constructor(){
    // tianToken天地图token，scale是否开启
    this.mapControl = new MapboxControl(tianToken, { scale: true })
  }
}
````

MapboxControl的实例上的方法：

1. mapLoad。请使用该方法作为mapbox的load的回调，并将地图实例作为参数
2. centerAndZoom。使用该方法进行定位和缩放，如果当前缩放比例大于指定值，则不会缩放
3. createStaticPop。构建一个静态气泡并返回。可以多次调用该方法创建多个静态气泡，MapboxControl会控制这些气泡保证每次只能有一个开启
4. createTemporaryPop。构建一个动态气泡并返回。其他同上
5. connectQueue。用于批量绑定数据源和图层。为connect的变种。
6. connect。单次绑定数据源和图层。params：source，layer，index。第三个参数index为指定图层的层级，理解为zIndex即可
7. 除此之外还有一些内部方法，如需了解，自行查看源码

#### 图层相关类介绍：BaseMapLayer、PointMapLayer、PointClusterMapLayer、LineMapLayer、FillMapLayer

````js
// 该类为其他图层类的基类，如下的属性与方法基本涵盖了使用所需。除此之外，还有一些内部方法未在下方展示，有需要可以查看源码
interface class BaseMapLayer{
  // 由该类自行生成的递增id
  id: string
  // 由用户在初始化时传入的别名，用于
  alias?: string
  constructor(
    options:{
      layer?: LayerConfig, // 图层配置
      alias?: string // 别名
    },
    dynamicConfig: {
      // 是否动态配置图层属性
      isDynamic: boolean,
      // 每个feature的property中标识其样式类型的字段
      typeKey?: string,
      // 如果typeKey不满足需求，通过该回调函数返回每一个feature对应的type
      typeCreater?(p: P): string,
      // 最终通过filter对比type时，需要将原数据的type和代码设置动态样式配置时提供的type相对比。
      // 例如，数据源中有一个数据，{ type: 'old' };与其相匹配的样式类型为{ type: 'newOld' }
      // 本着尽量不修改原数据的原则，尽管我们通过typeCreater，从'old'转换出了"newOld"，从而获取到了真实的样式配置。但是在filter中对比时，仍然需要将"newOld"通过typeTransformer转换为"old"
      typeTransformer?(type: string): string,
      // 用户提供的获取图层配置的方法
      getConfig?: (type: string) => Observable<GeometryStyleConfig|undefined>
    } = { isDynamic: false }
  ){}

  /**
   * @description 将静态气泡绑定至图层, 如果图层元素有显示或隐藏的变化，将同步修改气泡的显隐性。
   * @param staticPop 静态气泡实例
   * @param force boolean 是否强制绑定
   */
  bindPopControl(staticPop: StaticPopControl<any>, force = false):void
  setFilter(filter: any[] | null):void
  setLayoutProperty(paint: { [key: string]: any }):void
  setPaintProperty(paint: { [key: string]: any }):void
  resetLayerStyle():void
  // 在地图上移除当前图层，并在数据源中清理该图层
  removeSelf():void

  on<T extends keyof mapboxgl.MapLayerEventType>(
    type: T,
    listener: (ev: mapboxgl.MapLayerEventType[T] & mapboxgl.EventData) => void,
    // 是否禁止点击穿透
    onlySelf = false,
    interceptor?: (e: mapboxgl.MapLayerEventType[T] & mapboxgl.EventData) => boolean | Observable<boolean>
  ):void

  off<T extends keyof mapboxgl.MapLayerEventType>(
    type: T,
  ): void

  once<T extends keyof mapboxgl.MapLayerEventType>(
    type: T,
    listener: (ev: mapboxgl.MapLayerEventType[T] & mapboxgl.EventData) => void,
  ):void
}
````

##### useNotes

````js
class App {
  constructor(){
    // 不需要动态配置时，自己指定图层的layout与paint配置
    this.pointLayerControl = new PointMapLayer(
      {
        layer: {
          layout: {
            'icon-image': 'icon1'
          }
        },
        alias: '业务图层别名'
      }
    )
    // 启用动态配置
    this.pointLayerControl = new PointMapLayer(
      {
        alias: '业务图层别名'
      },
      {
        isDynamic: true,
        typeKey: 'type',
        getConfig: someFunction //...函数
      }
    )
    // 如果想要启用聚合，将PointMapLayer替换为PointClusterMapLayer即可。并且对应的数据源也需要开启cluster

  }
}
````

#### MapboxGeoJsonSource

##### useNotes

````js
class App {
  constructor(){
    this.sourceControl = new MapboxGeoJsonSource(
      {
        mapApi: api // 或者api$ 即Observable<api>,
        alias: '业务别名',
        sourceOptions: {} // 其他配置，如promoteId
      },
      true // 该数据源开启了聚合
    )
    // 更新数据
    this.sourceControl.setData(data)

    // 注意，在完成数据源和图层的初始化后，需要调用mapboxControl上的connect或connectQueue方法将将二者绑定
    this.mapboxControl.connect(this.sourceControl, this.layerControl, 1)
  }
}
````

### 气泡相关类介绍

#### useNotes

````js
const pop = new StaticPopControl(
  mapApi$: api$,
  createContent: someFunction, // 用于构建气泡内容，返回字符串或者dom
  true, // 气泡在线上还是点上，线上为true，默认为false
  {
    key: 'id' // 提供key用于打开该气泡的数据的唯一标识。或者使用keySelector，动态构建id
  }
)
pop.show(data)
pop.close()
pop.update(newData) // 更新气泡，内部会通过新旧数据的id判断是否更新
pop.close('id1') // 关闭指定id的气泡
````

### 其他工具类

1. 批量管理图层：LayerGather、LayerGroup、LayerArray
2. 异步方法动态竞争：raceFactory
3. 数据源数据构建：createPointFeatures、createMultiPointFeeatures、createHashLineFeatures、createHashMultiLineFeatures、createHashAreaFeatures、createHashMultiAreaFeatures

````js

const layerGroup1 = new LayerGroup()
// 添加layer不需要指定key
layerGroup1.addTo(
  layer1
)

const layerGroup2 = new LayerGroup()
// LayerGroup、LayerArray可以互相嵌套，必须指定key
layerGroup2.addTo(layerGroup1, 'group1')

// 可以通过这种方式批量地操作图层
layerGroup2.setFilter(filters)

// raceFactory接收一个回调函数作为参数，可以在race上push可观察对象，每当当前队列中有可观察对象返回时，该回调被调用。其他可观察对象被取消，队列清空。
const race = raceFactory((v) => console.log(v))

const after100ms$ = new Observable(function(next){
  setTimeout(function(){
    next('100ms')
  }, 100)
})

// 最终会仅打印出100ms
race.push(after100ms$)
race.push(after200ms$)
race.push(after300ms$)

````

### store

1. 装饰器AutoUpdate

与Store中的装饰器Action连用，自动将返回内容更新至store实例当中

````.js

class A extends Store<{ a: number }>{

  constructor(){
    super({ a: 1 })
  }

  // 会自动同步返回的store中的a。当然，也可以返回一个简单对象，而非Observable
  @Autoupdate()
  @Action()
  exampleA(): Observable<{ a: string }> {
    return of({ a: 2 })
  }

}

2. 工具函数AutoBatch
与被AutoUpdate装饰的函数配合使用，将多个函数批量顺序调用，并将每个函数返回的store顺序合并，最终构建一个新的store并更新至实例

````.js

class AStore extends Store<{ a: number, b: number }>{

  constructor(){
    super({ a: 1, b: 5 })
  }

  // 会自动同步返回的store中的a。当然，也可以返回一个简单对象，而非Observable
  // 特别注意：使用Autoupdate装饰的函数最终的返回值应当是{ store: any, payload: any }, 其中store代表要更新的内容，payload为其他任意参数，例如，请求回来的信息
  @Autoupdate()
  @Action()
  exampleA(): Observable<{ store: any, paylaod: any}> {
    return of({ store: { a: 2 }, payload: { message: 'success' } })
  }

  @Autoupdate()
  @Action()
  exampleA(): Observable<{ store: any, paylaod: any }> {
    return of({ store: { b: 3 }, payload: { message: 'success' } })
  }

}

class BComponent{
  constructor(
    private store: AStore
  ){

  }
  doSomething(){
    // 特别注意：这里需要对函数作this的绑定
    AutoBatch([store.exampleA.bind(store), store.exampleA.bind(store)]).subscribe(res => {
      // res 这里的res是每一个函数的返回值构成的数组，顺序与函数调用顺序相同
    })
  }
}

````