@hzab/map-combine 0.4.2-alpha.3 → 0.4.2-alpha.5

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,7 @@ fix: openlayer getFromLonLat this 指向修复
5
5
  break: PointAggregation datas 改为数组对象
6
6
  fix: cesium flyTo height 计算
7
7
  feat: map.bussinesses onFrame 事件回调
8
+ fix: AmapPoint drawPoint 逻辑修复
8
9
 
9
10
  # @hzab/map-combine@0.4.1
10
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzab/map-combine",
3
- "version": "0.4.2-alpha.3",
3
+ "version": "0.4.2-alpha.5",
4
4
  "description": "地图组件",
5
5
  "main": "src",
6
6
  "scripts": {
@@ -29,7 +29,7 @@
29
29
  "@amap/amap-jsapi-loader": "^1.0.1",
30
30
  "@hzab/permissions": "0.1.1",
31
31
  "@hzab/webpack-config": "^0.2.1",
32
- "@turf/turf": "6.5",
32
+ "@turf/turf": "6.5.0",
33
33
  "@types/amap-js-api": "^1.4.16",
34
34
  "@types/react": "^17.0.62",
35
35
  "@types/react-dom": "^17.0.20",
@@ -51,8 +51,9 @@
51
51
  "lib": "lib"
52
52
  },
