@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
@@ -0,0 +1,289 @@
1
+
2
+ import { OpenlayerMap } from "./OpenlayerMap";
3
+ import Feature from 'ol/Feature.js'
4
+ import LineString from 'ol/geom/LineString.js'
5
+ import { Icon, Stroke, Style } from 'ol/style.js'
6
+ import { fromLonLat, toLonLat } from "ol/proj";
7
+ import { Polyline } from "../basic/Polyline";
8
+ import VectorSource from "ol/source/Vector";
9
+ import VectorLayer from "ol/layer/Vector";
10
+ import iconPoint from '../assets/point.png'
11
+ import iconAdd from '../assets/add.png'
12
+ import Point from 'ol/geom/Point.js'
13
+ import { ChainNode, PolylineEditor } from "../utils/PolylineEditor";
14
+ const styleNode = new Style({
15
+ image: new Icon({
16
+ width: 16,
17
+ height: 16,
18
+ src: iconPoint,
19
+ crossOrigin: 'anonymous'
20
+ })
21
+ });
22
+
23
+ const styleVirtual = new Style({
24
+ image: new Icon({
25
+ width: 16,
26
+ height: 16,
27
+ src: iconAdd,
28
+ crossOrigin: 'anonymous'
29
+ })
30
+ });
31
+
32
+ export function drawPolyline(map: OpenlayerMap, polyline: Polyline<unknown>) {
33
+ const { event, viewer } = map
34
+
35
+ const source = new VectorSource();
36
+
37
+ const layer = new VectorLayer({
38
+ source,
39
+ visible: polyline.show
40
+ })
41
+ viewer.addLayer(layer)
42
+
43
+ let status = 0
44
+ let skip = 0
45
+
46
+ const feature = new Feature()
47
+ const geometry = new LineString([[0, 0], [0, 0]])
48
+
49
+ const stroke = new Stroke({
50
+ color: polyline.stroke,
51
+ width: polyline.lineWidth,
52
+ lineDash: polyline.dash ? [10, 10] : undefined,
53
+ })
54
+ const style = new Style({
55
+ zIndex: 50,
56
+ stroke,
57
+ })
58
+
59
+ const _event = {
60
+ cursor: polyline.cursor,
61
+ silent: polyline.silent,
62
+ onPointerIn() {
63
+ status == 2 && polyline.event.trigger('pointer-in')
64
+ },
65
+ onPointerOut() {
66
+ status == 2 && polyline.event.trigger('pointer-out')
67
+ },
68
+ onLClick() {
69
+ status == 2 && polyline.event.trigger('left-click')
70
+ },
71
+ onRClick() {
72
+ if (polyline.menu) {
73
+ map.menu.show(polyline.menu);
74
+ }
75
+ status == 2 && polyline.event.trigger('right-click')
76
+ },
77
+ onDbClick() {
78
+ polyline.editable && status == 2 && setTimeout(() => {
79
+ polyline.event.trigger('start-edit')
80
+ });
81
+ }
82
+ }
83
+
84
+ feature.setGeometry(geometry)
85
+ feature.setStyle(style)
86
+ feature.set('event', _event, true)
87
+ if (polyline.coordinates) {
88
+ status = 2
89
+ geometry.setCoordinates(polyline.coordinates.map(e => fromLonLat(e)))
90
+ layer.setZIndex(polyline.coordinates[0][2] ?? 1)
91
+ source.addFeature(feature)
92
+ }
93
+
94
+ const onUpdate = (e: Set<string>) => {
95
+ if (skip) {
96
+ skip--
97
+ return
98
+ }
99
+
100
+ e.forEach(key => {
101
+ switch (key) {
102
+ case 'show':
103
+ layer.setVisible(polyline.show)
104
+ break;
105
+ case 'editable':
106
+ break;
107
+ case 'coordinates':
108
+ if (polyline.coordinates) {
109
+ geometry.setCoordinates(polyline.coordinates.map(e => fromLonLat(e)))
110
+ layer.setZIndex(polyline.coordinates[0][2] ?? 1)
111
+ switch (status) {
112
+ case 0:
113
+ source.addFeature(feature)
114
+ status = 2;
115
+ break;
116
+ case 1:
117
+ status = 2;
118
+ break;
119
+ }
120
+ } else {
121
+ source.removeFeature(feature)
122
+ status == 3 && editor.setCoordinates([])
123
+ status = 0
124
+ }
125
+
126
+ break;
127
+
128
+ case 'lineWidth':
129
+ stroke.setWidth(polyline.lineWidth)
130
+ break;
131
+ case 'stroke':
132
+ stroke.setColor(polyline.stroke)
133
+ break;
134
+ case 'dash':
135
+ stroke.setLineDash(polyline.dash ? [10, 10] : undefined)
136
+ break;
137
+ default:
138
+ throw new Error(`${key} 还不支持修改`)
139
+ }
140
+ })
141
+
142
+ feature.changed()
143
+ }
144
+ let cache = [[0]]
145
+ let activeNode: ChainNode<Feature, Feature>
146
+ const onMouseMove = () => {
147
+ switch (status) {
148
+ case 1:
149
+ cache.pop()
150
+ cache.push(fromLonLat(event.geography))
151
+ geometry.setCoordinates(cache)
152
+ break
153
+ case 4:
154
+ activeNode.update(fromLonLat(event.geography))
155
+ geometry.setCoordinates(editor.getCoordinates())
156
+ break;
157
+ }
158
+ }
159
+
160
+ const onClick = () => {
161
+ switch (status) {
162
+ case 0:
163
+ status = 1
164
+ _event.silent = true
165
+ cache = [fromLonLat(event.geography)]
166
+ cache.push(cache[0])
167
+ geometry.setCoordinates(cache)
168
+ source.addFeature(feature)
169
+ break;
170
+ case 1:
171
+ cache.push(cache[cache.length - 1])
172
+ geometry.setCoordinates(cache)
173
+ break
174
+ }
175
+ }
176
+
177
+ const onDbClick = () => {
178
+ switch (status) {
179
+ case 1:
180
+ status = 2
181
+ skip++
182
+ cache.pop()
183
+ cache.pop()
184
+ geometry.setCoordinates(cache)
185
+ polyline.coordinates = cache.map(e => toLonLat(e))
186
+ _event.silent = polyline.silent
187
+ polyline.event.trigger('data-loaded')
188
+ polyline.event.trigger('pointer-in')
189
+ break
190
+ case 3:
191
+ polyline.event.trigger('stop-edit')
192
+ break
193
+ }
194
+ }
195
+
196
+ const editor = new PolylineEditor<Feature<Point>, Feature<Point>>()
197
+
198
+ editor.on('create-node', (n) => {
199
+ if (n.type == 'node') {
200
+ const feature = new Feature(new Point(n.position))
201
+ feature.setStyle(styleNode)
202
+ feature.set('event', {
203
+ cursor: 'move',
204
+ onPointerDown() {
205
+ status = 4
206
+ activeNode = n
207
+ map.moveable = false
208
+ },
209
+ onPointerUp() {
210
+ if (status == 4) {
211
+ status = 3
212
+ map.moveable = true
213
+ activeNode = undefined
214
+ }
215
+ },
216
+ onDbClick() {
217
+ n.destroy()
218
+ geometry.setCoordinates(editor.getCoordinates())
219
+ }
220
+ }, true)
221
+ source.addFeature(feature)
222
+ n.shape = feature
223
+ } else {
224
+ const feature = new Feature(new Point(n.position))
225
+ feature.setStyle(styleVirtual)
226
+ feature.set('event', {
227
+ cursor: 'pointer',
228
+ onPointerDown() {
229
+ status = 4
230
+ activeNode = n.insert()
231
+ map.cursor = 'move'
232
+ map.moveable = false
233
+ },
234
+ }, true)
235
+ source.addFeature(feature)
236
+ n.shape = feature
237
+ }
238
+ })
239
+
240
+ editor.on('update-node', (n) => {
241
+ n.shape.getGeometry().setCoordinates(n.position)
242
+ })
243
+ editor.on('remove-node', (n) => {
244
+ source.removeFeature(n.shape)
245
+ })
246
+ editor.on('empty', () => {
247
+ source.removeFeature(feature)
248
+ status = 0
249
+ })
250
+ const onStartEdit = () => {
251
+ switch (status) {
252
+ case 2:
253
+ status = 3
254
+ editor.setCoordinates(geometry.getCoordinates())
255
+ break
256
+ }
257
+
258
+ }
259
+
260
+ const onStopEdit = () => {
261
+ if (status == 3) {
262
+ status = 2
263
+ skip++
264
+ polyline.coordinates = editor.getCoordinates().map(e => toLonLat(e))
265
+ editor.setCoordinates([])
266
+ }
267
+
268
+ }
269
+
270
+ const onDestroy = () => {
271
+ viewer.removeLayer(layer)
272
+ event.off('pointer-move', onMouseMove)
273
+ event.off('left-click', onClick)
274
+ event.off('double-click', onDbClick)
275
+ polyline.event.off('update', onUpdate)
276
+ polyline.event.off('start-edit', onStartEdit)
277
+ polyline.event.off('stop-edit', onStopEdit)
278
+ polyline.event.off('destroy', onDestroy)
279
+
280
+ }
281
+
282
+ event.on('pointer-move', onMouseMove)
283
+ event.on('left-click', onClick)
284
+ event.on('double-click', onDbClick)
285
+ polyline.event.on('update', onUpdate)
286
+ polyline.event.on('start-edit', onStartEdit)
287
+ polyline.event.on('stop-edit', onStopEdit)
288
+ polyline.event.on('destroy', onDestroy)
289
+ }
@@ -0,0 +1,46 @@
1
+
2
+
3
+ /**
4
+ * 加载图片
5
+ * @param src 路径
6
+ * @returns
7
+ */
8
+ export function loadImage(src: string): Promise<HTMLImageElement | undefined> {
9
+ return new Promise((resolve) => {
10
+ const image = new Image();
11
+ image.crossOrigin = "anonymous";
12
+ image.src = src;
13
+ image.onload = () => {
14
+ resolve(image);
15
+ };
16
+ image.onerror = () => {
17
+ resolve(undefined);
18
+ };
19
+ });
20
+ }
21
+
22
+
23
+
24
+ export function coverSolidColor(img: HTMLImageElement | HTMLCanvasElement, color: number) {
25
+ const canvas = document.createElement('canvas')
26
+
27
+ canvas.width = img.width
28
+ canvas.height = img.height
29
+ const ctx = canvas.getContext('2d')
30
+ ctx.drawImage(img, 0, 0)
31
+ const imgaeData = ctx.getImageData(0, 0, canvas.width, canvas.height)
32
+ const r = (color >> 16) % 256
33
+ const g = (color & 0xff00) >> 8
34
+ const b = color & 0xff
35
+ for (let i = 0; i < imgaeData.data.length; i += 4) {
36
+ if (imgaeData.data[i + 3] > 0) {
37
+ imgaeData.data[i] = r
38
+ imgaeData.data[i + 1] = g
39
+ imgaeData.data[i + 2] = b
40
+ imgaeData.data[i + 3] = 255
41
+ }
42
+ }
43
+ ctx.putImageData(imgaeData, 0, 0)
44
+
45
+ return canvas
46
+ }
@@ -0,0 +1,159 @@
1
+ import Eventful from "zrender/lib/core/Eventful"
2
+
3
+
4
+ export class PolygonEditor<M, N> extends Eventful<{
5
+ 'create-node': (n: ChainNode<M, N> | VirtualNode<M, N>) => void
6
+ 'update-node': (n: ChainNode<M, N> | VirtualNode<M, N>) => void
7
+ 'remove-node': (n: ChainNode<M, N> | VirtualNode<M, N>) => void
8
+ "empty": () => void
9
+ }>{
10
+ first: ChainNode<M, N>
11
+ setCoordinates(ps: number[][]) {
12
+
13
+
14
+ let anchor = this.first
15
+ if (this.first) {
16
+ this.first.previous.next = undefined
17
+ while (anchor) {
18
+ this.trigger('remove-node', anchor)
19
+ anchor.virtual && this.trigger('remove-node', anchor.virtual)
20
+ anchor = anchor.next
21
+ }
22
+ }
23
+
24
+ ps.forEach(e => {
25
+ if (anchor) {
26
+ anchor = anchor.createNext(e)
27
+
28
+ } else {
29
+ this.first = new ChainNode(this, e)
30
+ anchor = this.first
31
+ }
32
+ })
33
+ if (anchor) {
34
+ this.first.previous = anchor
35
+ anchor.next = this.first
36
+ anchor.virtual = new VirtualNode(anchor)
37
+ } else {
38
+ this.first = undefined
39
+ }
40
+
41
+ }
42
+ getCoordinates() {
43
+ if (this.first) {
44
+ this.first.previous.next = undefined
45
+ const res: number[][] = []
46
+ let anchor = this.first
47
+ while (anchor) {
48
+ res.push(anchor.position)
49
+ anchor = anchor.next
50
+ }
51
+ this.first.previous.next = this.first
52
+ return res
53
+ } else {
54
+ return []
55
+ }
56
+
57
+ }
58
+ }
59
+
60
+
61
+
62
+
63
+
64
+ export class ChainNode<M, N>{
65
+ type = 'node' as const
66
+ position: number[]
67
+ editor: PolygonEditor<M, N>
68
+ shape: M
69
+ previous: ChainNode<M, N>
70
+ next: ChainNode<M, N>
71
+ virtual: VirtualNode<M, N>
72
+ constructor(editor: PolygonEditor<M, N>, position: number[]) {
73
+ this.editor = editor
74
+ this.position = position
75
+ editor.trigger('create-node', this)
76
+
77
+ }
78
+
79
+ update(p: number[]) {
80
+ this.position = p
81
+ this.editor.trigger('update-node', this)
82
+ this.previous?.virtual.update()
83
+ this.virtual?.update()
84
+ }
85
+
86
+ createNext(p: number[]) {
87
+ this.next = new ChainNode(this.editor, p)
88
+ this.next.previous = this
89
+ this.virtual = new VirtualNode(this)
90
+ return this.next
91
+ }
92
+
93
+
94
+
95
+ destroy() {
96
+ const { editor, virtual, previous, next } = this
97
+ if (editor.first.next.next.next == editor.first) {
98
+
99
+ editor.setCoordinates([])
100
+ editor.trigger('empty')
101
+ } else {
102
+ if (editor.first == this) {
103
+ editor.first = this.next
104
+ }
105
+ previous.next = next
106
+ next.previous = previous
107
+ previous.virtual.update()
108
+ editor.trigger('remove-node', this)
109
+ editor.trigger('remove-node', virtual)
110
+ }
111
+
112
+
113
+
114
+ }
115
+ }
116
+
117
+
118
+ export class VirtualNode<M, N>{
119
+ type = 'virtual' as const
120
+ shape: N
121
+ previous: ChainNode<M, N>
122
+
123
+ get next() {
124
+ return this.previous.next
125
+ }
126
+ get editor() {
127
+ return this.previous.editor
128
+ }
129
+ get position() {
130
+ const { previous, next } = this
131
+ return previous.position.map((e, i) => (e + next.position[i]) / 2)
132
+ }
133
+ constructor(n: ChainNode<M, N>) {
134
+ this.previous = n
135
+ this.editor.trigger('create-node', this)
136
+ }
137
+ update() {
138
+ this.editor.trigger('update-node', this)
139
+ }
140
+
141
+ insert() {
142
+ const { previous, next } = this
143
+ const p = this.position
144
+ this.previous = previous.createNext(p)
145
+ this.previous.virtual = this
146
+ this.previous.next = next
147
+ next.previous = this.previous
148
+ this.update()
149
+ return this.previous
150
+ }
151
+
152
+ destroy() {
153
+ this.previous.virtual = undefined
154
+ this.editor.trigger('remove-node', this)
155
+ }
156
+
157
+
158
+ }
159
+
@@ -0,0 +1,149 @@
1
+ import Eventful from "zrender/lib/core/Eventful"
2
+
3
+
4
+ export class PolylineEditor<M, N> extends Eventful<{
5
+ 'create-node': (n: ChainNode<M, N> | VirtualNode<M, N>) => void
6
+ 'update-node': (n: ChainNode<M, N> | VirtualNode<M, N>) => void
7
+ 'remove-node': (n: ChainNode<M, N> | VirtualNode<M, N>) => void
8
+ "empty": () => void
9
+ }>{
10
+ first: ChainNode<M, N>
11
+ setCoordinates(ps: number[][]) {
12
+
13
+ let anchor = this.first
14
+
15
+ while (anchor) {
16
+ this.trigger('remove-node', anchor)
17
+ anchor.virtual && this.trigger('remove-node', anchor.virtual)
18
+ anchor = anchor.next
19
+ }
20
+ ps.forEach(e => {
21
+ if (anchor) {
22
+ anchor = anchor.createNext(e)
23
+
24
+ } else {
25
+ this.first = new ChainNode(this, e)
26
+ anchor = this.first
27
+ }
28
+ })
29
+ }
30
+ getCoordinates() {
31
+ const res: number[][] = []
32
+ let anchor = this.first
33
+ while (anchor) {
34
+ res.push(anchor.position)
35
+ anchor = anchor.next
36
+ }
37
+ return res
38
+ }
39
+ }
40
+
41
+
42
+
43
+
44
+
45
+ export class ChainNode<M, N>{
46
+ type = 'node' as const
47
+ position: number[]
48
+ editor: PolylineEditor<M, N>
49
+ shape: M
50
+ previous: ChainNode<M, N>
51
+ next: ChainNode<M, N>
52
+ virtual: VirtualNode<M, N>
53
+ constructor(editor: PolylineEditor<M, N>, position: number[]) {
54
+ this.editor = editor
55
+ this.position = position
56
+ editor.trigger('create-node', this)
57
+ }
58
+
59
+ update(p: number[]) {
60
+ this.position = p
61
+ this.editor.trigger('update-node', this)
62
+ this.previous?.virtual.update()
63
+ this.virtual?.update()
64
+ }
65
+
66
+ createNext(p: number[]) {
67
+ this.next = new ChainNode(this.editor, p)
68
+ this.next.previous = this
69
+ this.virtual = new VirtualNode(this)
70
+ return this.next
71
+ }
72
+
73
+
74
+
75
+ destroy() {
76
+ const { editor, virtual, previous, next } = this
77
+ if (previous && next) {
78
+ previous.next = next
79
+ next.previous = previous
80
+ previous.virtual.update()
81
+
82
+ editor.trigger('remove-node', virtual)
83
+ } else if (previous) {
84
+ previous.next = undefined
85
+
86
+ previous.virtual.destroy()
87
+ } else if (next) {
88
+ editor.first = next
89
+ next.previous = undefined
90
+
91
+ editor.trigger('remove-node', virtual)
92
+ } else {
93
+ editor.first = undefined
94
+ editor.trigger('empty')
95
+ }
96
+
97
+
98
+ if (editor.first && !editor.first.next) {
99
+ editor.first.destroy()
100
+ }
101
+
102
+ editor.trigger('remove-node', this)
103
+
104
+ }
105
+ }
106
+
107
+
108
+ export class VirtualNode<M, N>{
109
+ type = 'virtual' as const
110
+ shape: N
111
+ previous: ChainNode<M, N>
112
+
113
+ get next() {
114
+ return this.previous.next
115
+ }
116
+ get editor() {
117
+ return this.previous.editor
118
+ }
119
+ get position() {
120
+ const { previous, next } = this
121
+ return previous.position.map((e, i) => (e + next.position[i]) / 2)
122
+ }
123
+ constructor(n: ChainNode<M, N>) {
124
+ this.previous = n
125
+ this.editor.trigger('create-node', this)
126
+ }
127
+ update() {
128
+ this.editor.trigger('update-node', this)
129
+ }
130
+
131
+ insert() {
132
+ const { previous, next } = this
133
+ const p = this.position
134
+ this.previous = previous.createNext(p)
135
+ this.previous.virtual = this
136
+ this.previous.next = next
137
+ next.previous = this.previous
138
+ this.update()
139
+ return this.previous
140
+ }
141
+
142
+ destroy() {
143
+ this.previous.virtual = undefined
144
+ this.editor.trigger('remove-node', this)
145
+ }
146
+
147
+
148
+ }
149
+
@@ -0,0 +1,46 @@
1
+ const R = 6378137;
2
+ // const MAX_LATITUDE = 85.0511287798
3
+ const D = Math.PI / 180;
4
+ const RD = R * D;
5
+ const r = R / 2;
6
+ export const Projection = {
7
+ /** 经度转墨卡托横坐标 */
8
+ lonToX(lon: number) {
9
+ return RD * lon;
10
+ },
11
+ /** 纬度转墨卡托纵坐标 */
12
+ latToY(lat: number) {
13
+ const sin = Math.sin(lat * D);
14
+ return r * Math.log((1 + sin) / (1 - sin));
15
+ },
16
+ /** 墨卡托横坐标转经度 */
17
+ xToLon(x: number) {
18
+ return x / RD;
19
+ },
20
+ /** 墨卡托纵坐标转纬度 */
21
+ yToLat(y: number) {
22
+ return (2 * Math.atan(Math.exp(y / R)) - Math.PI / 2) / D;
23
+ },
24
+ };
25
+
26
+ // export const Projection = {
27
+ // /** 经度转横坐标 */
28
+ // lonToX (lon: number) {
29
+ // return lon
30
+ // },
31
+ // /** 纬度转纵坐标 */
32
+ // latToY (lat: number) {
33
+
34
+ // return lat
35
+ // },
36
+ // /** 横坐标转经度 */
37
+ // xToLon (x: number) {
38
+ // return x
39
+ // },
40
+ // /** 纵坐标转纬度 */
41
+ // yToLat (y: number) {
42
+ // return y
43
+ // }
44
+ // };
45
+
46
+ (window as any).Projection = Projection;