@hzab/map-combine 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +67 -0
  3. package/package.json +56 -0
  4. package/src/assets/Thumbs.db +0 -0
  5. package/src/assets/add.png +0 -0
  6. package/src/assets/point.png +0 -0
  7. package/src/basic/ContextMenu.tsx +49 -0
  8. package/src/basic/MapCombine.ts +67 -0
  9. package/src/basic/MapElement.ts +32 -0
  10. package/src/basic/MapEvent.ts +18 -0
  11. package/src/basic/Point.ts +176 -0
  12. package/src/basic/PointAggregation.tsx +189 -0
  13. package/src/basic/Polygon.ts +180 -0
  14. package/src/basic/Polyline.ts +194 -0
  15. package/src/basic/ReactPoint.tsx +357 -0
  16. package/src/basic/ReactPopup.tsx +34 -0
  17. package/src/basic/Tile3D.ts +99 -0
  18. package/src/cesium/CesiumEvent.ts +86 -0
  19. package/src/cesium/CesiumMap.ts +167 -0
  20. package/src/cesium/CesiumPoint.ts +137 -0
  21. package/src/cesium/CesiumPolygon.ts +368 -0
  22. package/src/cesium/CesiumPolyline.ts +382 -0
  23. package/src/cesium/CesiumTile3D.ts +100 -0
  24. package/src/mine/MineEvent.ts +29 -0
  25. package/src/mine/MineMap.ts +90 -0
  26. package/src/mine/MinePoint.ts +139 -0
  27. package/src/mine/MinePolygon.ts +388 -0
  28. package/src/mine/MinePolyline.ts +359 -0
  29. package/src/mine/MineTile3D.ts +32 -0
  30. package/src/openlayer/OpenlayerEvent.ts +75 -0
  31. package/src/openlayer/OpenlayerMap.ts +149 -0
  32. package/src/openlayer/OpenlayerPoint.ts +177 -0
  33. package/src/openlayer/OpenlayerPolygon.ts +300 -0
  34. package/src/openlayer/OpenlayerPolyline.ts +289 -0
  35. package/src/utils/Image.ts +46 -0
  36. package/src/utils/PolygonEditor.ts +159 -0
  37. package/src/utils/PolylineEditor.ts +149 -0
  38. package/src/utils/Projection.ts +46 -0
  39. package/src/utils/index.ts +131 -0
  40. package/src/utils/static.ts +7 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ # @hzab/map-combine@0.0.1
