-
Notifications
You must be signed in to change notification settings - Fork 107
/
MapView.ts
280 lines (244 loc) · 7.19 KB
/
MapView.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
import {BufferGeometry, Camera, Group, Material, Mesh, MeshBasicMaterial, Object3D, Raycaster, Scene, WebGLRenderer} from 'three';
import {OpenStreetMapsProvider} from './providers/OpenStreetMapsProvider';
import {MapNode} from './nodes/MapNode';
import {MapHeightNode} from './nodes/MapHeightNode';
import {MapPlaneNode} from './nodes/MapPlaneNode';
import {MapSphereNode} from './nodes/MapSphereNode';
import {MapHeightNodeShader} from './nodes/MapHeightNodeShader';
import {LODRaycast} from './lod/LODRaycast';
import {MapProvider} from './providers/MapProvider';
import {LODControl} from './lod/LODControl';
import {MapMartiniHeightNode} from './nodes/MapMartiniHeightNode';
/**
* Map viewer is used to read and display map tiles from a server.
*
* It was designed to work with a OpenMapTiles but can also be used with another map tiles.
*
* The map is drawn in plane map nodes using a quad tree that is subdivided as necessary to guaratee good map quality.
*/
export class MapView extends Mesh
{
/**
* Planar map projection.
*/
public static PLANAR: number = 200;
/**
* Spherical map projection.
*/
public static SPHERICAL: number = 201;
/**
* Planar map projection with height deformation.
*/
public static HEIGHT: number = 202;
/**
* Planar map projection with height deformation using the GPU for height generation.
*/
public static HEIGHT_SHADER: number = 203;
/**
* RTIN map mode.
*/
public static MARTINI: number = 204;
/**
* Map of the map node types available.
*/
public static mapModes: Map<number, any> = new Map<number, any>([
[MapView.PLANAR, MapPlaneNode],
[MapView.SPHERICAL, MapSphereNode],
[MapView.HEIGHT, MapHeightNode],
[MapView.HEIGHT_SHADER, MapHeightNodeShader],
[MapView.MARTINI, MapMartiniHeightNode]
]);
/**
* LOD control object used to defined how tiles are loaded in and out of memory.
*/
public lod: LODControl = null;
/**
* Map tile color layer provider.
*/
public provider: MapProvider = null;
/**
* Map height (terrain elevation) layer provider.
*
* Only used for HEIGHT, HEIGHT_SHADER and MARTINI map modes.
*/
public heightProvider: MapProvider = null;
/**
* Define the type of map node in use, defined how the map is presented.
*
* Should only be set on creation.
*/
public root: MapNode = null;
/**
* Indicate if the nodes should cache its children when it is simplified. Nodes that are no longer in use should be kept in memory.
*
* Usefull for fast moving scenarios to prevent reparsing data in fast moving scenes.
*
* Should only be used if the child generation process is time consuming. Should be kept off unless required.
*/
public cacheTiles: boolean = false;
/**
* Constructor for the map view objects.
*
* @param root - Map view node modes can be SPHERICAL, HEIGHT or PLANAR. PLANAR is used by default. Can also be a custom MapNode instance.
* @param provider - Map color tile provider by default a OSM maps provider is used if none specified.
* @param heightProvider - Map height tile provider, by default no height provider is used.
*/
public constructor(root: (number | MapNode) = MapView.PLANAR, provider: MapProvider = new OpenStreetMapsProvider(), heightProvider: MapProvider = null)
{
super(undefined, new MeshBasicMaterial({transparent: true, opacity: 0.0}));
this.lod = new LODRaycast();
this.provider = provider;
this.heightProvider = heightProvider;
this.setRoot(root);
this.preSubdivide();
}
/**
* Ajust node configuration depending on the camera distance.
*
* Called everytime automatically before render by the renderer.
*/
public onBeforeRender: (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group)=> void = (renderer, scene, camera, geometry, material, group) =>
{
this.lod.updateLOD(this, camera, renderer, scene);
};
/**
* Set the root of the map view.
*
* Is set by the constructor by default, can be changed in runtime.
*
* @param root - Map node to be used as root.
*/
public setRoot(root: (MapNode | number)): void
{
if (typeof root === 'number')
{
if (!MapView.mapModes.has(root))
{
throw new Error('Map mode ' + root + ' does is not registered.');
}
const rootConstructor = MapView.mapModes.get(root);
// @ts-ignore
root = new rootConstructor(null, this);
}
// Remove old root
if (this.root !== null)
{
this.remove(this.root);
this.root = null;
}
// @ts-ignore
this.root = root;
// Initialize root node
if (this.root !== null)
{
// @ts-ignore
this.geometry = this.root.constructor.baseGeometry;
// @ts-ignore
this.scale.copy(this.root.constructor.baseScale);
this.root.mapView = this;
this.add(this.root);
this.root.initialize();
}
}
/**
* Pre-subdivide map tree to create nodes of levels not available in the provider.
*
* Checks for the minimum zoom level in the providers attached to the map view.
*/
public preSubdivide(): void
{
function subdivide(node: MapNode, depth: number): void {
if (depth <= 0) {
return;
}
node.subdivide();
for(let i = 0; i < node.children.length; i++) {
if (node.children[i] instanceof MapNode) {
const child = node.children[i] as MapNode;
subdivide(child, depth - 1);
}
}
}
const minZoom = Math.max(this.provider.minZoom, this.heightProvider?.minZoom ?? -Infinity);
if (minZoom > 0) {
subdivide(this.root, minZoom);
}
}
/**
* Change the map provider of this map view.
*
* Will discard all the tiles already loaded using the old provider.
*/
public setProvider(provider: MapProvider): void
{
if (provider !== this.provider)
{
this.provider = provider;
this.clear();
}
}
/**
* Change the map height provider of this map view.
*
* Will discard all the tiles already loaded using the old provider.
*/
public setHeightProvider(heightProvider: MapProvider): void
{
if (heightProvider !== this.heightProvider)
{
this.heightProvider = heightProvider;
this.clear();
}
}
/**
* Clears all tiles from memory and reloads data. Used when changing the provider.
*
* Should be called manually if any changed to the provider are made without setting the provider.
*/
public clear(): any
{
this.traverse(function(children: Object3D): void
{
// @ts-ignore
if (children.childrenCache)
{
// @ts-ignore
children.childrenCache = null;
}
// @ts-ignore
if (children.initialize)
{
// @ts-ignore
children.initialize();
}
});
return this;
}
/**
* Get the minimum zoom level available in the providers attached to the map view.
*
* @returns Minimum zoom level available.
*/
public minZoom(): number {
return Math.max(this.provider.minZoom, this.heightProvider?.minZoom ?? -Infinity);
}
/**
* Get the maximum zoom level available in the providers attached to the map view.
*
* @returns Maximum zoom level available.
*/
public maxZoom(): number {
return Math.min(this.provider.maxZoom, this.heightProvider?.maxZoom ?? Infinity);
}
/**
* Get map meta data from server if supported.
*/
public getMetaData(): void
{
this.provider.getMetaData();
}
public raycast(raycaster: Raycaster, intersects: any[]): boolean
{
return false;
}
}