@antv/l7-map 2.25.7 → 2.25.9
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/es/index.d.ts +5 -0
- package/es/index.js +4 -0
- package/es/map/camera.d.ts +690 -0
- package/es/map/camera.js +1138 -0
- package/es/map/css/l7.css +171 -0
- package/es/map/events.d.ts +384 -0
- package/es/map/events.js +231 -0
- package/es/map/geo/edge_insets.d.ts +97 -0
- package/es/map/geo/edge_insets.js +115 -0
- package/es/map/geo/lng_lat.d.ts +116 -0
- package/es/map/geo/lng_lat.js +159 -0
- package/es/map/geo/lng_lat_bounds.d.ts +217 -0
- package/es/map/geo/lng_lat_bounds.js +334 -0
- package/es/map/geo/mercator_coordinate.d.ts +113 -0
- package/es/map/geo/mercator_coordinate.js +142 -0
- package/es/map/geo/transform.d.ts +262 -0
- package/es/map/geo/transform.js +736 -0
- package/es/map/handler/box_zoom.d.ts +65 -0
- package/es/map/handler/box_zoom.js +145 -0
- package/es/map/handler/click_zoom.d.ts +24 -0
- package/es/map/handler/click_zoom.js +47 -0
- package/es/map/handler/cooperative_gestures.d.ts +40 -0
- package/es/map/handler/cooperative_gestures.js +94 -0
- package/es/map/handler/drag_handler.d.ts +88 -0
- package/es/map/handler/drag_handler.js +89 -0
- package/es/map/handler/drag_move_state_manager.d.ts +30 -0
- package/es/map/handler/drag_move_state_manager.js +94 -0
- package/es/map/handler/handler_util.d.ts +3 -0
- package/es/map/handler/handler_util.js +8 -0
- package/es/map/handler/keyboard.d.ts +88 -0
- package/es/map/handler/keyboard.js +197 -0
- package/es/map/handler/map_event.d.ts +46 -0
- package/es/map/handler/map_event.js +131 -0
- package/es/map/handler/mouse.d.ts +30 -0
- package/es/map/handler/mouse.js +85 -0
- package/es/map/handler/one_finger_touch_drag.d.ts +15 -0
- package/es/map/handler/one_finger_touch_drag.js +39 -0
- package/es/map/handler/scroll_zoom.d.ts +102 -0
- package/es/map/handler/scroll_zoom.js +312 -0
- package/es/map/handler/shim/dblclick_zoom.d.ts +44 -0
- package/es/map/handler/shim/dblclick_zoom.js +60 -0
- package/es/map/handler/shim/drag_pan.d.ts +79 -0
- package/es/map/handler/shim/drag_pan.js +77 -0
- package/es/map/handler/shim/drag_rotate.d.ts +54 -0
- package/es/map/handler/shim/drag_rotate.js +66 -0
- package/es/map/handler/shim/two_fingers_touch.d.ts +74 -0
- package/es/map/handler/shim/two_fingers_touch.js +106 -0
- package/es/map/handler/tap_drag_zoom.d.ts +28 -0
- package/es/map/handler/tap_drag_zoom.js +92 -0
- package/es/map/handler/tap_recognizer.d.ts +35 -0
- package/es/map/handler/tap_recognizer.js +107 -0
- package/es/map/handler/tap_zoom.d.ts +28 -0
- package/es/map/handler/tap_zoom.js +87 -0
- package/es/map/handler/touch_pan.d.ts +40 -0
- package/es/map/handler/touch_pan.js +85 -0
- package/es/map/handler/transform-provider.d.ts +23 -0
- package/es/map/handler/transform-provider.js +35 -0
- package/es/map/handler/two_fingers_touch.d.ts +107 -0
- package/es/map/handler/two_fingers_touch.js +289 -0
- package/es/map/handler_inertia.d.ts +20 -0
- package/es/map/handler_inertia.js +128 -0
- package/es/map/handler_manager.d.ts +154 -0
- package/es/map/handler_manager.js +466 -0
- package/es/map/map.d.ts +637 -0
- package/es/map/map.js +984 -0
- package/es/map/util/abort_error.d.ts +15 -0
- package/es/map/util/abort_error.js +21 -0
- package/es/map/util/browser.d.ts +10 -0
- package/es/map/util/browser.js +30 -0
- package/es/map/util/dom.d.ts +30 -0
- package/es/map/util/dom.js +105 -0
- package/es/map/util/evented.d.ts +75 -0
- package/es/map/util/evented.js +158 -0
- package/es/map/util/simpleMapCoord.d.ts +31 -0
- package/es/map/util/simpleMapCoord.js +54 -0
- package/es/map/util/task_queue.d.ts +18 -0
- package/es/map/util/task_queue.js +54 -0
- package/es/map/util/util.d.ts +104 -0
- package/es/map/util/util.js +155 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +33 -0
- package/lib/map/camera.d.ts +690 -0
- package/lib/map/camera.js +1145 -0
- package/lib/map/css/l7.css +171 -0
- package/lib/map/events.d.ts +384 -0
- package/lib/map/events.js +240 -0
- package/lib/map/geo/edge_insets.d.ts +97 -0
- package/lib/map/geo/edge_insets.js +122 -0
- package/lib/map/geo/lng_lat.d.ts +116 -0
- package/lib/map/geo/lng_lat.js +166 -0
- package/lib/map/geo/lng_lat_bounds.d.ts +217 -0
- package/lib/map/geo/lng_lat_bounds.js +341 -0
- package/lib/map/geo/mercator_coordinate.d.ts +113 -0
- package/lib/map/geo/mercator_coordinate.js +157 -0
- package/lib/map/geo/transform.d.ts +262 -0
- package/lib/map/geo/transform.js +744 -0
- package/lib/map/handler/box_zoom.d.ts +65 -0
- package/lib/map/handler/box_zoom.js +153 -0
- package/lib/map/handler/click_zoom.d.ts +24 -0
- package/lib/map/handler/click_zoom.js +54 -0
- package/lib/map/handler/cooperative_gestures.d.ts +40 -0
- package/lib/map/handler/cooperative_gestures.js +101 -0
- package/lib/map/handler/drag_handler.d.ts +88 -0
- package/lib/map/handler/drag_handler.js +97 -0
- package/lib/map/handler/drag_move_state_manager.d.ts +30 -0
- package/lib/map/handler/drag_move_state_manager.js +103 -0
- package/lib/map/handler/handler_util.d.ts +3 -0
- package/lib/map/handler/handler_util.js +14 -0
- package/lib/map/handler/keyboard.d.ts +88 -0
- package/lib/map/handler/keyboard.js +205 -0
- package/lib/map/handler/map_event.d.ts +46 -0
- package/lib/map/handler/map_event.js +140 -0
- package/lib/map/handler/mouse.d.ts +30 -0
- package/lib/map/handler/mouse.js +93 -0
- package/lib/map/handler/one_finger_touch_drag.d.ts +15 -0
- package/lib/map/handler/one_finger_touch_drag.js +47 -0
- package/lib/map/handler/scroll_zoom.d.ts +102 -0
- package/lib/map/handler/scroll_zoom.js +320 -0
- package/lib/map/handler/shim/dblclick_zoom.d.ts +44 -0
- package/lib/map/handler/shim/dblclick_zoom.js +68 -0
- package/lib/map/handler/shim/drag_pan.d.ts +79 -0
- package/lib/map/handler/shim/drag_pan.js +85 -0
- package/lib/map/handler/shim/drag_rotate.d.ts +54 -0
- package/lib/map/handler/shim/drag_rotate.js +74 -0
- package/lib/map/handler/shim/two_fingers_touch.d.ts +74 -0
- package/lib/map/handler/shim/two_fingers_touch.js +114 -0
- package/lib/map/handler/tap_drag_zoom.d.ts +28 -0
- package/lib/map/handler/tap_drag_zoom.js +99 -0
- package/lib/map/handler/tap_recognizer.d.ts +35 -0
- package/lib/map/handler/tap_recognizer.js +116 -0
- package/lib/map/handler/tap_zoom.d.ts +28 -0
- package/lib/map/handler/tap_zoom.js +94 -0
- package/lib/map/handler/touch_pan.d.ts +40 -0
- package/lib/map/handler/touch_pan.js +92 -0
- package/lib/map/handler/transform-provider.d.ts +23 -0
- package/lib/map/handler/transform-provider.js +43 -0
- package/lib/map/handler/two_fingers_touch.d.ts +107 -0
- package/lib/map/handler/two_fingers_touch.js +296 -0
- package/lib/map/handler_inertia.d.ts +20 -0
- package/lib/map/handler_inertia.js +136 -0
- package/lib/map/handler_manager.d.ts +154 -0
- package/lib/map/handler_manager.js +474 -0
- package/lib/map/map.d.ts +637 -0
- package/lib/map/map.js +991 -0
- package/lib/map/util/abort_error.d.ts +15 -0
- package/lib/map/util/abort_error.js +29 -0
- package/lib/map/util/browser.d.ts +10 -0
- package/lib/map/util/browser.js +36 -0
- package/lib/map/util/dom.d.ts +30 -0
- package/lib/map/util/dom.js +113 -0
- package/lib/map/util/evented.d.ts +75 -0
- package/lib/map/util/evented.js +167 -0
- package/lib/map/util/simpleMapCoord.d.ts +31 -0
- package/lib/map/util/simpleMapCoord.js +62 -0
- package/lib/map/util/task_queue.d.ts +18 -0
- package/lib/map/util/task_queue.js +62 -0
- package/lib/map/util/util.d.ts +104 -0
- package/lib/map/util/util.js +171 -0
- package/package.json +2 -2
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
|
2
|
+
import Point from '@mapbox/point-geometry';
|
|
3
|
+
import { mat2, mat4, vec4 } from 'gl-matrix';
|
|
4
|
+
import { clamp, interpolates, wrap } from "../util/util";
|
|
5
|
+
import { EdgeInsets } from "./edge_insets";
|
|
6
|
+
import { LngLat } from "./lng_lat";
|
|
7
|
+
import { LngLatBounds } from "./lng_lat_bounds";
|
|
8
|
+
import { MercatorCoordinate, mercatorXfromLng, mercatorYfromLat, mercatorZfromAltitude } from "./mercator_coordinate";
|
|
9
|
+
export const MAX_VALID_LATITUDE = 85.051129;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @internal
|
|
13
|
+
* A single transform, generally used for a single tile to be
|
|
14
|
+
* scaled, rotated, and zoomed.
|
|
15
|
+
*/
|
|
16
|
+
export class Transform {
|
|
17
|
+
constructor(minZoom, maxZoom, minPitch, maxPitch, renderWorldCopies) {
|
|
18
|
+
_defineProperty(this, "tileSize", void 0);
|
|
19
|
+
_defineProperty(this, "tileZoom", void 0);
|
|
20
|
+
_defineProperty(this, "lngRange", void 0);
|
|
21
|
+
_defineProperty(this, "latRange", void 0);
|
|
22
|
+
_defineProperty(this, "scale", void 0);
|
|
23
|
+
_defineProperty(this, "width", void 0);
|
|
24
|
+
_defineProperty(this, "height", void 0);
|
|
25
|
+
_defineProperty(this, "angle", void 0);
|
|
26
|
+
_defineProperty(this, "rotationMatrix", void 0);
|
|
27
|
+
_defineProperty(this, "pixelsToGLUnits", void 0);
|
|
28
|
+
_defineProperty(this, "cameraToCenterDistance", void 0);
|
|
29
|
+
_defineProperty(this, "mercatorMatrix", void 0);
|
|
30
|
+
_defineProperty(this, "projMatrix", void 0);
|
|
31
|
+
_defineProperty(this, "invProjMatrix", void 0);
|
|
32
|
+
_defineProperty(this, "alignedProjMatrix", void 0);
|
|
33
|
+
_defineProperty(this, "pixelMatrix", void 0);
|
|
34
|
+
_defineProperty(this, "pixelMatrix3D", void 0);
|
|
35
|
+
_defineProperty(this, "pixelMatrixInverse", void 0);
|
|
36
|
+
_defineProperty(this, "glCoordMatrix", void 0);
|
|
37
|
+
_defineProperty(this, "labelPlaneMatrix", void 0);
|
|
38
|
+
_defineProperty(this, "minElevationForCurrentTile", void 0);
|
|
39
|
+
_defineProperty(this, "_fov", void 0);
|
|
40
|
+
_defineProperty(this, "_pitch", void 0);
|
|
41
|
+
_defineProperty(this, "_zoom", void 0);
|
|
42
|
+
_defineProperty(this, "_unmodified", void 0);
|
|
43
|
+
_defineProperty(this, "_renderWorldCopies", void 0);
|
|
44
|
+
_defineProperty(this, "_minZoom", void 0);
|
|
45
|
+
_defineProperty(this, "_maxZoom", void 0);
|
|
46
|
+
_defineProperty(this, "_minPitch", void 0);
|
|
47
|
+
_defineProperty(this, "_maxPitch", void 0);
|
|
48
|
+
_defineProperty(this, "_center", void 0);
|
|
49
|
+
_defineProperty(this, "_elevation", void 0);
|
|
50
|
+
_defineProperty(this, "_pixelPerMeter", void 0);
|
|
51
|
+
_defineProperty(this, "_edgeInsets", void 0);
|
|
52
|
+
_defineProperty(this, "_constraining", void 0);
|
|
53
|
+
_defineProperty(this, "_posMatrixCache", void 0);
|
|
54
|
+
_defineProperty(this, "_alignedPosMatrixCache", void 0);
|
|
55
|
+
this.tileSize = 512; // constant
|
|
56
|
+
|
|
57
|
+
this._renderWorldCopies = renderWorldCopies === undefined ? true : !!renderWorldCopies;
|
|
58
|
+
this._minZoom = minZoom || 0;
|
|
59
|
+
this._maxZoom = maxZoom || 22;
|
|
60
|
+
this._minPitch = minPitch === undefined || minPitch === null ? 0 : minPitch;
|
|
61
|
+
this._maxPitch = maxPitch === undefined || maxPitch === null ? 60 : maxPitch;
|
|
62
|
+
this.setMaxBounds();
|
|
63
|
+
this.width = 0;
|
|
64
|
+
this.height = 0;
|
|
65
|
+
this._center = new LngLat(0, 0);
|
|
66
|
+
this._elevation = 0;
|
|
67
|
+
this.zoom = 0;
|
|
68
|
+
this.angle = 0;
|
|
69
|
+
this._fov = 0.6435011087932844;
|
|
70
|
+
this._pitch = 0;
|
|
71
|
+
this._unmodified = true;
|
|
72
|
+
this._edgeInsets = new EdgeInsets();
|
|
73
|
+
this._posMatrixCache = {};
|
|
74
|
+
this._alignedPosMatrixCache = {};
|
|
75
|
+
this.minElevationForCurrentTile = 0;
|
|
76
|
+
}
|
|
77
|
+
clone() {
|
|
78
|
+
const clone = new Transform(this._minZoom, this._maxZoom, this._minPitch, this.maxPitch, this._renderWorldCopies);
|
|
79
|
+
clone.apply(this);
|
|
80
|
+
return clone;
|
|
81
|
+
}
|
|
82
|
+
apply(that) {
|
|
83
|
+
this.tileSize = that.tileSize;
|
|
84
|
+
this.latRange = that.latRange;
|
|
85
|
+
this.width = that.width;
|
|
86
|
+
this.height = that.height;
|
|
87
|
+
this._center = that._center;
|
|
88
|
+
this._elevation = that._elevation;
|
|
89
|
+
this.minElevationForCurrentTile = that.minElevationForCurrentTile;
|
|
90
|
+
this.zoom = that.zoom;
|
|
91
|
+
this.angle = that.angle;
|
|
92
|
+
this._fov = that._fov;
|
|
93
|
+
this._pitch = that._pitch;
|
|
94
|
+
this._unmodified = that._unmodified;
|
|
95
|
+
this._edgeInsets = that._edgeInsets.clone();
|
|
96
|
+
this._calcMatrices();
|
|
97
|
+
}
|
|
98
|
+
get minZoom() {
|
|
99
|
+
return this._minZoom;
|
|
100
|
+
}
|
|
101
|
+
set minZoom(zoom) {
|
|
102
|
+
if (this._minZoom === zoom) return;
|
|
103
|
+
this._minZoom = zoom;
|
|
104
|
+
this.zoom = Math.max(this.zoom, zoom);
|
|
105
|
+
}
|
|
106
|
+
get maxZoom() {
|
|
107
|
+
return this._maxZoom;
|
|
108
|
+
}
|
|
109
|
+
set maxZoom(zoom) {
|
|
110
|
+
if (this._maxZoom === zoom) return;
|
|
111
|
+
this._maxZoom = zoom;
|
|
112
|
+
this.zoom = Math.min(this.zoom, zoom);
|
|
113
|
+
}
|
|
114
|
+
get minPitch() {
|
|
115
|
+
return this._minPitch;
|
|
116
|
+
}
|
|
117
|
+
set minPitch(pitch) {
|
|
118
|
+
if (this._minPitch === pitch) return;
|
|
119
|
+
this._minPitch = pitch;
|
|
120
|
+
this.pitch = Math.max(this.pitch, pitch);
|
|
121
|
+
}
|
|
122
|
+
get maxPitch() {
|
|
123
|
+
return this._maxPitch;
|
|
124
|
+
}
|
|
125
|
+
set maxPitch(pitch) {
|
|
126
|
+
if (this._maxPitch === pitch) return;
|
|
127
|
+
this._maxPitch = pitch;
|
|
128
|
+
this.pitch = Math.min(this.pitch, pitch);
|
|
129
|
+
}
|
|
130
|
+
get renderWorldCopies() {
|
|
131
|
+
return this._renderWorldCopies;
|
|
132
|
+
}
|
|
133
|
+
set renderWorldCopies(renderWorldCopies) {
|
|
134
|
+
if (renderWorldCopies === undefined) {
|
|
135
|
+
renderWorldCopies = true;
|
|
136
|
+
} else if (renderWorldCopies === null) {
|
|
137
|
+
renderWorldCopies = false;
|
|
138
|
+
}
|
|
139
|
+
this._renderWorldCopies = renderWorldCopies;
|
|
140
|
+
}
|
|
141
|
+
get worldSize() {
|
|
142
|
+
return this.tileSize * this.scale;
|
|
143
|
+
}
|
|
144
|
+
get centerOffset() {
|
|
145
|
+
return this.centerPoint._sub(this.size._div(2));
|
|
146
|
+
}
|
|
147
|
+
get size() {
|
|
148
|
+
return new Point(this.width, this.height);
|
|
149
|
+
}
|
|
150
|
+
get bearing() {
|
|
151
|
+
return -this.angle / Math.PI * 180;
|
|
152
|
+
}
|
|
153
|
+
set bearing(bearing) {
|
|
154
|
+
const b = -wrap(bearing, -180, 180) * Math.PI / 180;
|
|
155
|
+
if (this.angle === b) return;
|
|
156
|
+
this._unmodified = false;
|
|
157
|
+
this.angle = b;
|
|
158
|
+
this._calcMatrices();
|
|
159
|
+
|
|
160
|
+
// 2x2 matrix for rotating points
|
|
161
|
+
this.rotationMatrix = mat2.create();
|
|
162
|
+
mat2.rotate(this.rotationMatrix, this.rotationMatrix, this.angle);
|
|
163
|
+
}
|
|
164
|
+
get pitch() {
|
|
165
|
+
return this._pitch / Math.PI * 180;
|
|
166
|
+
}
|
|
167
|
+
set pitch(pitch) {
|
|
168
|
+
const p = clamp(pitch, this.minPitch, this.maxPitch) / 180 * Math.PI;
|
|
169
|
+
if (this._pitch === p) return;
|
|
170
|
+
this._unmodified = false;
|
|
171
|
+
this._pitch = p;
|
|
172
|
+
this._calcMatrices();
|
|
173
|
+
}
|
|
174
|
+
get fov() {
|
|
175
|
+
return this._fov / Math.PI * 180;
|
|
176
|
+
}
|
|
177
|
+
set fov(fov) {
|
|
178
|
+
fov = Math.max(0.01, Math.min(60, fov));
|
|
179
|
+
if (this._fov === fov) return;
|
|
180
|
+
this._unmodified = false;
|
|
181
|
+
this._fov = fov / 180 * Math.PI;
|
|
182
|
+
this._calcMatrices();
|
|
183
|
+
}
|
|
184
|
+
get zoom() {
|
|
185
|
+
return this._zoom;
|
|
186
|
+
}
|
|
187
|
+
set zoom(zoom) {
|
|
188
|
+
const constrainedZoom = Math.min(Math.max(zoom, this.minZoom), this.maxZoom);
|
|
189
|
+
if (this._zoom === constrainedZoom) return;
|
|
190
|
+
this._unmodified = false;
|
|
191
|
+
this._zoom = constrainedZoom;
|
|
192
|
+
this.tileZoom = Math.max(0, Math.floor(constrainedZoom));
|
|
193
|
+
this.scale = this.zoomScale(constrainedZoom);
|
|
194
|
+
this._constrain();
|
|
195
|
+
this._calcMatrices();
|
|
196
|
+
}
|
|
197
|
+
get center() {
|
|
198
|
+
return this._center;
|
|
199
|
+
}
|
|
200
|
+
set center(center) {
|
|
201
|
+
if (center.lat === this._center.lat && center.lng === this._center.lng) return;
|
|
202
|
+
this._unmodified = false;
|
|
203
|
+
this._center = center;
|
|
204
|
+
this._constrain();
|
|
205
|
+
this._calcMatrices();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Elevation at current center point, meters above sea level
|
|
210
|
+
*/
|
|
211
|
+
get elevation() {
|
|
212
|
+
return this._elevation;
|
|
213
|
+
}
|
|
214
|
+
set elevation(elevation) {
|
|
215
|
+
if (elevation === this._elevation) return;
|
|
216
|
+
this._elevation = elevation;
|
|
217
|
+
this._constrain();
|
|
218
|
+
this._calcMatrices();
|
|
219
|
+
}
|
|
220
|
+
get padding() {
|
|
221
|
+
return this._edgeInsets.toJSON();
|
|
222
|
+
}
|
|
223
|
+
set padding(padding) {
|
|
224
|
+
if (this._edgeInsets.equals(padding)) return;
|
|
225
|
+
this._unmodified = false;
|
|
226
|
+
//Update edge-insets inplace
|
|
227
|
+
this._edgeInsets.interpolate(this._edgeInsets, padding, 1);
|
|
228
|
+
this._calcMatrices();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* The center of the screen in pixels with the top-left corner being (0,0)
|
|
233
|
+
* and +y axis pointing downwards. This accounts for padding.
|
|
234
|
+
*/
|
|
235
|
+
get centerPoint() {
|
|
236
|
+
return this._edgeInsets.getCenter(this.width, this.height);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Returns if the padding params match
|
|
241
|
+
*
|
|
242
|
+
* @param padding - the padding to check against
|
|
243
|
+
* @returns true if they are equal, false otherwise
|
|
244
|
+
*/
|
|
245
|
+
isPaddingEqual(padding) {
|
|
246
|
+
return this._edgeInsets.equals(padding);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Helper method to update edge-insets in place
|
|
251
|
+
*
|
|
252
|
+
* @param start - the starting padding
|
|
253
|
+
* @param target - the target padding
|
|
254
|
+
* @param t - the step/weight
|
|
255
|
+
*/
|
|
256
|
+
interpolatePadding(start, target, t) {
|
|
257
|
+
this._unmodified = false;
|
|
258
|
+
this._edgeInsets.interpolate(start, target, t);
|
|
259
|
+
this._constrain();
|
|
260
|
+
this._calcMatrices();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Return a zoom level that will cover all tiles the transform
|
|
265
|
+
* @param options - the options
|
|
266
|
+
* @returns zoom level An integer zoom level at which all tiles will be visible.
|
|
267
|
+
*/
|
|
268
|
+
coveringZoomLevel(options) {
|
|
269
|
+
const z = (options.roundZoom ? Math.round : Math.floor)(this.zoom + this.scaleZoom(this.tileSize / options.tileSize));
|
|
270
|
+
// At negative zoom levels load tiles from z0 because negative tile zoom levels don't exist.
|
|
271
|
+
return Math.max(0, z);
|
|
272
|
+
}
|
|
273
|
+
resize(width, height) {
|
|
274
|
+
this.width = width;
|
|
275
|
+
this.height = height;
|
|
276
|
+
this.pixelsToGLUnits = [2 / width, -2 / height];
|
|
277
|
+
this._constrain();
|
|
278
|
+
this._calcMatrices();
|
|
279
|
+
}
|
|
280
|
+
get unmodified() {
|
|
281
|
+
return this._unmodified;
|
|
282
|
+
}
|
|
283
|
+
zoomScale(zoom) {
|
|
284
|
+
return Math.pow(2, zoom);
|
|
285
|
+
}
|
|
286
|
+
scaleZoom(scale) {
|
|
287
|
+
return Math.log(scale) / Math.LN2;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Convert from LngLat to world coordinates (Mercator coordinates scaled by 512)
|
|
292
|
+
* @param lnglat - the lngLat
|
|
293
|
+
* @returns Point
|
|
294
|
+
*/
|
|
295
|
+
project(lnglat) {
|
|
296
|
+
const lat = clamp(lnglat.lat, -MAX_VALID_LATITUDE, MAX_VALID_LATITUDE);
|
|
297
|
+
return new Point(mercatorXfromLng(lnglat.lng) * this.worldSize, mercatorYfromLat(lat) * this.worldSize);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Convert from world coordinates ([0, 512],[0, 512]) to LngLat ([-180, 180], [-90, 90])
|
|
302
|
+
* @param point - world coordinate
|
|
303
|
+
* @returns LngLat
|
|
304
|
+
*/
|
|
305
|
+
unproject(point) {
|
|
306
|
+
return new MercatorCoordinate(point.x / this.worldSize, point.y / this.worldSize).toLngLat();
|
|
307
|
+
}
|
|
308
|
+
get point() {
|
|
309
|
+
return this.project(this.center);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* get the camera position in LngLat and altitudes in meter
|
|
314
|
+
* @returns An object with lngLat & altitude.
|
|
315
|
+
*/
|
|
316
|
+
getCameraPosition() {
|
|
317
|
+
const lngLat = this.pointLocation(this.getCameraPoint());
|
|
318
|
+
const altitude = Math.cos(this._pitch) * this.cameraToCenterDistance / this._pixelPerMeter;
|
|
319
|
+
return {
|
|
320
|
+
lngLat,
|
|
321
|
+
altitude: altitude + this.elevation
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
setLocationAtPoint(lnglat, point) {
|
|
325
|
+
const a = this.pointCoordinate(point);
|
|
326
|
+
const b = this.pointCoordinate(this.centerPoint);
|
|
327
|
+
const loc = this.locationCoordinate(lnglat);
|
|
328
|
+
const newCenter = new MercatorCoordinate(loc.x - (a.x - b.x), loc.y - (a.y - b.y));
|
|
329
|
+
this.center = this.coordinateLocation(newCenter);
|
|
330
|
+
if (this._renderWorldCopies) {
|
|
331
|
+
this.center = this.center.wrap();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Given a LngLat location, return the screen point that corresponds to it
|
|
337
|
+
* @param lnglat - location
|
|
338
|
+
* @param terrain - optional terrain
|
|
339
|
+
* @returns screen point
|
|
340
|
+
*/
|
|
341
|
+
locationPoint(lnglat) {
|
|
342
|
+
return this.coordinatePoint(this.locationCoordinate(lnglat));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Given a point on screen, return its lnglat
|
|
347
|
+
* @param p - screen point
|
|
348
|
+
* @param terrain - optional terrain
|
|
349
|
+
* @returns lnglat location
|
|
350
|
+
*/
|
|
351
|
+
pointLocation(p) {
|
|
352
|
+
return this.coordinateLocation(this.pointCoordinate(p));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Given a geographical lnglat, return an unrounded
|
|
357
|
+
* coordinate that represents it at low zoom level.
|
|
358
|
+
* @param lnglat - the location
|
|
359
|
+
* @returns The mercator coordinate
|
|
360
|
+
*/
|
|
361
|
+
locationCoordinate(lnglat) {
|
|
362
|
+
return MercatorCoordinate.fromLngLat(lnglat);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Given a Coordinate, return its geographical position.
|
|
367
|
+
* @param coord - mercator coordinates
|
|
368
|
+
* @returns lng and lat
|
|
369
|
+
*/
|
|
370
|
+
coordinateLocation(coord) {
|
|
371
|
+
return coord && coord.toLngLat();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Given a Point, return its mercator coordinate.
|
|
376
|
+
* @param p - the point
|
|
377
|
+
* @param terrain - optional terrain
|
|
378
|
+
* @returns lnglat
|
|
379
|
+
*/
|
|
380
|
+
pointCoordinate(p) {
|
|
381
|
+
// calculate point-coordinate on flat earth
|
|
382
|
+
const targetZ = 0;
|
|
383
|
+
// since we don't know the correct projected z value for the point,
|
|
384
|
+
// unproject two points to get a line and then find the point on that
|
|
385
|
+
// line with z=0
|
|
386
|
+
|
|
387
|
+
const coord0 = [p.x, p.y, 0, 1];
|
|
388
|
+
const coord1 = [p.x, p.y, 1, 1];
|
|
389
|
+
vec4.transformMat4(coord0, coord0, this.pixelMatrixInverse);
|
|
390
|
+
vec4.transformMat4(coord1, coord1, this.pixelMatrixInverse);
|
|
391
|
+
const w0 = coord0[3];
|
|
392
|
+
const w1 = coord1[3];
|
|
393
|
+
const x0 = coord0[0] / w0;
|
|
394
|
+
const x1 = coord1[0] / w1;
|
|
395
|
+
const y0 = coord0[1] / w0;
|
|
396
|
+
const y1 = coord1[1] / w1;
|
|
397
|
+
const z0 = coord0[2] / w0;
|
|
398
|
+
const z1 = coord1[2] / w1;
|
|
399
|
+
const t = z0 === z1 ? 0 : (targetZ - z0) / (z1 - z0);
|
|
400
|
+
return new MercatorCoordinate(interpolates.number(x0, x1, t) / this.worldSize, interpolates.number(y0, y1, t) / this.worldSize);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Given a coordinate, return the screen point that corresponds to it
|
|
405
|
+
* @param coord - the coordinates
|
|
406
|
+
* @param elevation - the elevation
|
|
407
|
+
* @param pixelMatrix - the pixel matrix
|
|
408
|
+
* @returns screen point
|
|
409
|
+
*/
|
|
410
|
+
coordinatePoint(coord, elevation = 0, pixelMatrix = this.pixelMatrix) {
|
|
411
|
+
const p = [coord.x * this.worldSize, coord.y * this.worldSize, elevation, 1];
|
|
412
|
+
vec4.transformMat4(p, p, pixelMatrix);
|
|
413
|
+
return new Point(p[0] / p[3], p[1] / p[3]);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Returns the map's geographical bounds. When the bearing or pitch is non-zero, the visible region is not
|
|
418
|
+
* an axis-aligned rectangle, and the result is the smallest bounds that encompasses the visible region.
|
|
419
|
+
* @returns Returns a {@link LngLatBounds} object describing the map's geographical bounds.
|
|
420
|
+
*/
|
|
421
|
+
getBounds() {
|
|
422
|
+
const top = Math.max(0, this.height / 2 - this.getHorizon());
|
|
423
|
+
return new LngLatBounds().extend(this.pointLocation(new Point(0, top))).extend(this.pointLocation(new Point(this.width, top))).extend(this.pointLocation(new Point(this.width, this.height))).extend(this.pointLocation(new Point(0, this.height)));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Returns the maximum geographical bounds the map is constrained to, or `null` if none set.
|
|
428
|
+
* @returns max bounds
|
|
429
|
+
*/
|
|
430
|
+
getMaxBounds() {
|
|
431
|
+
if (!this.latRange || this.latRange.length !== 2 || !this.lngRange || this.lngRange.length !== 2) return null;
|
|
432
|
+
return new LngLatBounds([this.lngRange[0], this.latRange[0]], [this.lngRange[1], this.latRange[1]]);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Calculate pixel height of the visible horizon in relation to map-center (e.g. height/2),
|
|
437
|
+
* multiplied by a static factor to simulate the earth-radius.
|
|
438
|
+
* The calculated value is the horizontal line from the camera-height to sea-level.
|
|
439
|
+
* @returns Horizon above center in pixels.
|
|
440
|
+
*/
|
|
441
|
+
getHorizon() {
|
|
442
|
+
return Math.tan(Math.PI / 2 - this._pitch) * this.cameraToCenterDistance * 0.85;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Sets or clears the map's geographical constraints.
|
|
447
|
+
* @param bounds - A {@link LngLatBounds} object describing the new geographic boundaries of the map.
|
|
448
|
+
*/
|
|
449
|
+
setMaxBounds(bounds) {
|
|
450
|
+
if (bounds) {
|
|
451
|
+
this.lngRange = [bounds.getWest(), bounds.getEast()];
|
|
452
|
+
this.latRange = [bounds.getSouth(), bounds.getNorth()];
|
|
453
|
+
this._constrain();
|
|
454
|
+
} else {
|
|
455
|
+
this.lngRange = null;
|
|
456
|
+
this.latRange = [-MAX_VALID_LATITUDE, MAX_VALID_LATITUDE];
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
customLayerMatrix() {
|
|
460
|
+
return mat4.clone(this.mercatorMatrix);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Get center lngLat and zoom to ensure that
|
|
465
|
+
* 1) everything beyond the bounds is excluded
|
|
466
|
+
* 2) a given lngLat is as near the center as possible
|
|
467
|
+
* Bounds are those set by maxBounds or North & South "Poles" and, if only 1 globe is displayed, antimeridian.
|
|
468
|
+
*/
|
|
469
|
+
getConstrained(lngLat, zoom) {
|
|
470
|
+
zoom = clamp(+zoom, this.minZoom, this.maxZoom);
|
|
471
|
+
const result = {
|
|
472
|
+
center: new LngLat(lngLat.lng, lngLat.lat),
|
|
473
|
+
zoom
|
|
474
|
+
};
|
|
475
|
+
let lngRange = this.lngRange;
|
|
476
|
+
if (!this._renderWorldCopies && lngRange === null) {
|
|
477
|
+
const almost180 = 180 - 1e-10;
|
|
478
|
+
lngRange = [-almost180, almost180];
|
|
479
|
+
}
|
|
480
|
+
const worldSize = this.tileSize * this.zoomScale(result.zoom); // A world size for the requested zoom level, not the current world size
|
|
481
|
+
let minY = 0;
|
|
482
|
+
let maxY = worldSize;
|
|
483
|
+
let minX = 0;
|
|
484
|
+
let maxX = worldSize;
|
|
485
|
+
let scaleY = 0;
|
|
486
|
+
let scaleX = 0;
|
|
487
|
+
const {
|
|
488
|
+
x: screenWidth,
|
|
489
|
+
y: screenHeight
|
|
490
|
+
} = this.size;
|
|
491
|
+
if (this.latRange) {
|
|
492
|
+
const latRange = this.latRange;
|
|
493
|
+
minY = mercatorYfromLat(latRange[1]) * worldSize;
|
|
494
|
+
maxY = mercatorYfromLat(latRange[0]) * worldSize;
|
|
495
|
+
const shouldZoomIn = maxY - minY < screenHeight;
|
|
496
|
+
if (shouldZoomIn) scaleY = screenHeight / (maxY - minY);
|
|
497
|
+
}
|
|
498
|
+
if (lngRange) {
|
|
499
|
+
minX = wrap(mercatorXfromLng(lngRange[0]) * worldSize, 0, worldSize);
|
|
500
|
+
maxX = wrap(mercatorXfromLng(lngRange[1]) * worldSize, 0, worldSize);
|
|
501
|
+
if (maxX < minX) maxX += worldSize;
|
|
502
|
+
const shouldZoomIn = maxX - minX < screenWidth;
|
|
503
|
+
if (shouldZoomIn) scaleX = screenWidth / (maxX - minX);
|
|
504
|
+
}
|
|
505
|
+
const {
|
|
506
|
+
x: originalX,
|
|
507
|
+
y: originalY
|
|
508
|
+
} = this.project.call({
|
|
509
|
+
worldSize
|
|
510
|
+
}, lngLat);
|
|
511
|
+
let modifiedX, modifiedY;
|
|
512
|
+
const scale = Math.max(scaleX || 0, scaleY || 0);
|
|
513
|
+
if (scale) {
|
|
514
|
+
// zoom in to exclude all beyond the given lng/lat ranges
|
|
515
|
+
const newPoint = new Point(scaleX ? (maxX + minX) / 2 : originalX, scaleY ? (maxY + minY) / 2 : originalY);
|
|
516
|
+
result.center = this.unproject.call({
|
|
517
|
+
worldSize
|
|
518
|
+
}, newPoint).wrap();
|
|
519
|
+
result.zoom += this.scaleZoom(scale);
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
if (this.latRange) {
|
|
523
|
+
const h2 = screenHeight / 2;
|
|
524
|
+
if (originalY - h2 < minY) modifiedY = minY + h2;
|
|
525
|
+
if (originalY + h2 > maxY) modifiedY = maxY - h2;
|
|
526
|
+
}
|
|
527
|
+
if (lngRange) {
|
|
528
|
+
const centerX = (minX + maxX) / 2;
|
|
529
|
+
let wrappedX = originalX;
|
|
530
|
+
if (this._renderWorldCopies) {
|
|
531
|
+
wrappedX = wrap(originalX, centerX - worldSize / 2, centerX + worldSize / 2);
|
|
532
|
+
}
|
|
533
|
+
const w2 = screenWidth / 2;
|
|
534
|
+
if (wrappedX - w2 < minX) modifiedX = minX + w2;
|
|
535
|
+
if (wrappedX + w2 > maxX) modifiedX = maxX - w2;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// pan the map if the screen goes off the range
|
|
539
|
+
if (modifiedX !== undefined || modifiedY !== undefined) {
|
|
540
|
+
var _modifiedX, _modifiedY;
|
|
541
|
+
const newPoint = new Point((_modifiedX = modifiedX) !== null && _modifiedX !== void 0 ? _modifiedX : originalX, (_modifiedY = modifiedY) !== null && _modifiedY !== void 0 ? _modifiedY : originalY);
|
|
542
|
+
result.center = this.unproject.call({
|
|
543
|
+
worldSize
|
|
544
|
+
}, newPoint).wrap();
|
|
545
|
+
}
|
|
546
|
+
return result;
|
|
547
|
+
}
|
|
548
|
+
_constrain() {
|
|
549
|
+
if (!this.center || !this.width || !this.height || this._constraining) return;
|
|
550
|
+
this._constraining = true;
|
|
551
|
+
const unmodified = this._unmodified;
|
|
552
|
+
const {
|
|
553
|
+
center,
|
|
554
|
+
zoom
|
|
555
|
+
} = this.getConstrained(this.center, this.zoom);
|
|
556
|
+
this.center = center;
|
|
557
|
+
this.zoom = zoom;
|
|
558
|
+
this._unmodified = unmodified;
|
|
559
|
+
this._constraining = false;
|
|
560
|
+
}
|
|
561
|
+
_calcMatrices() {
|
|
562
|
+
if (!this.height) return;
|
|
563
|
+
const halfFov = this._fov / 2;
|
|
564
|
+
const offset = this.centerOffset;
|
|
565
|
+
const x = this.point.x,
|
|
566
|
+
y = this.point.y;
|
|
567
|
+
this.cameraToCenterDistance = 0.5 / Math.tan(halfFov) * this.height;
|
|
568
|
+
this._pixelPerMeter = mercatorZfromAltitude(1, this.center.lat) * this.worldSize;
|
|
569
|
+
let m = mat4.identity(new Float64Array(16));
|
|
570
|
+
mat4.scale(m, m, [this.width / 2, -this.height / 2, 1]);
|
|
571
|
+
mat4.translate(m, m, [1, -1, 0]);
|
|
572
|
+
this.labelPlaneMatrix = m;
|
|
573
|
+
m = mat4.identity(new Float64Array(16));
|
|
574
|
+
mat4.scale(m, m, [1, -1, 1]);
|
|
575
|
+
mat4.translate(m, m, [-1, -1, 0]);
|
|
576
|
+
mat4.scale(m, m, [2 / this.width, 2 / this.height, 1]);
|
|
577
|
+
this.glCoordMatrix = m;
|
|
578
|
+
|
|
579
|
+
// Calculate the camera to sea-level distance in pixel in respect of terrain
|
|
580
|
+
const cameraToSeaLevelDistance = this.cameraToCenterDistance + this._elevation * this._pixelPerMeter / Math.cos(this._pitch);
|
|
581
|
+
// In case of negative minimum elevation (e.g. the dead see, under the sea maps) use a lower plane for calculation
|
|
582
|
+
const minElevation = Math.min(this.elevation, this.minElevationForCurrentTile);
|
|
583
|
+
const cameraToLowestPointDistance = cameraToSeaLevelDistance - minElevation * this._pixelPerMeter / Math.cos(this._pitch);
|
|
584
|
+
const lowestPlane = minElevation < 0 ? cameraToLowestPointDistance : cameraToSeaLevelDistance;
|
|
585
|
+
|
|
586
|
+
// Find the distance from the center point [width/2 + offset.x, height/2 + offset.y] to the
|
|
587
|
+
// center top point [width/2 + offset.x, 0] in Z units, using the law of sines.
|
|
588
|
+
// 1 Z unit is equivalent to 1 horizontal px at the center of the map
|
|
589
|
+
// (the distance between[width/2, height/2] and [width/2 + 1, height/2])
|
|
590
|
+
const groundAngle = Math.PI / 2 + this._pitch;
|
|
591
|
+
const fovAboveCenter = this._fov * (0.5 + offset.y / this.height);
|
|
592
|
+
const topHalfSurfaceDistance = Math.sin(fovAboveCenter) * lowestPlane / Math.sin(clamp(Math.PI - groundAngle - fovAboveCenter, 0.01, Math.PI - 0.01));
|
|
593
|
+
|
|
594
|
+
// Find the distance from the center point to the horizon
|
|
595
|
+
const horizon = this.getHorizon();
|
|
596
|
+
const horizonAngle = Math.atan(horizon / this.cameraToCenterDistance);
|
|
597
|
+
const fovCenterToHorizon = 2 * horizonAngle * (0.5 + offset.y / (horizon * 2));
|
|
598
|
+
const topHalfSurfaceDistanceHorizon = Math.sin(fovCenterToHorizon) * lowestPlane / Math.sin(clamp(Math.PI - groundAngle - fovCenterToHorizon, 0.01, Math.PI - 0.01));
|
|
599
|
+
|
|
600
|
+
// Calculate z distance of the farthest fragment that should be rendered.
|
|
601
|
+
// Add a bit extra to avoid precision problems when a fragment's distance is exactly `furthestDistance`
|
|
602
|
+
const topHalfMinDistance = Math.min(topHalfSurfaceDistance, topHalfSurfaceDistanceHorizon);
|
|
603
|
+
const farZ = (Math.cos(Math.PI / 2 - this._pitch) * topHalfMinDistance + lowestPlane) * 1.01;
|
|
604
|
+
|
|
605
|
+
// The larger the value of nearZ is
|
|
606
|
+
// - the more depth precision is available for features (good)
|
|
607
|
+
// - clipping starts appearing sooner when the camera is close to 3d features (bad)
|
|
608
|
+
//
|
|
609
|
+
// Other values work for mapbox-gl-js but deckgl was encountering precision issues
|
|
610
|
+
// when rendering custom layers. This value was experimentally chosen and
|
|
611
|
+
// seems to solve z-fighting issues in deckgl while not clipping buildings too close to the camera.
|
|
612
|
+
const nearZ = this.height / 50;
|
|
613
|
+
|
|
614
|
+
// matrix for conversion from location to clip space(-1 .. 1)
|
|
615
|
+
m = new Float64Array(16);
|
|
616
|
+
mat4.perspective(m, this._fov, this.width / this.height, nearZ, farZ);
|
|
617
|
+
|
|
618
|
+
// Apply center of perspective offset
|
|
619
|
+
m[8] = -offset.x * 2 / this.width;
|
|
620
|
+
m[9] = offset.y * 2 / this.height;
|
|
621
|
+
mat4.scale(m, m, [1, -1, 1]);
|
|
622
|
+
mat4.translate(m, m, [0, 0, -this.cameraToCenterDistance]);
|
|
623
|
+
mat4.rotateX(m, m, this._pitch);
|
|
624
|
+
mat4.rotateZ(m, m, this.angle);
|
|
625
|
+
mat4.translate(m, m, [-x, -y, 0]);
|
|
626
|
+
|
|
627
|
+
// The mercatorMatrix can be used to transform points from mercator coordinates
|
|
628
|
+
// ([0, 0] nw, [1, 1] se) to clip space.
|
|
629
|
+
this.mercatorMatrix = mat4.scale([], m, [this.worldSize, this.worldSize, this.worldSize]);
|
|
630
|
+
|
|
631
|
+
// scale vertically to meters per pixel (inverse of ground resolution):
|
|
632
|
+
mat4.scale(m, m, [1, 1, this._pixelPerMeter]);
|
|
633
|
+
|
|
634
|
+
// matrix for conversion from world space to screen coordinates in 2D
|
|
635
|
+
this.pixelMatrix = mat4.multiply(new Float64Array(16), this.labelPlaneMatrix, m);
|
|
636
|
+
|
|
637
|
+
// matrix for conversion from world space to clip space (-1 .. 1)
|
|
638
|
+
mat4.translate(m, m, [0, 0, -this.elevation]); // elevate camera over terrain
|
|
639
|
+
this.projMatrix = m;
|
|
640
|
+
this.invProjMatrix = mat4.invert([], m);
|
|
641
|
+
|
|
642
|
+
// matrix for conversion from world space to screen coordinates in 3D
|
|
643
|
+
this.pixelMatrix3D = mat4.multiply(new Float64Array(16), this.labelPlaneMatrix, m);
|
|
644
|
+
|
|
645
|
+
// Make a second projection matrix that is aligned to a pixel grid for rendering raster tiles.
|
|
646
|
+
// We're rounding the (floating point) x/y values to achieve to avoid rendering raster images to fractional
|
|
647
|
+
// coordinates. Additionally, we adjust by half a pixel in either direction in case that viewport dimension
|
|
648
|
+
// is an odd integer to preserve rendering to the pixel grid. We're rotating this shift based on the angle
|
|
649
|
+
// of the transformation so that 0°, 90°, 180°, and 270° rasters are crisp, and adjust the shift so that
|
|
650
|
+
// it is always <= 0.5 pixels.
|
|
651
|
+
const xShift = this.width % 2 / 2,
|
|
652
|
+
yShift = this.height % 2 / 2,
|
|
653
|
+
angleCos = Math.cos(this.angle),
|
|
654
|
+
angleSin = Math.sin(this.angle),
|
|
655
|
+
dx = x - Math.round(x) + angleCos * xShift + angleSin * yShift,
|
|
656
|
+
dy = y - Math.round(y) + angleCos * yShift + angleSin * xShift;
|
|
657
|
+
const alignedM = new Float64Array(m);
|
|
658
|
+
mat4.translate(alignedM, alignedM, [dx > 0.5 ? dx - 1 : dx, dy > 0.5 ? dy - 1 : dy, 0]);
|
|
659
|
+
this.alignedProjMatrix = alignedM;
|
|
660
|
+
|
|
661
|
+
// inverse matrix for conversion from screen coordinates to location
|
|
662
|
+
m = mat4.invert(new Float64Array(16), this.pixelMatrix);
|
|
663
|
+
if (!m) throw new Error('failed to invert matrix');
|
|
664
|
+
this.pixelMatrixInverse = m;
|
|
665
|
+
this._posMatrixCache = {};
|
|
666
|
+
this._alignedPosMatrixCache = {};
|
|
667
|
+
}
|
|
668
|
+
maxPitchScaleFactor() {
|
|
669
|
+
// calcMatrices hasn't run yet
|
|
670
|
+
if (!this.pixelMatrixInverse) return 1;
|
|
671
|
+
const coord = this.pointCoordinate(new Point(0, 0));
|
|
672
|
+
const p = [coord.x * this.worldSize, coord.y * this.worldSize, 0, 1];
|
|
673
|
+
const topPoint = vec4.transformMat4(p, p, this.pixelMatrix);
|
|
674
|
+
return topPoint[3] / this.cameraToCenterDistance;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* The camera looks at the map from a 3D (lng, lat, altitude) location. Let's use `cameraLocation`
|
|
679
|
+
* as the name for the location under the camera and on the surface of the earth (lng, lat, 0).
|
|
680
|
+
* `cameraPoint` is the projected position of the `cameraLocation`.
|
|
681
|
+
*
|
|
682
|
+
* This point is useful to us because only fill-extrusions that are between `cameraPoint` and
|
|
683
|
+
* the query point on the surface of the earth can extend and intersect the query.
|
|
684
|
+
*
|
|
685
|
+
* When the map is not pitched the `cameraPoint` is equivalent to the center of the map because
|
|
686
|
+
* the camera is right above the center of the map.
|
|
687
|
+
*/
|
|
688
|
+
getCameraPoint() {
|
|
689
|
+
const pitch = this._pitch;
|
|
690
|
+
const yOffset = Math.tan(pitch) * (this.cameraToCenterDistance || 1);
|
|
691
|
+
return this.centerPoint.add(new Point(0, yOffset));
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* When the map is pitched, some of the 3D features that intersect a query will not intersect
|
|
696
|
+
* the query at the surface of the earth. Instead the feature may be closer and only intersect
|
|
697
|
+
* the query because it extrudes into the air.
|
|
698
|
+
* @param queryGeometry - For point queries, the line from the query point to the "camera point",
|
|
699
|
+
* for other geometries, the envelope of the query geometry and the "camera point"
|
|
700
|
+
* @returns a geometry that includes all of the original query as well as all possible ares of the
|
|
701
|
+
* screen where the *base* of a visible extrusion could be.
|
|
702
|
+
*
|
|
703
|
+
*/
|
|
704
|
+
getCameraQueryGeometry(queryGeometry) {
|
|
705
|
+
const c = this.getCameraPoint();
|
|
706
|
+
if (queryGeometry.length === 1) {
|
|
707
|
+
return [queryGeometry[0], c];
|
|
708
|
+
} else {
|
|
709
|
+
let minX = c.x;
|
|
710
|
+
let minY = c.y;
|
|
711
|
+
let maxX = c.x;
|
|
712
|
+
let maxY = c.y;
|
|
713
|
+
for (const p of queryGeometry) {
|
|
714
|
+
minX = Math.min(minX, p.x);
|
|
715
|
+
minY = Math.min(minY, p.y);
|
|
716
|
+
maxX = Math.max(maxX, p.x);
|
|
717
|
+
maxY = Math.max(maxY, p.y);
|
|
718
|
+
}
|
|
719
|
+
return [new Point(minX, minY), new Point(maxX, minY), new Point(maxX, maxY), new Point(minX, maxY), new Point(minX, minY)];
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Return the distance to the camera in clip space from a LngLat.
|
|
724
|
+
* This can be compared to the value from the depth buffer (terrain.depthAtPoint)
|
|
725
|
+
* to determine whether a point is occluded.
|
|
726
|
+
* @param lngLat - the point
|
|
727
|
+
* @param elevation - the point's elevation
|
|
728
|
+
* @returns depth value in clip space (between 0 and 1)
|
|
729
|
+
*/
|
|
730
|
+
lngLatToCameraDepth(lngLat, elevation) {
|
|
731
|
+
const coord = this.locationCoordinate(lngLat);
|
|
732
|
+
const p = [coord.x * this.worldSize, coord.y * this.worldSize, elevation, 1];
|
|
733
|
+
vec4.transformMat4(p, p, this.projMatrix);
|
|
734
|
+
return p[2] / p[3];
|
|
735
|
+
}
|
|
736
|
+
}
|