2
+ 组件初始化
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @hzab/map-combine
2
+
3
+ 组件模板
4
+
5
+ - node@16.16.0
6
+
7
+ # 组件
8
+
9
+ ## 示例
10
+
11
+ ```jsx
12
+ import Demo from "@hzab/map-combine";
13
+
14
+ <Demo />;
15
+ ```
16
+
17
+ ## API
18
+
19
+ ### InfoPanel Attributes
20
+
21
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
22
+ | ------ | ------ | ---- | ------ | ----------------- |
23
+ | schema | Object | 是 | - | 数据信息的 schema |
24
+
25
+ # 组件开发流程
26
+
27
+ - 在 config/webpack.config.js 中按需修改 library 配置的文件名
28
+ - 在 config/webpack.config.js 中按需修改 alias 配置的包名,便于本地调试
29
+ - 在 tsconfig.json 中按需修改 paths 配置的包名,解决 ts 报错问题
30
+ - npm run dev
31
+
32
+ ## 文件目录
33
+
34
+ - example 本地开发测试代码
35
+ - src 组件源码
36
+ - lib 组件打包编译后的代码
37
+
38
+ ## 命令
39
+
40
+ - Mac 执行该命令,设置 pre-commit 为可执行文件
41
+
42
+ - npm run mac-chmod
43
+ - chmod +x .husky && chmod +x .husky/pre-commit
44
+
45
+ - 生成文档:npm run docs
46
+ - 本地运行:npm run dev
47
+ - 打包编译:npm run build
48
+
49
+ ## 发布
50
+
51
+ - 注意:示例代码生效,但发布之后未生效。确认是否执行了编译!!!
52
+
53
+ - 编译组件:npm run build
54
+ - 命令:npm publish --access public
55
+ - 发布目录:
56
+ - lib
57
+ - src
58
+
59
+ ## 配置
60
+
61
+ ### 配置文件
62
+
63
+ - 本地配置文件:config/config.js
64
+
65
+ ### webpack 配置文件
66
+
67
+ - config/webpack.config.js
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@hzab/map-combine",
3
+ "version": "0.0.1",
4
+ "description": "地图组件",
5
+ "main": "src",
6
+ "scripts": {
7
+ "dev": "npm run prepare && webpack serve -c ./config/webpack.config.js --env local",
8
+ "build": "webpack -c ./config/webpack.config.js --env production",
9
+ "publish-patch": "npm version patch && npm publish --access public",
10
+ "publish-minor": "npm version minor && npm publish --access public",
11
+ "publish-major": "npm version major && npm publish --access public",
12
+ "prepare": "husky install",
13
+ "mac-chmod": "chmod +x .husky && chmod +x .husky/pre-commit",
14
+ "docs": "typedoc"
15
+ },
16
+ "files": [
17
+ "src",
18
+ "CHANGELOG.md"
19
+ ],
20
+ "keywords": [],
21
+ "author": "CaiYansong",
22
+ "license": "ISC",
23
+ "devDependencies": {
24
+ "@hzab/permissions": "0.1.1",
25
+ "@hzab/webpack-config": "^0.2.1",
26
+ "@types/react": "^17.0.62",
27
+ "@types/react-dom": "^17.0.20",
28
+ "antd": "^4.14.0",
29
+ "axios": "^1.4.0",
30
+ "eslint": "^8.30.0",
31
+ "less": "^4.1.3",
32
+ "mobx": "^6.7.0",
33
+ "mobx-react": "^7.6.0",
34
+ "react": "^17.0.2",
35
+ "react-dom": "^17.0.2",
36
+ "react-router-dom": "^6.14.1",
37
+ "typedoc": "^0.24.8",
38
+ "typescript": "^4.9.4"
39
+ },
40
+ "directories": {
41
+ "lib": "lib"
42
+ },
43
+ "peerDependencies": {
44
+ "@dvgis/cesium-map": "^3.1.0",
45
+ "coordtransform": "^2.1.2",
46
+ "lodash": ">=4.17.21",
47
+ "ol": "^7.4.0",
48
+ "react-dom": "^17.0.2",
49
+ "zrender": "^5.4.4"
50
+ },
51
+ "lint-staged": {
52
+ "**.{js,jsx,ts,tsx,css,scss,less,json,html}": [
53
+ "prettier --write"
54
+ ]
55
+ }
56
+ }
Binary file
Binary file
Binary file
@@ -0,0 +1,49 @@
1
+ import ReactDOM from "react-dom";
2
+ import { MapCombine } from "./MapCombine";
3
+
4
+ export interface ContextMenuItem {
5
+ label: string;
6
+ onclick: () => void;
7
+ }
8
+
9
+ export class ContextMenu {
10
+ map: MapCombine;
11
+ root: HTMLDivElement;
12
+ element = (items: ContextMenuItem[]) => {
13
+ return (
14
+ <div className="combine-context-menu">
15
+ {items.map((e) => (
16
+ <div className="combine-context-item" onClick={e.onclick} key={e.label}>
17
+ {e.label}
18
+ </div>
19
+ ))}
20
+ </div>
21
+ );
22
+ };
23
+ hide() {
24
+ ReactDOM.unmountComponentAtNode(this.root);
25
+ }
26
+ constructor(map: MapCombine) {
27
+ this.map = map;
28
+ const root = document.createElement("div");
29
+ root.style.zIndex = "2000";
30
+ root.style.position = "absolute";
31
+ root.addEventListener("pointerleave", () => {
32
+ this.hide();
33
+ });
34
+ root.addEventListener("click", () => {
35
+ setTimeout(() => {
36
+ this.hide();
37
+ });
38
+ });
39
+ map.htmllayer.appendChild(root);
40
+ this.root = root;
41
+ }
42
+ show(items: ContextMenuItem[]) {
43
+ const { root, map } = this;
44
+ ReactDOM.render(this.element(items), root);
45
+ root.style.left = map.event.canvas[0] - 5 + "px";
46
+ root.style.top = map.event.canvas[1] - 5 + "px";
47
+ root.focus();
48
+ }
49
+ }
@@ -0,0 +1,67 @@
1
+ import { ContextMenu } from "./ContextMenu";
2
+ import { MapElement } from "./MapElement";
3
+ import { MapEvent } from "./MapEvent";
4
+ import { Point, PointOption } from "./Point";
5
+ import { Polygon, PolygonOption } from "./Polygon";
6
+ import { Polyline, PolylineOption } from "./Polyline";
7
+ import { ReactPoint, ReactPointOption, drawReactPoint } from "./ReactPoint";
8
+ import { Tile3D, Tile3DOption } from "./Tile3D";
9
+ export abstract class MapCombine {
10
+ /* 用于挂载html元素的根节点 */
11
+ readonly htmllayer = document.createElement("div");
12
+ readonly elements = new Map<string, MapElement>()
13
+ readonly event = new MapEvent()
14
+ readonly menu: ContextMenu;
15
+ abstract get cursor(): string;
16
+ abstract set cursor(val: string);
17
+
18
+ abstract get moveable(): boolean;
19
+ abstract set moveable(val: boolean);
20
+
21
+ abstract get center(): number[]
22
+ abstract set center(val)
23
+
24
+ abstract get zoom(): number
25
+ abstract set zoom(val)
26
+
27
+ constructor() {
28
+ const { htmllayer } = this
29
+ htmllayer.style.zIndex = "1000";
30
+ htmllayer.style.position = "absolute";
31
+ htmllayer.style.width = "0";
32
+ htmllayer.style.height = "0";
33
+ htmllayer.style.left = "0";
34
+ htmllayer.style.top = "0";
35
+ this.menu = new ContextMenu(this);
36
+ (window as any).map = this
37
+ }
38
+
39
+ createReactPoint<K>(option?: Partial<ReactPointOption<K>>): ReactPoint<K> {
40
+ const e = new ReactPoint(this, option)
41
+ drawReactPoint(this, e)
42
+ return e
43
+ }
44
+
45
+
46
+ /**
47
+ * 设置地图视图
48
+ * @param option 视图参数
49
+ * @param time 设置时间会动画形式调整视图
50
+ */
51
+ abstract createPoint<K>(option?: Partial<PointOption<K>>): Point<K>
52
+
53
+ abstract createPolyline<K>(option?: Partial<PolylineOption<K>>): Polyline<K>
54
+
55
+ abstract createPolygon<K>(option?: Partial<PolygonOption<K>>): Polygon<K>
56
+
57
+ abstract createTile3D(option?: Partial<Tile3DOption>): Tile3D
58
+
59
+ abstract canvasTgeography(p: number[]): number[]
60
+
61
+ abstract geographyTcanvas(p: number[]): number[]
62
+
63
+ destroy() {
64
+ this.htmllayer.remove()
65
+ this.event.trigger('destroy')
66
+ }
67
+ }
@@ -0,0 +1,32 @@
1
+ import { MapCombine } from "./MapCombine"
2
+
3
+ export interface MapElementOption {
4
+ name: string
5
+ }
6
+
7
+ export abstract class MapElement<
8
+ T extends MapElementOption = MapElementOption
9
+ >{
10
+ abstract type: string
11
+ readonly map: MapCombine
12
+ protected readonly _option: T
13
+ get name() {
14
+ return this._option.name
15
+ }
16
+
17
+ constructor(map: MapCombine, option: T) {
18
+ this.map = map
19
+ this._option = option
20
+ if (map.elements.has(option.name)) {
21
+ throw new Error(`已存在同名元素 ${option.name}`)
22
+ }
23
+ map.elements.set(option.name, this)
24
+ }
25
+
26
+ destroy() {
27
+ this.map.elements.delete(this.name)
28
+ this._onDestroy()
29
+ }
30
+
31
+ protected abstract _onDestroy(): void
32
+ }
@@ -0,0 +1,18 @@
1
+ import Eventful from "zrender/lib/core/Eventful";
2
+
3
+ export class MapEvent extends Eventful<{
4
+ 'pointer-down': () => void
5
+ 'pointer-move': () => void
6
+ 'pointer-up': () => void
7
+ 'left-click': () => void
8
+ 'right-click': () => void
9
+ 'double-click': () => void
10
+ 'view-change': () => void
11
+ 'view-moveEnd': () => void
12
+ 'frame': () => void
13
+ 'resize': () => void
14
+ 'destroy': () => void
15
+ }> {
16
+ canvas = [0, 0]
17
+ geography = [0, 0, 0]
18
+ }
@@ -0,0 +1,176 @@
1
+ import Eventful from "zrender/lib/core/Eventful"
2
+
3
+ import { MapElement, MapElementOption } from "./MapElement"
4
+ import { MapCombine } from "./MapCombine"
5
+
6
+ export interface PointOption<K> extends MapElementOption {
7
+ show: boolean
8
+ cursor: string
9
+ coordinates?: number[]
10
+ src: string
11
+ center: number[]
12
+ size: number[]
13
+ rotation: number
14
+ editable: boolean
15
+ userdata: K
16
+ silent: boolean,
17
+ height: number
18
+ }
19
+
20
+ export class Point<K = undefined> extends MapElement<PointOption<K>> {
21
+ type = 'Point'
22
+ event = new Eventful<{
23
+ 'data-loaded': () => void
24
+ 'data-update': () => void
25
+ 'pointer-in': () => void
26
+ 'pointer-out': () => void
27
+ 'left-click': () => void
28
+ 'right-click': () => void
29
+ 'update': (keys: Set<string>) => void
30
+ 'destroy': () => void
31
+ }>()
32
+
33
+ get userdata() {
34
+ return this._option.userdata
35
+ }
36
+
37
+
38
+ get cursor() {
39
+ return this._option.cursor
40
+ }
41
+
42
+ get show() {
43
+ return this._option.show
44
+ }
45
+
46
+ set show(val) {
47
+ if (this._option.show != val) {
48
+ this._option.show = val
49
+ this._update('show')
50
+ }
51
+ }
52
+
53
+ get silent() {
54
+ return this._option.silent
55
+ }
56
+
57
+ set silent(val) {
58
+ if (this._option.silent != val) {
59
+ this._option.silent = val
60
+ this._update('silent')
61
+ }
62
+ }
63
+
64
+ get editable() {
65
+ return this._option.editable
66
+ }
67
+
68
+ set editable(val) {
69
+ if (this._option.editable != val) {
70
+ this._option.editable = val
71
+ this._update('editable')
72
+ }
73
+ }
74
+
75
+ get coordinates() {
76
+ return this._option.coordinates
77
+ }
78
+
79
+ set coordinates(val) {
80
+ if (this._option.coordinates != val) {
81
+ this._option.coordinates = val
82
+ this._update('coordinates')
83
+ }
84
+ }
85
+
86
+ get src() {
87
+ return this._option.src
88
+ }
89
+
90
+ set src(val) {
91
+ if (this._option.src != val) {
92
+ this._option.src = val
93
+ this._update('src')
94
+ }
95
+ }
96
+
97
+ get size() {
98
+ return this._option.size
99
+ }
100
+
101
+ set size(val) {
102
+ if (this._option.size != val) {
103
+ this._option.size = val
104
+ this._update('size')
105
+ }
106
+ }
107
+ get center() {
108
+ return this._option.center
109
+ }
110
+
111
+ set center(val) {
112
+ if (this._option.center != val) {
113
+ this._option.center = val
114
+ this._update('center')
115
+ }
116
+ }
117
+
118
+ get height() {
119
+ return this._option.height
120
+ }
121
+
122
+ set height(val) {
123
+ if (this._option.height != val) {
124
+ this._option.height = val
125
+ this._update('height')
126
+ }
127
+ }
128
+
129
+ get rotation() {
130
+ return this._option.rotation
131
+ }
132
+
133
+ set rotation(val) {
134
+ if (this._option.rotation != val) {
135
+ this._option.rotation = val
136
+ this._update('rotation')
137
+ }
138
+ }
139
+
140
+
141
+
142
+ constructor(map: MapCombine, option: Partial<PointOption<K>> = {}) {
143
+ super(map, Object.assign({ ...Point.option }, option))
144
+ }
145
+
146
+ static option: PointOption<undefined> = {
147
+ name: '点',
148
+ show: true,
149
+ src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAIxJREFUOE+tU4ENgCAM6y5TL1MuEy+bmQjChAjIEkKAretIS1DBwAy3pnuXDAvgIGDT+RRfsEtYdZI6mxgoAFQWe6wAcgE0FicgHoA/aJeeF+rs/rBgYI9+u5WIFQa99K9mQwB+j1AjntLfGBlBZCssmoMAGiOkDjWmUvbcK0WVN1PGlS87i63JWTvECRNBLcaa3Jq0AAAAAElFTkSuQmCC',
150
+ center: [0.5, 0.5],
151
+ editable: false,
152
+ size: [16, 16],
153
+ rotation: 0,
154
+ cursor: 'pointer',
155
+ userdata: undefined,
156
+ silent: false,
157
+ height: 0
158
+ }
159
+
160
+ private _needUpdate?: Set<string>
161
+ private _update(key: string) {
162
+ if (this._needUpdate) {
163
+ this._needUpdate.add(key)
164
+ } else {
165
+ this._needUpdate = new Set([key])
166
+ setTimeout(() => {
167
+ this.event.trigger('update', this._needUpdate)
168
+ this._needUpdate = undefined
169
+ });
170
+ }
171
+ }
172
+
173
+ protected _onDestroy(): void {
174
+ this.event.trigger('destroy')
175
+ }
176
+ }
@@ -0,0 +1,189 @@
1
+ import { ReactElement } from "react";
2
+ import { throttle } from 'lodash';
3
+
4
+ import { LATTOONE, LONTOONE } from "../utils/static";
5
+ import { getUUID, setValue } from "../utils";
6
+ import { MapCombine } from "../basic/MapCombine";
7
+ import { ReactPoint } from "./ReactPoint";
8
+
9
+ export interface PointAggregationOption {
10
+ /** 组件名,唯一标识 */
11
+ name: string;
12
+ /** 组件是否显示 */
13
+ show: boolean;
14
+ datas: any[];
15
+ /** 小于该值的等级下会聚合 */
16
+ maxZoomLevel?: number;
17
+ /** 值越大聚合点的间距越小 */
18
+ density?: number;
19
+ element(e: any[], p: number[]): ReactElement;
20
+ location(e: any): number[];
21
+ }
22
+
23
+ class TileNode {
24
+ private _location: number[];
25
+ private _element: any;
26
+ marker: ReactPoint | undefined;
27
+ private _map: MapCombine;
28
+ constructor(map: MapCombine, location: number[], element: ReactElement) {
29
+ this._map = map;
30
+ this._location = location;
31
+ this._element = element;
32
+ }
33
+ test(east: number, south: number, west: number, north: number) {
34
+ if (
35
+ this._location[0] < east &&
36
+ this._location[1] > south &&
37
+ this._location[0] > west &&
38
+ this._location[1] < north
39
+ ) {
40
+ if (!this.marker) {
41
+ const option = {
42
+ show: true,
43
+ name: getUUID(),
44
+ element: () => (this._element),
45
+ center: [0.5, 0.5],
46
+ coordinates: [this._location[0], this._location[1], 0],
47
+ editable: false,
48
+ };
49
+ this.marker = this._map.createReactPoint(option);
50
+ } else {
51
+ this.marker.coordinates = [this._location[0], this._location[1]];
52
+ }
53
+ } else {
54
+ if (this.marker) {
55
+ this.marker.destroy();
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+
62
+ export class PointAggregation {
63
+ private readonly _cache = new Map<number, TileNode[]>();
64
+ private _current = 0;
65
+ private _map: MapCombine;
66
+ private _option: PointAggregationOption;
67
+
68
+ /** 唯一标识 */
69
+ get name() {
70
+ return this._option.name;
71
+ }
72
+
73
+ /** 是否显示 */
74
+ get show() {
75
+ return this._option.show;
76
+ }
77
+
78
+ set show(val) {
79
+ setValue(this._option, "show", val, () => {
80
+ val ? this._update() : this._turnoff();
81
+ });
82
+ }
83
+
84
+ /** 点聚合 */
85
+ constructor(map: MapCombine, option: PointAggregationOption) {
86
+ this._map = map;
87
+ this._option = option;
88
+ const { event } = map;
89
+ this.onAfterViewChange();
90
+ event.on("view-moveEnd", () => {
91
+ this.onAfterViewChange()
92
+ });
93
+ }
94
+
95
+ private _turnon(zoom: number) {
96
+ const { _option, _cache } = this;
97
+ const maxZoomLevel = this._option.maxZoomLevel ?? 17;
98
+ const cache = _cache.get(zoom);
99
+ if (!cache) {
100
+ if (zoom < maxZoomLevel) {
101
+ const cache: TileNode[] = [];
102
+ const grid = new Map();
103
+ const max = Math.pow(2, zoom) * (_option.density ?? 1);
104
+ _option.datas.forEach((data) => {
105
+ const location = _option.location(data);
106
+ const x = Math.floor(LONTOONE.forward(location[0]) * max * 2);
107
+ const y = Math.floor(LATTOONE.forward(location[1]) * max);
108
+ const k = `${x}_${y}`;
109
+ const group = grid.get(k);
110
+ if (group) {
111
+ group.push(data);
112
+ } else {
113
+ grid.set(k, [data]);
114
+ }
115
+ });
116
+ grid.forEach((e) => {
117
+ const { lon, lat } = e.reduce(
118
+ (a, b) => {
119
+ const loaction = _option.location(b);
120
+ a.lon += loaction[0];
121
+ a.lat += loaction[1];
122
+ return a;
123
+ },
124
+ { lon: 0, lat: 0 },
125
+ );
126
+ cache.push(
127
+ new TileNode(
128
+ this._map,
129
+ [lon / e.length, lat / e.length],
130
+ _option.element(e, [lon / e.length, lat / e.length]),
131
+ ),
132
+ );
133
+ });
134
+ _cache.set(zoom, cache);
135
+ } else {
136
+ const cache: TileNode[] = [];
137
+ _option.datas.forEach((e) => {
138
+ const location = _option.location(e);
139
+ cache.push(new TileNode(this._map, location, _option.element([e], location)));
140
+ });
141
+ _cache.set(zoom, cache);
142
+ }
143
+ }
144
+ }
145
+
146
+ private _turnoff() {
147
+ this._cache.get(this._current)?.forEach((e) => {
148
+ if (e.marker) {
149
+ e.marker.destroy();
150
+ }
151
+ });
152
+ this._cache.delete(this._current);
153
+ }
154
+
155
+ private _update() {
156
+ const { _map } = this;
157
+ const { offsetHeight, offsetWidth } = (_map as any).container;
158
+ if (offsetHeight && offsetWidth) {
159
+ const [west, south] = _map.canvasTgeography([0, offsetHeight]);
160
+ const [east, north] = _map.canvasTgeography([offsetWidth, 0]);
161
+ this._cache.get(this._current)?.forEach((e) => {
162
+ e.test(east, south, west, north);
163
+ });
164
+ }
165
+ }
166
+
167
+ onAfterViewChange(): void {
168
+ const { _map } = this;
169
+ const zoom = Math.min(Math.round(_map.zoom * 4) / 4, this._option.maxZoomLevel ?? 17);
170
+ if (zoom !== this._current) {
171
+ this._turnoff();
172
+ this._current = zoom;
173
+ this._turnon(zoom);
174
+ }
175
+ this._update();
176
+ }
177
+
178
+ /** 销毁组件 */
179
+ destroy() {
180
+ const { event } = this._map;
181
+ event.on("view-moveEnd", null);
182
+ this._map.elements.delete(this.name);
183
+ this._cache.forEach((e) => {
184
+ e.forEach((f) => {
185
+ f.marker?.destroy();
186
+ });
187
+ });
188
+ }
189
+ }