53
53
  "peerDependencies": {
54
- "@turf/turf": "6.5",
54
+ "@turf/turf": "6.5.0",
55
55
  "coordtransform": "^2.1.2",
56
+ "nanoid": "^3.3.7",
56
57
  "lodash": ">=4.17.21",
57
58
  "react-dom": "^17.0.2",
58
59
  "zrender": "^5.4.4"
package/src/amap/AMap.ts CHANGED
@@ -1,306 +1,313 @@
1
- import { MapCombine } from "../basic/MapCombine";
2
- import { PointOption, Point } from "../basic/Point";
3
- import { PointAggregationOption, PointAggregation } from "../basic/PointAggregation";
4
- import { ReactPointOption, ReactPoint, drawReactPoint } from "../basic/ReactPoint";
5
- import { PolylineOption, Polyline } from "../basic/Polyline";
6
- import { PolygonOption, Polygon } from "../basic/Polygon";
7
- import { Tile3DOption, Tile3D } from "../basic/Tile3D";
8
-
9
- import AMapLoader from "./loader";
10
- import { bindEvent } from "./AMapEvent";
11
- import { drawPoint } from "./AMapPoint";
12
- import { drawPolyline } from "./AMapPolyline";
13
- import { drawPolygon } from "./AMapPolygon";
14
-
15
- export interface IAMapOption {
16
- container: HTMLDivElement;
17
- url?: string | string[];
18
- center?: number[];
19
- zoom?: number;
20
- maxZoom?: number;
21
- minZoom?: number;
22
- /** 地图瓦片转换规则 GCJ02 转 WGS84 */
23
- proj?: "GCJ02->WGS84";
24
- /** 高德地图 key */
25
- key: string;
26
- /** 高德地图 securityJsCode */
27
- securityJsCode: string;
28
- /** 加载的插件列表 */
29
- plugins?: [string];
30
- /** loader 配置参数 */
31
- loaderOpt?: Object;
32
- /** 地图初始化 配置参数 */
33
- mapOpt?: Object;
34
- }
35
-
36
- export interface IFlyToViewerOpt {
37
- /**
38
- * 立即缩放到指定位置
39
- */
40
- immediately?: boolean;
41
- /**
42
- * (Array<number> = [0,0,0,0]) 距离边框的内边距,顺序:上、下、左、右
43
- */
44
- avoid?: [number, number, number, number];
45
- /**
46
- * 最大缩放级别
47
- */
48
- maxZoom?: number;
49
- /**
50
- * 动画时间
51
- */
52
- duration?: number;
53
- }
54
-
55
- export class AMap extends MapCombine {
56
- viewer: any;
57
- container: HTMLDivElement = null;
58
- option: any = {};
59
- offset: number[];
60
- fov = 0.9272952180016121;
61
- isFromLonLat = true;
62
- LbsAMap: any;
63
- get cursor(): string {
64
- return this.container.style.cursor;
65
- }
66
- set cursor(val: string) {
67
- this.container.style.cursor = val;
68
- }
69
-
70
- get moveable(): boolean {
71
- return this.viewer?.getStatus()?.dragEnable;
72
- }
73
- set moveable(val: boolean) {
74
- this.viewer?.setStatus({ dragEnable: val });
75
- }
76
-
77
- get center(): number[] {
78
- return this.viewer?.getCenter();
79
- }
80
- set center(val: number[]) {
81
- this.viewer?.setCenter(val);
82
- }
83
- get zoom(): number {
84
- return this.viewer?.getZoom();
85
- }
86
- set zoom(val: number) {
87
- this.viewer?.setZoom(val);
88
- }
89
-
90
- constructor(viewer = undefined) {
91
- super();
92
- viewer && this._initParamsEvent(viewer);
93
- }
94
-
95
- private _initParamsEvent(viewer) {
96
- if (!viewer) {
97
- return;
98
- }
99
- this.viewer = viewer;
100
-
101
- this.container = this.viewer.getContainer();
102
- // this.markers = this.viewer.scene.primitives.add(new Cesium.BillboardCollection());
103
-
104
- bindEvent(this);
105
- }
106
-
107
- init(opt: IAMapOption) {
108
- this.option = opt;
109
- const {
110
- container,
111
- center = [120.2288892, 30.2349677, 0],
112
- zoom = 13,
113
- url,
114
- maxZoom,
115
- minZoom,
116
- proj,
117
- key,
118
- securityJsCode,
119
- plugins,
120
- } = opt || {};
121
-
122
- this.loadPromise = new Promise((resolve, reject) => {
123
- AMapLoader({
124
- ...opt.loaderOpt,
125
- key,
126
- securityJsCode,
127
- plugins,
128
- })
129
- .then((LbsAMap: any) => {
130
- this.LbsAMap = LbsAMap;
131
- let _urls = Array.isArray(url) ? url : [url];
132
- this.viewer = new LbsAMap.Map(container, {
133
- ...opt.mapOpt,
134
- zoom: zoom || 14,
135
- center: center || [120.160217, 30.243861],
136
- doubleClickZoom: false,
137
- zooms: minZoom && maxZoom && [minZoom, maxZoom],
138
- layers: url
139
- ? _urls.map((it: any) => {
140
- let opt = { tileUrl: "", getTileUrl: undefined };
141
- if (typeof it === "string") {
142
- opt.tileUrl = it;
143
- } else {
144
- opt = it;
145
- }
146
- // 使用 getTileUrl 解决接口地址强制改为 https 的问题
147
- opt.getTileUrl = function (x, y, z) {
148
- return opt.tileUrl.replace("{x}", x).replace("{y}", y).replace("{z}", z);
149
- };
150
-
151
- return new LbsAMap.TileLayer(opt);
152
- })
153
- : undefined,
154
- });
155
- this.viewer.on("complete", () => {
156
- resolve(this);
157
- });
158
-
159
- this._initParamsEvent(this.viewer);
160
- return this;
161
- })
162
- .catch(reject);
163
- });
164
- this.loadPromise.then(() => {
165
- this.container.appendChild(this.htmllayer);
166
- });
167
- return this.loadPromise;
168
- }
169
-
170
- createPoint<K>(option?: Partial<PointOption<K>>): Point<K> {
171
- const e = new Point(this, option);
172
- this.loadPromise.then(() => {
173
- drawPoint(this, e);
174
- });
175
- return e;
176
- }
177
-
178
- createPointAggregation(option?: PointAggregationOption): PointAggregation {
179
- const e = new PointAggregation(this, option);
180
- return e;
181
- }
182
-
183
- createReactPoint<K>(option?: Partial<ReactPointOption<K>>): ReactPoint<K> {
184
- const e = new ReactPoint(this, option);
185
- this.loadPromise.then(() => {
186
- drawReactPoint(this, e);
187
- });
188
- return e;
189
- }
190
-
191
- createPolyline<K>(option?: Partial<PolylineOption<K>>): Polyline<K> {
192
- const e = new Polyline(this, option);
193
- this.loadPromise.then(() => {
194
- drawPolyline(this, e);
195
- });
196
- return e;
197
- }
198
- createPolygon<K>(option?: Partial<PolygonOption<K>>): Polygon<K> {
199
- const e = new Polygon(this, option);
200
- this.loadPromise.then(() => {
201
- drawPolygon(this, e);
202
- });
203
- return e;
204
- }
205
- createTile3D(option?: Partial<Tile3DOption>): Tile3D {
206
- throw new Error("Method not implemented.");
207
- }
208
-
209
- /**
210
- * 将高德地图 Canvas 像素坐标转换为经纬度
211
- * @param {Array} p - canvas 点位数组 [x, y]
212
- * @returns {Array} p - 经纬度数组 [lng, lat]
213
- */
214
- canvasTgeography(p: number[]) {
215
- const mapInstance = this.viewer;
216
- if (!mapInstance) {
217
- this.loadPromise.then(() => {
218
- this.canvasTgeography(p);
219
- });
220
- return [];
221
- }
222
- try {
223
- const lngLat = this.viewer.lngLatToContainer(p);
224
- return [lngLat.x, lngLat.y];
225
- } catch (error) {
226
- console.error("canvasPixelToLngLat 转换出错:", error);
227
- return [];
228
- }
229
- }
230
-
231
- /**
232
- * 经纬度转换为高德地图 Canvas 像素坐标
233
- * @param {Array} p - 经纬度数组 [lng, lat]
234
- * @returns {Array} Canvas 像素坐标 [x, y]
235
- */
236
- geographyTcanvas(p: number[]): number[] {
237
- const mapInstance = this.viewer;
238
- if (!mapInstance) {
239
- this.loadPromise.then(() => {
240
- this.geographyTcanvas(p);
241
- });
242
- return [];
243
- }
244
- try {
245
- const containerPixel = this.viewer.lngLatToContainer(p);
246
- return [containerPixel.x, containerPixel.y];
247
- } catch (error) {
248
- console.error("lngLatToCanvasPixel 转换出错:", error);
249
- return [];
250
- }
251
- }
252
- /**
253
- * 所有点位移动至视口,调整位置及层级
254
- * @param points
255
- * @param opt
256
- * @param {Array<number> = [0,0,0,0]} opt.avoid 距离边框的内边距,顺序:上、下、左、右
257
- */
258
- flyToViewer(points: number[][], opt: IFlyToViewerOpt = { avoid: [0, 0, 0, 0], immediately: true }): void {
259
- this.loadPromise.then(() => {
260
- const [zoom, center] = this.viewer.getFitZoomAndCenterByBounds(
261
- this.getBounds(points, { canvas: this.viewer.canvas, ...opt }),
262
- opt?.avoid,
263
- opt.maxZoom,
264
- );
265
-
266
- this.viewer.setZoomAndCenter(zoom, center, opt?.immediately, opt?.duration);
267
- });
268
- }
269
-
270
- /**
271
- * 将点集视野调整至容器内,支持相对边距(百分比)
272
- * @param {number[][]} points - 经纬度数组 [[lng, lat], ...]
273
- * @param {Object} opt
274
- * @param {number[]} opt.avoid - 相对边距 [上, 下, 左, 右],取值范围 0~1,默认 [0,0,0,0]
275
- * @param {boolean} opt.immediately - 是否立即跳转,默认 false(带动画)
276
- */
277
- flyToViewerPercent(points: number[][], opt = { avoid: [0, 0, 0, 0], immediately: false }) {
278
- if (!points?.length) return;
279
-
280
- // 计算原始经纬度范围
281
- let west = Infinity,
282
- east = -Infinity,
283
- south = Infinity,
284
- north = -Infinity;
285
- points.forEach(([lng, lat]) => {
286
- west = Math.min(west, lng);
287
- east = Math.max(east, lng);
288
- south = Math.min(south, lat);
289
- north = Math.max(north, lat);
290
- });
291
-
292
- // 应用相对边距
293
- const [top, bottom, left, right] = opt.avoid;
294
- const lngSpan = east - west;
295
- const latSpan = north - south;
296
-
297
- const bounds = this.LbsAMap.Bounds(
298
- [west - lngSpan * left, south - latSpan * bottom],
299
- [east + lngSpan * right, north + latSpan * top],
300
- );
301
-
302
- this.viewer.setBounds(bounds, opt.immediately);
303
- }
304
-
305
- flyTo() {}
306
- }
1
+ import { MapCombine } from "../basic/MapCombine";
2
+ import { PointOption, Point } from "../basic/Point";
3
+ import { PointAggregationOption, PointAggregation } from "../basic/PointAggregation";
4
+ import { ReactPointOption, ReactPoint, drawReactPoint } from "../basic/ReactPoint";
5
+ import { PolylineOption, Polyline } from "../basic/Polyline";
6
+ import { PolygonOption, Polygon } from "../basic/Polygon";
7
+ import { Tile3DOption, Tile3D } from "../basic/Tile3D";
8
+
9
+ import AMapLoader from "./loader";
10
+ import { bindEvent } from "./AMapEvent";
11
+ import { drawPoint } from "./AMapPoint";
12
+ import { drawPolyline } from "./AMapPolyline";
13
+ import { drawPolygon } from "./AMapPolygon";
14
+
15
+ export interface IAMapOption {
16
+ container: HTMLDivElement;
17
+ url?: string | string[];
18
+ center?: number[];
19
+ zoom?: number;
20
+ maxZoom?: number;
21
+ minZoom?: number;
22
+ /** 地图瓦片转换规则 GCJ02 转 WGS84 */
23
+ proj?: "GCJ02->WGS84";
24
+ /** 高德地图 key */
25
+ key: string;
26
+ /** 高德地图 securityJsCode */
27
+ securityJsCode: string;
28
+ /** 加载的插件列表 */
29
+ plugins?: [string];
30
+ /** loader 配置参数 */
31
+ loaderOpt?: Object;
32
+ /** 地图初始化 配置参数 */
33
+ mapOpt?: Object;
34
+ }
35
+
36
+ export interface IFlyToViewerOpt {
37
+ /**
38
+ * 立即缩放到指定位置
39
+ */
40
+ immediately?: boolean;
41
+ /**
42
+ * (Array<number> = [0,0,0,0]) 距离边框的内边距,顺序:上、下、左、右
43
+ */
44
+ avoid?: [number, number, number, number];
45
+ /**
46
+ * 最大缩放级别
47
+ */
48
+ maxZoom?: number;
49
+ /**
50
+ * 动画时间
51
+ */
52
+ duration?: number;
53
+ }
54
+
55
+ export class AMap extends MapCombine {
56
+ viewer: any;
57
+ container: HTMLDivElement = null;
58
+ option: any = {};
59
+ offset: number[];
60
+ fov = 0.9272952180016121;
61
+ isFromLonLat = true;
62
+ LbsAMap: any;
63
+ get cursor(): string {
64
+ return this.container.style.cursor;
65
+ }
66
+ set cursor(val: string) {
67
+ this.container.style.cursor = val;
68
+ }
69
+
70
+ get moveable(): boolean {
71
+ return this.viewer?.getStatus()?.dragEnable;
72
+ }
73
+ set moveable(val: boolean) {
74
+ this.viewer?.setStatus({ dragEnable: val });
75
+ }
76
+
77
+ _center;
78
+
79
+ get center(): number[] {
80
+ return this.viewer?.getCenter();
81
+ }
82
+ set center(val: number[]) {
83
+ this._center = val;
84
+ this.viewer?.setCenter(val);
85
+ }
86
+ get zoom(): number {
87
+ return this.viewer?.getZoom();
88
+ }
89
+ set zoom(val: number) {
90
+ this.viewer?.setZoom(val);
91
+ }
92
+
93
+ constructor(viewer = undefined) {
94
+ super();
95
+ viewer && this._initParamsEvent(viewer);
96
+ }
97
+
98
+ private _initParamsEvent(viewer) {
99
+ if (!viewer) {
100
+ return;
101
+ }
102
+ this.viewer = viewer;
103
+
104
+ this.container = this.viewer.getContainer();
105
+ // this.markers = this.viewer.scene.primitives.add(new Cesium.BillboardCollection());
106
+
107
+ bindEvent(this);
108
+ }
109
+
110
+ init(opt: IAMapOption) {
111
+ this.option = opt;
112
+ const {
113
+ container,
114
+ center = [120.2288892, 30.2349677, 0],
115
+ zoom = 13,
116
+ url,
117
+ maxZoom,
118
+ minZoom,
119
+ proj,
120
+ key,
121
+ securityJsCode,
122
+ plugins,
123
+ } = opt || {};
124
+
125
+ this.loadPromise = new Promise((resolve, reject) => {
126
+ AMapLoader({
127
+ ...opt.loaderOpt,
128
+ key,
129
+ securityJsCode,
130
+ plugins,
131
+ })
132
+ .then((LbsAMap: any) => {
133
+ this.LbsAMap = LbsAMap;
134
+ let _urls = Array.isArray(url) ? url : [url];
135
+ this.viewer = new LbsAMap.Map(container, {
136
+ ...opt.mapOpt,
137
+ zoom: zoom || 14,
138
+ // _center 解决异步初始化 导致设置 center 无效的问题
139
+ center: this._center || center || [120.160217, 30.243861],
140
+ doubleClickZoom: false,
141
+ zooms: minZoom && maxZoom && [minZoom, maxZoom],
142
+ layers: url
143
+ ? _urls.map((it: any) => {
144
+ let opt = { tileUrl: "", getTileUrl: undefined };
145
+ if (typeof it === "string") {
146
+ opt.tileUrl = it;
147
+ } else {
148
+ opt = it;
149
+ }
150
+ // 使用 getTileUrl 解决接口地址强制改为 https 的问题
151
+ opt.getTileUrl = function (x, y, z) {
152
+ return opt.tileUrl.replace("{x}", x).replace("{y}", y).replace("{z}", z);
153
+ };
154
+
155
+ return new LbsAMap.TileLayer(opt);
156
+ })
157
+ : undefined,
158
+ });
159
+ this.viewer.on("complete", () => {
160
+ resolve(this);
161
+ });
162
+
163
+ this._initParamsEvent(this.viewer);
164
+ return this;
165
+ })
166
+ .catch(reject);
167
+ });
168
+ this.loadPromise.then(() => {
169
+ this.container.appendChild(this.htmllayer);
170
+ });
171
+ return this.loadPromise;
172
+ }
173
+
174
+ createPoint<K>(option?: Partial<PointOption<K>>): Point<K> {
175
+ const e = new Point(this, option);
176
+ this.loadPromise.then(() => {
177
+ drawPoint(this, e);
178
+ });
179
+ return e;
180
+ }
181
+
182
+ createPointAggregation(option?: PointAggregationOption): PointAggregation {
183
+ const e = new PointAggregation(this, option);
184
+ return e;
185
+ }
186
+
187
+ createReactPoint<K>(option?: Partial<ReactPointOption<K>>): ReactPoint<K> {
188
+ const e = new ReactPoint(this, option);
189
+ this.loadPromise.then(() => {
190
+ drawReactPoint(this, e);
191
+ });
192
+ return e;
193
+ }
194
+
195
+ createPolyline<K>(option?: Partial<PolylineOption<K>>): Polyline<K> {
196
+ const e = new Polyline(this, option);
197
+ this.loadPromise.then(() => {
198
+ drawPolyline(this, e);
199
+ });
200
+ return e;
201
+ }
202
+ createPolygon<K>(option?: Partial<PolygonOption<K>>): Polygon<K> {
203
+ const e = new Polygon(this, option);
204
+ this.loadPromise.then(() => {
205
+ drawPolygon(this, e);
206
+ });
207
+ return e;
208
+ }
209
+ createTile3D(option?: Partial<Tile3DOption>): Tile3D {
210
+ throw new Error("Method not implemented.");
211
+ }
212
+
213
+ /**
214
+ * 将高德地图 Canvas 像素坐标转换为经纬度
215
+ * @param {Array} p - canvas 点位数组 [x, y]
216
+ * @returns {Array} p - 经纬度数组 [lng, lat]
217
+ */
218
+ canvasTgeography(p: number[]) {
219
+ const mapInstance = this.viewer;
220
+ if (!mapInstance) {
221
+ this.loadPromise.then(() => {
222
+ this.canvasTgeography(p);
223
+ });
224
+ return [];
225
+ }
226
+ try {
227
+ const lngLat = this.viewer.lngLatToContainer(p);
228
+ return [lngLat.x, lngLat.y];
229
+ } catch (error) {
230
+ console.error("canvasPixelToLngLat 转换出错:", error);
231
+ return [];
232
+ }
233
+ }
234
+
235
+ /**
236
+ * 经纬度转换为高德地图 Canvas 像素坐标
237
+ * @param {Array} p - 经纬度数组 [lng, lat]
238
+ * @returns {Array} Canvas 像素坐标 [x, y]
239
+ */
240
+ geographyTcanvas(p: number[]): number[] {
241
+ const mapInstance = this.viewer;
242
+ if (!mapInstance) {
243
+ this.loadPromise.then(() => {
244
+ this.geographyTcanvas(p);
245
+ });
246
+ return [];
247
+ }
248
+ try {
249
+ const containerPixel = this.viewer.lngLatToContainer(p);
250
+ return [containerPixel.x, containerPixel.y];
251
+ } catch (error) {
252
+ console.error("lngLatToCanvasPixel 转换出错:", error);
253
+ return [];
254
+ }
255
+ }
256
+ /**
257
+ * 所有点位移动至视口,调整位置及层级
258
+ * @param points
259
+ * @param opt
260
+ * @param {Array<number> = [0,0,0,0]} opt.avoid 距离边框的内边距,顺序:上、下、左、右
261
+ */
262
+ flyToViewer(points: number[][], opt: IFlyToViewerOpt = { avoid: [0, 0, 0, 0], immediately: true }): void {
263
+ this.loadPromise.then(() => {
264
+ const [zoom, center] = this.viewer.getFitZoomAndCenterByBounds(
265
+ this.getBounds(points, { canvas: this.viewer.canvas, ...opt }),
266
+ opt?.avoid,
267
+ opt.maxZoom,
268
+ );
269
+
270
+ this.viewer.setZoomAndCenter(zoom, center, opt?.immediately, opt?.duration);
271
+ });
272
+ }
273
+
274
+ /**
275
+ * 将点集视野调整至容器内,支持相对边距(百分比)
276
+ * @param {number[][]} points - 经纬度数组 [[lng, lat], ...]
277
+ * @param {Object} opt
278
+ * @param {number[]} opt.avoid - 相对边距 [上, 下, 左, 右],取值范围 0~1,默认 [0,0,0,0]
279
+ * @param {boolean} opt.immediately - 是否立即跳转,默认 false(带动画)
280
+ */
281
+ flyToViewerPercent(points: number[][], opt = { avoid: [0, 0, 0, 0], immediately: false }) {
282
+ if (!points?.length) return;
283
+
284
+ // 计算原始经纬度范围
285
+ let west = Infinity,
286
+ east = -Infinity,
287
+ south = Infinity,
288
+ north = -Infinity;
289
+ points.forEach(([lng, lat]) => {
290
+ west = Math.min(west, lng);
291
+ east = Math.max(east, lng);
292
+ south = Math.min(south, lat);
293
+ north = Math.max(north, lat);
294
+ });
295
+
296
+ // 应用相对边距
297
+ const [top, bottom, left, right] = opt.avoid;
298
+ const lngSpan = east - west;
299
+ const latSpan = north - south;
300
+
301
+ const bounds = this.LbsAMap.Bounds(
302
+ [west - lngSpan * left, south - latSpan * bottom],
303
+ [east + lngSpan * right, north + latSpan * top],
304
+ );
305
+
306
+ this.viewer.setBounds(bounds, opt.immediately);
307
+ }
308
+
309
+ flyTo(point: number[], opt?: { immediately: false; duration: undefined }) {
310
+ const { immediately, duration } = opt || {};
311
+ this.viewer.setCenter(point, immediately, duration);
312
+ }
313
+ }
@@ -1,89 +1,76 @@
1
1
  import { Point } from "../basic/Point";
2
2
  import { AMap } from "./AMap";
3
3
 
4
- const RD = 0.017453292519943295;
5
-
6
4
  export function drawPoint(map: AMap, point: Point<unknown>) {
7
5
  const { viewer, event, LbsAMap } = map;
8
6
  let status = 0;
9
- let marker = null;
10
- const _event = {
11
- onPointerIn() {
12
- point.event.trigger("pointer-in");
13
- },
14
- onPointerOut() {
15
- point.event.trigger("pointer-out");
16
- },
17
- onLClick() {
18
- point.event.trigger("left-click");
19
- },
20
- onRClick(e) {
21
- event.canvas = [e.pixel.x, e.pixel.y];
22
- point.event.trigger("right-click");
23
- },
24
- onPointerDown() {
25
- if (point.editable && status == 2) {
26
- status = 3;
27
- map.moveable = false;
28
- }
29
- },
30
- onPointerUp() {
31
- if (point.editable && status == 3) {
32
- status = 2;
33
- map.moveable = true;
34
- point.coordinates = [event.geography[0], event.geography[1], point.coordinates[2] ?? point.height];
35
- point.event.trigger("data-update");
36
- }
7
+ let skip = 0;
8
+
9
+ const { size, center } = point;
10
+ const pointOffset = [-size[0] * center[0], -size[1] * center[1]];
11
+ const marker = new LbsAMap.Marker({
12
+ position: new LbsAMap.LngLat(0, 0),
13
+ height: point.height,
14
+ title: point._option.name,
15
+ icon: new LbsAMap.Icon({
16
+ image: point.src,
17
+ size: point.size,
18
+ imageSize: point.size,
19
+ }),
20
+ angle: point.rotation,
21
+ anchor: point.center,
22
+ draggable: point.editable,
23
+ extData: {
24
+ cursor: point.cursor,
25
+ silent: point.silent,
37
26
  },
38
- };
27
+ offset: pointOffset,
28
+ });
29
+ setIcon();
30
+ viewer.add([marker]);
39
31
 
40
- const bindEvent = (graphical) => {
41
- graphical.on("click", _event.onLClick);
42
- graphical.on("rightclick", _event.onRClick);
43
- graphical.on("mouseover", _event.onPointerIn);
44
- graphical.on("mouseout", _event.onPointerOut);
45
- };
32
+ if (point.coordinates) {
33
+ status = 2;
34
+ marker.setPosition(point.coordinates);
35
+ if (point.show) {
36
+ marker.show();
37
+ } else {
38
+ marker.hide();
39
+ }
40
+ }
46
41
 
47
- const darwMarker = (opt) => {
48
- marker = new LbsAMap.Marker({
49
- ...opt,
50
- title: point._option.name,
51
- icon: new LbsAMap.Icon({
52
- image: point.src,
53
- size: point.size,
54
- imageSize: point.size,
55
- }),
56
- angle: point.rotation * RD,
57
- anchor: point.center,
58
- draggable: point.editable,
59
- extData: {
60
- cursor: point.cursor,
61
- silent: point.silent,
62
- },
63
- });
64
- bindEvent(marker);
65
- viewer.add([marker]);
42
+ const onMouseMove = () => {
43
+ switch (status) {
44
+ case 0:
45
+ status = 1;
46
+ marker.setPosition(event.geography);
47
+ if (point.show) {
48
+ marker.show();
49
+ } else {
50
+ marker.hide();
51
+ }
52
+ break;
53
+ case 1:
54
+ case 3:
55
+ marker.setPosition(event.geography);
56
+ break;
57
+ }
66
58
  };
67
59
 
68
- const eventMarker = (e) => {
69
- darwMarker({
70
- position: new LbsAMap.LngLat(e.lnglat.lng, e.lnglat.lat),
71
- height: point.coordinates[2] ?? point.height,
72
- });
73
- viewer.off("click", eventMarker);
60
+ const onClick = () => {
61
+ if (status == 1) {
62
+ status = 2;
63
+ skip++;
64
+ point.coordinates = event.geography;
65
+ point.event.trigger("pointer-in");
66
+ }
74
67
  };
75
68
 
76
- if (point.coordinates) {
77
- darwMarker({
78
- position: new LbsAMap.LngLat(point.coordinates[0], point.coordinates[1]),
79
- height: point.coordinates[2] ?? point.height,
80
- });
81
- } else {
82
- viewer.on("click", eventMarker);
83
- }
84
-
85
69
  const onUpdate = (e: Set<string>) => {
86
- const keys = new Set();
70
+ if (skip) {
71
+ skip--;
72
+ return;
73
+ }
87
74
  e.forEach((key) => {
88
75
  switch (key) {
89
76
  case "show":
@@ -110,40 +97,41 @@ export function drawPoint(map: AMap, point: Point<unknown>) {
110
97
  }
111
98
  break;
112
99
  case "src":
113
- keys.add("icon");
100
+ setIcon();
114
101
  break;
115
102
  case "size":
116
- keys.add("icon");
103
+ setIcon();
117
104
  break;
118
105
  case "center":
119
106
  marker.setAnchor(point.center);
120
107
  break;
121
108
  case "rotation":
122
- marker.setAngle(point.rotation * RD);
109
+ marker.setAngle(point.rotation);
123
110
  break;
124
111
  default:
125
112
  throw new Error(`${key} 还不支持修改`);
126
113
  }
127
114
  });
128
-
129
- keys.forEach((e) => {
130
- switch (e) {
131
- case "icon":
132
- marker.setIcon(
133
- new LbsAMap.Icon({
134
- image: point.src,
135
- size: point.size,
136
- imageSize: point.size,
137
- }),
138
- );
139
- break;
140
- }
141
- });
142
115
  };
143
116
 
117
+ function setIcon() {
118
+ marker.setIcon(
119
+ new LbsAMap.Icon({
120
+ image: point.src,
121
+ size: point.size,
122
+ imageSize: point.size,
123
+ }),
124
+ );
125
+ }
126
+
144
127
  const onDestroy = () => {
145
128
  marker.remove();
146
129
  point.event.off("update", onUpdate);
147
130
  point.event.off("destroy", onDestroy);
148
131
  };
132
+
133
+ event.on("pointer-move", onMouseMove);
134
+ event.on("left-click", onClick);
135
+ point.event.on("update", onUpdate);
136
+ point.event.on("destroy", onDestroy);
149
137
  }
@@ -16,10 +16,6 @@ import { drawTile3D } from "./CesiumTile3D";
16
16
  import { CesiumModel } from "./CesiumModel";
17
17
 
18
18
  const $m = 20037508.34278924;
19
- /**
20
- * flyToViewer 最小判断半径(米)
21
- */
22
- const MIN_RADIUS = 500;
23
19
  /**
24
20
  * flyToViewer 最大缩放层级
25
21
  */
@@ -280,10 +276,6 @@ export class CesiumMap extends MapCombine {
280
276
  opt = {
281
277
  avoid: [0, 0, 0, 0],
282
278
 
283
- /**
284
- * flyToViewer 最小判断半径(米)
285
- */
286
- minRadius: MIN_RADIUS,
287
279
  /**
288
280
  * flyToViewer 最大缩放层级
289
281
  */
@@ -294,32 +286,10 @@ export class CesiumMap extends MapCombine {
294
286
  return this.flyTo(points[0], opt);
295
287
  }
296
288
  // 创建矩形
297
- const rectangle = Cesium.Rectangle.fromDegrees(
298
- ...this.getBounds(points, { canvas: this.viewer.scene.canvas, ...opt }),
289
+ const destination = Cesium.Rectangle.fromDegrees(
290
+ ...this.getBounds(points, { canvas: this.viewer.scene.canvas, maxZoom: MAX_ZOOM, ...opt }),
299
291
  );
300
292
 
301
- // 将经纬度转换为笛卡尔坐标
302
- const positions = points.map((coord) => {
303
- return Cesium.Cartesian3.fromDegrees(coord[0], coord[1]);
304
- });
305
-
306
- // 计算所有点位的包围球
307
- const boundingSphere = Cesium.BoundingSphere.fromPoints(positions);
308
-
309
- // 计算包围球半径
310
- const radius = boundingSphere.radius;
311
-
312
- let destination = rectangle;
313
- // 点位距离小于指定范围使用固定高度
314
- if (radius < MIN_RADIUS || (opt?.minRadius && radius < opt.minRadius)) {
315
- const center = Cesium.Rectangle.center(rectangle);
316
- destination = Cesium.Cartesian3.fromRadians(
317
- center.longitude,
318
- center.latitude,
319
- this.zoomTdistance(opt?.maxZoom ?? MAX_ZOOM),
320
- );
321
- }
322
-
323
293
  // 设置相机视角
324
294
  this.viewer.camera.flyTo({
325
295
  destination,
@@ -12,6 +12,9 @@ export function setValue<T>(obj: any, key: string, val: T, onChange: () => void)
12
12
  }
13
13
  }
14
14
 
15
+ /**
16
+ * 建立两个一维区间之间的线性映射关系,提供正向变换和逆向变换
17
+ */
15
18
  export class OneDimensional {
16
19
  k: number;
17
20
  b: number;
@@ -43,5 +43,31 @@ export const getBounds = function (points, opt) {
43
43
  minLat -= avoid[1] * latPixelRatio;
44
44
  maxLat += avoid[0] * latPixelRatio;
45
45
 
46
- return [minLon, minLat, maxLon, maxLat];
46
+ // 根据 maxZoom 计算最小矩形范围
47
+ const minRange = getMinRangeFromZoom(opt.maxZoom);
48
+ const centerLon = (minLon + maxLon) / 2;
49
+ const centerLat = (minLat + maxLat) / 2;
50
+
51
+ // 确保矩形范围不小于最小值 解决点位距离过近导致异常缩放问题
52
+ let finalWest = Math.min(minLon, centerLon - minRange / 2);
53
+ let finalEast = Math.max(maxLon, centerLon + minRange / 2);
54
+ let finalSouth = Math.min(minLat, centerLat - minRange / 2);
55
+ let finalNorth = Math.max(maxLat, centerLat + minRange / 2);
56
+
57
+ return [finalWest, finalSouth, finalEast, finalNorth];
47
58
  };
59
+
60
+ /**
61
+ * 根据 maxZoom 计算出一个“最小”的经纬度跨度
62
+ * @param zoom
63
+ * @returns
64
+ */
65
+ export function getMinRangeFromZoom(zoom) {
66
+ // 每个瓦片覆盖的经度范围
67
+ const lonPerTile = 360 / Math.pow(2, zoom);
68
+ // 每个瓦片覆盖的纬度范围(近似)
69
+ const latPerTile = 180 / Math.pow(2, zoom);
70
+
71
+ // 返回一个足够小的范围,比如一个瓦片范围的一半
72
+ return Math.max(lonPerTile, latPerTile) / 2;
73
+ }