itowns 2.45.1-next.0 → 2.45.1-next.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 (185) hide show
  1. package/dist/455.js +2 -0
  2. package/dist/455.js.map +1 -0
  3. package/dist/debug.js +3 -0
  4. package/dist/debug.js.LICENSE.txt +13 -0
  5. package/dist/debug.js.map +1 -0
  6. package/dist/itowns.js +3 -0
  7. package/dist/itowns.js.LICENSE.txt +5 -0
  8. package/dist/itowns.js.map +1 -0
  9. package/dist/itowns_lasparser.js +2 -0
  10. package/dist/itowns_lasparser.js.map +1 -0
  11. package/dist/itowns_lasworker.js +2 -0
  12. package/dist/itowns_lasworker.js.map +1 -0
  13. package/dist/itowns_potree2worker.js +2 -0
  14. package/dist/itowns_potree2worker.js.map +1 -0
  15. package/dist/itowns_widgets.js +2 -0
  16. package/dist/itowns_widgets.js.map +1 -0
  17. package/lib/Controls/FirstPersonControls.js +308 -0
  18. package/lib/Controls/FlyControls.js +175 -0
  19. package/lib/Controls/GlobeControls.js +1178 -0
  20. package/lib/Controls/PlanarControls.js +1025 -0
  21. package/lib/Controls/StateControl.js +432 -0
  22. package/lib/Controls/StreetControls.js +392 -0
  23. package/lib/Converter/Feature2Mesh.js +612 -0
  24. package/lib/Converter/Feature2Texture.js +174 -0
  25. package/lib/Converter/convertToTile.js +70 -0
  26. package/lib/Converter/textureConverter.js +43 -0
  27. package/lib/Core/3DTiles/C3DTBatchTable.js +131 -0
  28. package/lib/Core/3DTiles/C3DTBatchTableHierarchyExtension.js +96 -0
  29. package/lib/Core/3DTiles/C3DTBoundingVolume.js +156 -0
  30. package/lib/Core/3DTiles/C3DTExtensions.js +97 -0
  31. package/lib/Core/3DTiles/C3DTFeature.js +110 -0
  32. package/lib/Core/3DTiles/C3DTilesEnums.js +20 -0
  33. package/lib/Core/3DTiles/C3DTileset.js +99 -0
  34. package/lib/Core/3DTiles/utils/BinaryPropertyAccessor.js +100 -0
  35. package/lib/Core/AnimationPlayer.js +142 -0
  36. package/lib/Core/CopcNode.js +174 -0
  37. package/lib/Core/Deprecated/Undeprecator.js +74 -0
  38. package/lib/Core/EntwinePointTileNode.js +126 -0
  39. package/lib/Core/Feature.js +488 -0
  40. package/lib/Core/Geographic/GeoidGrid.js +108 -0
  41. package/lib/Core/Label.js +222 -0
  42. package/lib/Core/MainLoop.js +209 -0
  43. package/lib/Core/Picking.js +255 -0
  44. package/lib/Core/PointCloudNode.js +42 -0
  45. package/lib/Core/Potree2Node.js +206 -0
  46. package/lib/Core/Potree2PointAttributes.js +139 -0
  47. package/lib/Core/PotreeNode.js +101 -0
  48. package/lib/Core/Prefab/Globe/Atmosphere.js +293 -0
  49. package/lib/Core/Prefab/Globe/GlobeLayer.js +152 -0
  50. package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +110 -0
  51. package/lib/Core/Prefab/Globe/SkyShader.js +78 -0
  52. package/lib/Core/Prefab/GlobeView.js +155 -0
  53. package/lib/Core/Prefab/Planar/PlanarLayer.js +59 -0
  54. package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +71 -0
  55. package/lib/Core/Prefab/PlanarView.js +62 -0
  56. package/lib/Core/Prefab/TileBuilder.js +82 -0
  57. package/lib/Core/Prefab/computeBufferTileGeometry.js +248 -0
  58. package/lib/Core/Scheduler/Cache.js +17 -0
  59. package/lib/Core/Scheduler/CancelledCommandException.js +15 -0
  60. package/lib/Core/Scheduler/Scheduler.js +294 -0
  61. package/lib/Core/Style.js +660 -0
  62. package/lib/Core/StyleOptions.js +486 -0
  63. package/lib/Core/System/Capabilities.js +63 -0
  64. package/lib/Core/Tile/Tile.js +205 -0
  65. package/lib/Core/Tile/TileGrid.js +49 -0
  66. package/lib/Core/TileGeometry.js +124 -0
  67. package/lib/Core/TileMesh.js +108 -0
  68. package/lib/Core/View.js +1115 -0
  69. package/lib/Layer/C3DTilesLayer.js +459 -0
  70. package/lib/Layer/ColorLayer.js +154 -0
  71. package/lib/Layer/CopcLayer.js +63 -0
  72. package/lib/Layer/ElevationLayer.js +139 -0
  73. package/lib/Layer/EntwinePointTileLayer.js +71 -0
  74. package/lib/Layer/FeatureGeometryLayer.js +77 -0
  75. package/lib/Layer/GeoidLayer.js +80 -0
  76. package/lib/Layer/GeometryLayer.js +233 -0
  77. package/lib/Layer/InfoLayer.js +64 -0
  78. package/lib/Layer/LabelLayer.js +469 -0
  79. package/lib/Layer/Layer.js +335 -0
  80. package/lib/Layer/LayerUpdateState.js +89 -0
  81. package/lib/Layer/LayerUpdateStrategy.js +80 -0
  82. package/lib/Layer/OGC3DTilesLayer.js +543 -0
  83. package/lib/Layer/OrientedImageLayer.js +227 -0
  84. package/lib/Layer/PointCloudLayer.js +405 -0
  85. package/lib/Layer/Potree2Layer.js +171 -0
  86. package/lib/Layer/PotreeLayer.js +72 -0
  87. package/lib/Layer/RasterLayer.js +37 -0
  88. package/lib/Layer/ReferencingLayerProperties.js +62 -0
  89. package/lib/Layer/TiledGeometryLayer.js +459 -0
  90. package/lib/Loader/LASLoader.js +193 -0
  91. package/lib/Loader/Potree2BrotliLoader.js +261 -0
  92. package/lib/Loader/Potree2Loader.js +207 -0
  93. package/lib/Main.js +113 -0
  94. package/lib/MainBundle.js +4 -0
  95. package/lib/Parser/B3dmParser.js +174 -0
  96. package/lib/Parser/CameraCalibrationParser.js +94 -0
  97. package/lib/Parser/GDFParser.js +72 -0
  98. package/lib/Parser/GTXParser.js +75 -0
  99. package/lib/Parser/GeoJsonParser.js +212 -0
  100. package/lib/Parser/GpxParser.js +25 -0
  101. package/lib/Parser/ISGParser.js +71 -0
  102. package/lib/Parser/KMLParser.js +25 -0
  103. package/lib/Parser/LASParser.js +137 -0
  104. package/lib/Parser/MapBoxUrlParser.js +83 -0
  105. package/lib/Parser/PntsParser.js +131 -0
  106. package/lib/Parser/Potree2BinParser.js +92 -0
  107. package/lib/Parser/PotreeBinParser.js +106 -0
  108. package/lib/Parser/PotreeCinParser.js +29 -0
  109. package/lib/Parser/ShapefileParser.js +78 -0
  110. package/lib/Parser/VectorTileParser.js +215 -0
  111. package/lib/Parser/XbilParser.js +120 -0
  112. package/lib/Parser/deprecated/LegacyGLTFLoader.js +1386 -0
  113. package/lib/Parser/iGLTFLoader.js +168 -0
  114. package/lib/Process/3dTilesProcessing.js +304 -0
  115. package/lib/Process/FeatureProcessing.js +76 -0
  116. package/lib/Process/LayeredMaterialNodeProcessing.js +229 -0
  117. package/lib/Process/ObjectRemovalHelper.js +97 -0
  118. package/lib/Process/handlerNodeError.js +23 -0
  119. package/lib/Provider/3dTilesProvider.js +149 -0
  120. package/lib/Provider/DataSourceProvider.js +24 -0
  121. package/lib/Provider/Fetcher.js +233 -0
  122. package/lib/Provider/PointCloudProvider.js +45 -0
  123. package/lib/Provider/TileProvider.js +16 -0
  124. package/lib/Provider/URLBuilder.js +116 -0
  125. package/lib/Renderer/Camera.js +281 -0
  126. package/lib/Renderer/Color.js +56 -0
  127. package/lib/Renderer/ColorLayersOrdering.js +115 -0
  128. package/lib/Renderer/CommonMaterial.js +31 -0
  129. package/lib/Renderer/Label2DRenderer.js +192 -0
  130. package/lib/Renderer/LayeredMaterial.js +243 -0
  131. package/lib/Renderer/OBB.js +150 -0
  132. package/lib/Renderer/OrientedImageCamera.js +118 -0
  133. package/lib/Renderer/OrientedImageMaterial.js +167 -0
  134. package/lib/Renderer/PointsMaterial.js +485 -0
  135. package/lib/Renderer/RasterTile.js +243 -0
  136. package/lib/Renderer/RenderMode.js +31 -0
  137. package/lib/Renderer/Shader/ShaderChunk.js +160 -0
  138. package/lib/Renderer/Shader/ShaderUtils.js +47 -0
  139. package/lib/Renderer/SphereHelper.js +17 -0
  140. package/lib/Renderer/WebXR.js +51 -0
  141. package/lib/Renderer/c3DEngine.js +214 -0
  142. package/lib/Source/C3DTilesGoogleSource.js +74 -0
  143. package/lib/Source/C3DTilesIonSource.js +54 -0
  144. package/lib/Source/C3DTilesSource.js +30 -0
  145. package/lib/Source/CopcSource.js +126 -0
  146. package/lib/Source/EntwinePointTileSource.js +72 -0
  147. package/lib/Source/FileSource.js +188 -0
  148. package/lib/Source/OGC3DTilesGoogleSource.js +29 -0
  149. package/lib/Source/OGC3DTilesIonSource.js +34 -0
  150. package/lib/Source/OGC3DTilesSource.js +21 -0
  151. package/lib/Source/OrientedImageSource.js +59 -0
  152. package/lib/Source/Potree2Source.js +167 -0
  153. package/lib/Source/PotreeSource.js +82 -0
  154. package/lib/Source/Source.js +202 -0
  155. package/lib/Source/TMSSource.js +144 -0
  156. package/lib/Source/VectorTilesSource.js +182 -0
  157. package/lib/Source/WFSSource.js +170 -0
  158. package/lib/Source/WMSSource.js +167 -0
  159. package/lib/Source/WMTSSource.js +92 -0
  160. package/lib/ThreeExtended/capabilities/WebGL.js +69 -0
  161. package/lib/ThreeExtended/libs/ktx-parse.module.js +506 -0
  162. package/lib/ThreeExtended/libs/zstddec.module.js +29 -0
  163. package/lib/ThreeExtended/loaders/DDSLoader.js +200 -0
  164. package/lib/ThreeExtended/loaders/DRACOLoader.js +400 -0
  165. package/lib/ThreeExtended/loaders/GLTFLoader.js +2879 -0
  166. package/lib/ThreeExtended/loaders/KTX2Loader.js +709 -0
  167. package/lib/ThreeExtended/math/ColorSpaces.js +59 -0
  168. package/lib/ThreeExtended/utils/BufferGeometryUtils.js +846 -0
  169. package/lib/ThreeExtended/utils/WorkerPool.js +70 -0
  170. package/lib/Utils/CameraUtils.js +554 -0
  171. package/lib/Utils/DEMUtils.js +350 -0
  172. package/lib/Utils/FeaturesUtils.js +156 -0
  173. package/lib/Utils/Gradients.js +16 -0
  174. package/lib/Utils/ThreeUtils.js +115 -0
  175. package/lib/Utils/gui/C3DTilesStyle.js +218 -0
  176. package/lib/Utils/gui/Main.js +7 -0
  177. package/lib/Utils/gui/Minimap.js +152 -0
  178. package/lib/Utils/gui/Navigation.js +245 -0
  179. package/lib/Utils/gui/Scale.js +104 -0
  180. package/lib/Utils/gui/Searchbar.js +234 -0
  181. package/lib/Utils/gui/Widget.js +80 -0
  182. package/lib/Utils/placeObjectOnGround.js +136 -0
  183. package/lib/Worker/LASLoaderWorker.js +19 -0
  184. package/lib/Worker/Potree2Worker.js +21 -0
  185. package/package.json +2 -2
@@ -0,0 +1,218 @@
1
+ import { C3DTILES_LAYER_EVENTS } from 'itowns';
2
+ import Widget from "./Widget.js";
3
+ const DEFAULT_OPTIONS = {
4
+ width: 200,
5
+ position: 'top-right'
6
+ };
7
+
8
+ /**
9
+ * A widget for dynamic 3DTiles styling
10
+ *
11
+ * To use it, you need to link the widgets' stylesheet to your html webpage. This stylesheet is included in
12
+ * [itowns bundles](https://github.com/iTowns/itowns/releases) if you downloaded them, or it can be found in
13
+ * `node_modules/itowns/examples/css` if you installed iTowns with npm. Otherwise, it can be found at
14
+ * [this link](https://raw.githubusercontent.com/iTowns/itowns/master/examples/css/widgets.css). See
15
+ * [this example](http://www.itowns-project.org/itowns/examples/#widgets_3dtiles_style) for more details.
16
+ *
17
+ * @extends Widget
18
+ *
19
+ * @property {HTMLElement} domElement An html div containing the minimap.
20
+ * @property {HTMLElement} parentElement The parent HTML container of `this.domElement`.
21
+ */
22
+ class C3DTilesStyle extends Widget {
23
+ /**
24
+ *
25
+ * @param {View} view view
26
+ * @param {*} options options
27
+ */
28
+ constructor(view, options) {
29
+ super(view, options, DEFAULT_OPTIONS);
30
+ this.domElement.onclick = event => event.stopImmediatePropagation();
31
+
32
+ // create select of the C3DTilesLayers
33
+ const selectC3DTilesLayer = document.createElement('select');
34
+ this.domElement.appendChild(selectC3DTilesLayer);
35
+
36
+ /** @type {Map<HTMLElement, HTMLElement>} */
37
+ const selectOptionLayerContent = new Map();
38
+ const updateSelectedLayer = () => {
39
+ for (const [sO, lC] of selectOptionLayerContent) {
40
+ lC.hidden = sO !== selectC3DTilesLayer.selectedOptions[0];
41
+ }
42
+ };
43
+ selectC3DTilesLayer.onchange = updateSelectedLayer;
44
+ view.getLayers().filter(el => el.isC3DTilesLayer === true).forEach(c3DTilesLayer => {
45
+ const selectC3DTilesLayerOption = document.createElement('option');
46
+ selectC3DTilesLayerOption.innerText = c3DTilesLayer.name;
47
+ selectC3DTilesLayer.add(selectC3DTilesLayerOption);
48
+ const layerContent = document.createElement('div');
49
+ this.domElement.appendChild(layerContent);
50
+
51
+ // link select option to layer content
52
+ selectOptionLayerContent.set(selectC3DTilesLayerOption, layerContent);
53
+
54
+ // wait for C3DTileFeatures to load
55
+ c3DTilesLayer.addEventListener(C3DTILES_LAYER_EVENTS.ON_TILE_CONTENT_LOADED, () => {
56
+ // reset
57
+ while (layerContent.firstChild) {
58
+ layerContent.firstChild.remove();
59
+ }
60
+
61
+ /** @type {Map<string,Array>} */
62
+ const buffer = new Map(); // record what are the possible values for a key in batchTable
63
+ for (const [, tileC3DTileFeatures] of c3DTilesLayer.tilesC3DTileFeatures) {
64
+ for (const [, c3DTileFeature] of tileC3DTileFeatures) {
65
+ // eslint-disable-next-line guard-for-in
66
+ for (const key in c3DTileFeature.getInfo().batchTable) {
67
+ if (!buffer.has(key)) {
68
+ buffer.set(key, []);
69
+ }
70
+
71
+ // check possible value for this key
72
+ const value = c3DTileFeature.getInfo().batchTable[key];
73
+ if (!buffer.get(key).includes(value)) {
74
+ buffer.get(key).push(value);
75
+ }
76
+ }
77
+ }
78
+ }
79
+
80
+ /** @type {Map<HTMLElement, Function>} */
81
+ const colorFunctions = new Map();
82
+ const fillColorFunction = c3DTileFeature => {
83
+ let result = null;
84
+ for (const [, colorFunction] of colorFunctions) {
85
+ result = colorFunction(c3DTileFeature) || result;
86
+ }
87
+ return result;
88
+ };
89
+
90
+ /** @type {Map<HTMLElement, Function>} */
91
+ const opacityFunctions = new Map();
92
+ const fillOpacityFunction = c3DTileFeature => {
93
+ let result = 1;
94
+ for (const [, opacityFunction] of opacityFunctions) {
95
+ result = opacityFunction(c3DTileFeature) || result;
96
+ }
97
+ return result;
98
+ };
99
+ const appendInputColorAndOpacity = (getKeyValue, key, possibleValues) => {
100
+ const inputColor = document.createElement('input');
101
+ inputColor.setAttribute('type', 'color');
102
+ layerContent.appendChild(inputColor);
103
+ inputColor.onchange = () => {
104
+ const keyValue = getKeyValue();
105
+ if (!possibleValues.includes(keyValue)) {
106
+ return;
107
+ }
108
+ const colorSelected = inputColor.value; // copy
109
+ colorFunctions.set(keyValue, c3DTileFeature => {
110
+ if (c3DTileFeature.getInfo().batchTable[key] == keyValue) {
111
+ return colorSelected;
112
+ }
113
+ return null;
114
+ });
115
+ c3DTilesLayer.updateStyle();
116
+ view.notifyChange();
117
+ };
118
+ const opacityElement = document.createElement('input');
119
+ opacityElement.setAttribute('type', 'range');
120
+ opacityElement.min = 0;
121
+ opacityElement.max = 1;
122
+ opacityElement.step = 0.1;
123
+ opacityElement.value = 1;
124
+ layerContent.appendChild(opacityElement);
125
+ opacityElement.onchange = () => {
126
+ const keyValue = getKeyValue();
127
+ if (!possibleValues.includes(keyValue)) {
128
+ return;
129
+ }
130
+ const opacitySelected = opacityElement.value; // copy
131
+ opacityFunctions.set(keyValue, c3DTileFeature => {
132
+ if (c3DTileFeature.getInfo().batchTable[key] == keyValue) {
133
+ return opacitySelected;
134
+ }
135
+ return null;
136
+ });
137
+ c3DTilesLayer.updateStyle();
138
+ view.notifyChange();
139
+ };
140
+ return {
141
+ inputColor,
142
+ opacityElement
143
+ };
144
+ };
145
+ const appendFilterSelect = (key, values) => {
146
+ const label = document.createElement('label');
147
+ label.innerText = key;
148
+ layerContent.appendChild(label);
149
+ const selectKey = document.createElement('select');
150
+ layerContent.appendChild(selectKey);
151
+ values.forEach(value => {
152
+ const selectKeyOption = document.createElement('option');
153
+ selectKeyOption.value = value;
154
+ selectKeyOption.text = value;
155
+ selectKey.add(selectKeyOption);
156
+ });
157
+ appendInputColorAndOpacity(() => selectKey.selectedOptions[0].value, key, values);
158
+ };
159
+ const appendFilterInputText = (key, values) => {
160
+ const label = document.createElement('label');
161
+ label.innerText = key;
162
+ layerContent.appendChild(label);
163
+ const inputText = document.createElement('input');
164
+ inputText.setAttribute('type', 'text');
165
+ layerContent.appendChild(inputText);
166
+ const {
167
+ inputColor,
168
+ opacityElement
169
+ } = appendInputColorAndOpacity(() => inputText.value, key, values);
170
+ inputText.onchange = () => {
171
+ if (!values.includes(inputText.value)) {
172
+ return;
173
+ }
174
+ const colorSelected = inputColor.value; // copy
175
+ const textSelected = inputText.value; // copy
176
+ colorFunctions.set(textSelected, c3DTileFeature => {
177
+ if (c3DTileFeature.getInfo().batchTable[key] == textSelected) {
178
+ return colorSelected;
179
+ }
180
+ return null;
181
+ });
182
+ const opacitySelected = opacityElement.value; // copy
183
+ opacityFunctions.set(textSelected, c3DTileFeature => {
184
+ if (c3DTileFeature.getInfo().batchTable[key] == textSelected) {
185
+ return opacitySelected;
186
+ }
187
+ return null;
188
+ });
189
+ c3DTilesLayer.updateStyle();
190
+ view.notifyChange();
191
+ };
192
+ };
193
+
194
+ // create ui from buffer
195
+ for (const [key, values] of buffer) {
196
+ if (values.length < C3DTilesStyle.MAX_SELECT_VALUE) {
197
+ appendFilterSelect(key, values);
198
+ } else {
199
+ appendFilterInputText(key, values);
200
+ }
201
+ }
202
+
203
+ // set style
204
+ c3DTilesLayer.style = {
205
+ fill: {
206
+ color: fillColorFunction,
207
+ opacity: fillOpacityFunction
208
+ }
209
+ };
210
+ });
211
+ });
212
+ updateSelectedLayer();
213
+ }
214
+ static get MAX_SELECT_VALUE() {
215
+ return 10;
216
+ }
217
+ }
218
+ export default C3DTilesStyle;
@@ -0,0 +1,7 @@
1
+ // eslint-disable-next-line import/prefer-default-export
2
+ export { default as Navigation } from "./Navigation.js";
3
+ export { default as Minimap } from "./Minimap.js";
4
+ export { default as Scale } from "./Scale.js";
5
+ export { default as Searchbar } from "./Searchbar.js";
6
+ export { default as Widget } from "./Widget.js";
7
+ export { default as C3DTilesStyle } from "./C3DTilesStyle.js";
@@ -0,0 +1,152 @@
1
+ import { Coordinates } from '@itowns/geographic';
2
+ import { MAIN_LOOP_EVENTS, PlanarView, CAMERA_TYPE } from 'itowns';
3
+ import Widget from "./Widget.js";
4
+ const DEFAULT_OPTIONS = {
5
+ minScale: 1 / 500000,
6
+ maxScale: 1 / 5E8,
7
+ zoomRatio: 1 / 30,
8
+ width: 150,
9
+ height: 150,
10
+ position: 'bottom-left'
11
+ };
12
+
13
+ /**
14
+ * A widget for minimap
15
+ *
16
+ * To use it, you need to link the widgets' stylesheet to your html webpage. This stylesheet is included in
17
+ * [itowns bundles](https://github.com/iTowns/itowns/releases) if you downloaded them, or it can be found in
18
+ * `node_modules/itowns/examples/css` if you installed iTowns with npm. Otherwise, it can be found at
19
+ * [this link](https://raw.githubusercontent.com/iTowns/itowns/master/examples/css/widgets.css). See
20
+ * [this example](http://www.itowns-project.org/itowns/examples/#widgets_minimap) for more details.
21
+ *
22
+ * @extends Widget
23
+ *
24
+ * @property {HTMLElement} domElement An html div containing the minimap.
25
+ * @property {HTMLElement} parentElement The parent HTML container of `this.domElement`.
26
+ */
27
+ class Minimap extends Widget {
28
+ /**
29
+ * @param {GlobeView} view The iTowns view the minimap should be
30
+ * linked to. Only {@link GlobeView} is
31
+ * supported at the moment.
32
+ * @param {ColorLayer} layer The {@link ColorLayer} that should be
33
+ * displayed on the minimap.
34
+ * @param {Object} [options] The minimap optional configuration.
35
+ * @param {HTMLElement} [options.parentElement=view.domElement] The parent HTML container of the div
36
+ * which contains minimap widgets.
37
+ * @param {number} [options.size] The size of the minimap. It is a number
38
+ * that describes both width and height
39
+ * in pixels of the minimap.
40
+ * @param {number} [options.width=150] The width in pixels of the minimap.
41
+ * @param {number} [options.height=150] The height in pixels of the minimap.
42
+ * @param {string} [options.position='bottom-left'] Defines which position within the
43
+ * `parentElement` the minimap should be
44
+ * displayed to. Possible values are
45
+ * `top`, `bottom`, `left`, `right`,
46
+ * `top-left`, `top-right`, `bottom-left`
47
+ * and `bottom-right`. If the input value
48
+ * does not match one of these, it will
49
+ * be defaulted to `bottom-left`.
50
+ * @param {Object} [options.translate] An optional translation of the minimap.
51
+ * @param {number} [options.translate.x=0] The minimap translation along the page
52
+ * x-axis.
53
+ * @param {number} [options.translate.y=0] The minimap translation along the page
54
+ * y-axis.
55
+ * @param {HTMLElement|string} [options.cursor] An html element or an HTML string
56
+ * describing a cursor showing minimap
57
+ * view camera target position at the
58
+ * center of the minimap.
59
+ * @param {number} [options.minScale=1/2000] The minimal scale the minimap can reach.
60
+ * @param {number} [options.maxScale=1/1_250_000] The maximal scale the minimap can reach.
61
+ * @param {number} [options.zoomRatio=1/30] The ratio between minimap camera zoom
62
+ * and view camera zoom.
63
+ * @param {number} [options.pitch=0.28] The screen pixel pitch, used to compute
64
+ * view and minimap scale.
65
+ */
66
+ constructor(view, layer) {
67
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
68
+ // ---------- BUILD PROPERTIES ACCORDING TO DEFAULT OPTIONS AND OPTIONS PASSED IN PARAMETERS : ----------
69
+
70
+ if (!view.isGlobeView) {
71
+ throw new Error('\'Minimap\' plugin only supports \'GlobeView\'. Therefore, the \'view\' parameter must be a ' + '\'GlobeView\'.');
72
+ }
73
+ if (!layer.isColorLayer) {
74
+ throw new Error('\'layer\' parameter form \'Minimap\' constructor should be a \'ColorLayer\'.');
75
+ }
76
+ super(view, options, DEFAULT_OPTIONS);
77
+ this.minScale = options.minScale || DEFAULT_OPTIONS.minScale;
78
+ this.maxScale = options.maxScale || DEFAULT_OPTIONS.maxScale;
79
+
80
+ // TODO : it could be interesting to be able to specify a method as zoomRatio parameter. This method could
81
+ // return a zoom ratio from the scale of the minimap.
82
+ this.zoomRatio = options.zoomRatio || DEFAULT_OPTIONS.zoomRatio;
83
+
84
+ // ---------- this.domElement SETTINGS SPECIFIC TO MINIMAP : ----------
85
+
86
+ this.domElement.id = 'widgets-minimap';
87
+
88
+ // Display a cursor at the center of the minimap, if requested.
89
+ if (options.cursor) {
90
+ // Wrap cursor domElement inside a div to center it in minimap.
91
+ const cursorWrapper = document.createElement('div');
92
+ cursorWrapper.id = 'cursor-wrapper';
93
+ this.domElement.appendChild(cursorWrapper);
94
+
95
+ // Add specified cursor to its wrapper.
96
+ if (typeof options.cursor === 'string') {
97
+ cursorWrapper.innerHTML = options.cursor;
98
+ } else if (options.cursor instanceof HTMLElement) {
99
+ cursorWrapper.appendChild(options.cursor);
100
+ }
101
+ }
102
+
103
+ // ---------- CREATE A MINIMAP View AND DISPLAY DATA PASSED IN Layer PARAMETER : ----------
104
+
105
+ this.view = new PlanarView(this.domElement, layer.source.extent, {
106
+ camera: {
107
+ type: CAMERA_TYPE.ORTHOGRAPHIC
108
+ },
109
+ placement: layer.source.extent,
110
+ // TODO : the default placement should be the view extent for ortho camera
111
+ noControls: true,
112
+ maxSubdivisionLevel: view.tileLayer.maxSubdivisionLevel,
113
+ disableFocusOnStart: true
114
+ });
115
+ this.view.addLayer(layer); // TODO : should this promise be returned by constructor so that user can use it ?
116
+
117
+ // Prevent the minimap domElement to get focus when clicked, and prevent click event to be propagated to the
118
+ // main view controls.
119
+ this.domElement.addEventListener('pointerdown', event => {
120
+ event.preventDefault();
121
+ });
122
+
123
+ // ---------- UPDATE MINIMAP VIEW WHEN UPDATING THE MAIN VIEW : ----------
124
+
125
+ // The minimal and maximal value the minimap camera3D zoom can reach in order to stay in the scale limits.
126
+ const initialScale = this.view.getScale(options.pitch);
127
+ const minZoom = this.view.camera3D.zoom * this.maxScale / initialScale;
128
+ const maxZoom = this.view.camera3D.zoom * this.minScale / initialScale;
129
+
130
+ // Coordinates used to transform position vectors from the main view CRS to the minimap view CRS.
131
+ const mainViewCoordinates = new Coordinates(view.referenceCrs);
132
+ const viewCoordinates = new Coordinates(this.view.referenceCrs);
133
+ const targetPosition = view.controls.getCameraTargetPosition();
134
+ view.addFrameRequester(MAIN_LOOP_EVENTS.AFTER_RENDER, () => {
135
+ // Update minimap camera zoom
136
+ const distance = view.camera3D.position.distanceTo(targetPosition);
137
+ const scale = view.getScaleFromDistance(options.pitch, distance);
138
+ this.view.camera3D.zoom = this.zoomRatio * maxZoom * scale / this.minScale;
139
+ this.view.camera3D.zoom = Math.min(Math.max(this.view.camera3D.zoom, minZoom), maxZoom);
140
+ this.view.camera3D.updateProjectionMatrix();
141
+
142
+ // Update minimap camera position.
143
+ mainViewCoordinates.setFromVector3(view.controls.getCameraTargetPosition());
144
+ mainViewCoordinates.as(this.view.referenceCrs, viewCoordinates);
145
+ this.view.camera3D.position.x = viewCoordinates.x;
146
+ this.view.camera3D.position.y = viewCoordinates.y;
147
+ this.view.camera3D.updateMatrixWorld(true);
148
+ this.view.notifyChange(this.view.camera3D);
149
+ });
150
+ }
151
+ }
152
+ export default Minimap;
@@ -0,0 +1,245 @@
1
+ import { VIEW_EVENTS } from 'itowns';
2
+ import Widget from "./Widget.js";
3
+ const DEFAULT_OPTIONS = {
4
+ displayCompass: true,
5
+ display3DToggle: true,
6
+ displayZoomIn: true,
7
+ displayZoomOut: true,
8
+ animationDuration: 500,
9
+ position: 'bottom-left',
10
+ direction: 'column'
11
+ };
12
+ const DEFAULT_BUTTONS = {
13
+ compass: {
14
+ id: 'compass',
15
+ content: '',
16
+ info: 'Rotate the camera to face North',
17
+ parentId: 'widgets'
18
+ },
19
+ toggle3D: {
20
+ id: '3d-button',
21
+ content: '3D',
22
+ info: 'Tilt the camera'
23
+ },
24
+ zoomIn: {
25
+ id: 'zoom-in-button',
26
+ content: '<span class="widget-zoom-button-logo"></span>',
27
+ info: 'Zoom in',
28
+ parentId: 'zoom-button-bar'
29
+ },
30
+ zoomOut: {
31
+ id: 'zoom-out-button',
32
+ content: '<span id="zoom-out-logo" class="widget-zoom-button-logo"></span>',
33
+ info: 'Zoom out',
34
+ parentId: 'zoom-button-bar'
35
+ }
36
+ };
37
+
38
+ /**
39
+ * A widget menu manager for navigation.
40
+ *
41
+ * To use it, you need to link the widgets' stylesheet to your html webpage. This stylesheet is included in
42
+ * [itowns bundles](https://github.com/iTowns/itowns/releases) if you downloaded them, or it can be found in
43
+ * `node_modules/itowns/examples/css` if you installed iTowns with npm. Otherwise, it can be found at
44
+ * [this link](https://raw.githubusercontent.com/iTowns/itowns/master/examples/css/widgets.css). See
45
+ * [this example](http://www.itowns-project.org/itowns/examples/#widgets_navigation) for more details.
46
+ *
47
+ * @extends Widget
48
+ *
49
+ * @property {HTMLElement} domElement An html div containing all navigation widgets.
50
+ * @property {HTMLElement} parentElement The parent HTML container of `this.domElement`.
51
+ * @property {HTMLButtonElement} compass The HTML button for the compass.
52
+ * @property {HTMLButtonElement} toggle3D The HTML button for the 3D/2D toggle button.
53
+ * @property {HTMLButtonElement} zoomIn The HTML button for the zoom-in button.
54
+ * @property {HTMLButtonElement} zoomOut The HTML button for the zoom-out button.
55
+ *
56
+ * @example
57
+ * // Create a Navigation widget in the bottom-right corner of an iTowns view domElement
58
+ * const navigation = new Navigation(view, { position: 'bottom-right' };
59
+ *
60
+ * // Change the tooltip for the compass :
61
+ * navigation.compass.title = 'new tooltip';
62
+ *
63
+ * // Change the method ran when clicking zoom-in button :
64
+ * function newMethod() {
65
+ * // do something
66
+ * }
67
+ * navigation.zoomIn.onclick = newMethod;
68
+ */
69
+ class Navigation extends Widget {
70
+ #_view;
71
+ #_action(params) {
72
+ params.time = this.animationDuration;
73
+ return this.#_view.controls.lookAtCoordinate(params);
74
+ }
75
+ #_addDefaultButton(settings, onclick) {
76
+ return this.addButton(settings.id, settings.content, settings.info, onclick, settings.parentId);
77
+ }
78
+
79
+ /**
80
+ * @param {GlobeView} view The iTowns view the navigation should be linked
81
+ * to. For the moment, only `{@link GlobeView}`
82
+ * is supported.
83
+ * @param {Object} options The navigation menu optional configuration.
84
+ * @param {HTMLElement} [options.parentElement=view.domElement] The parent HTML container of the div which
85
+ * contains navigation widgets.
86
+ * @param {boolean} [options.displayCompass=true] Whether the compass widget should be displayed.
87
+ * @param {boolean} [options.display3DToggle=true] Whether the navigation should include a widget
88
+ * to toggle between top and oblique view.
89
+ * @param {boolean} [options.displayZoomIn=true] Whether the zoom-in widget should be displayed.
90
+ * @param {boolean} [options.displayZoomOut=true] Whether the zoom-out widget should be displayed.
91
+ * @param {number} [options.animationDuration=500] The duration of travel animations, when clicking
92
+ * navigation widgets.
93
+ * @param {string} [options.position='bottom-left'] Defines which corner of the `parentElement` the
94
+ * navigation menu should be displayed to.
95
+ * Possible values are `top-left`, `top-right`,
96
+ * `bottom-left` and `bottom-right`. If the input
97
+ * value does not match one of these, it will be
98
+ * defaulted to `bottom-left`.
99
+ * @param {string} [options.direction='column'] Whether the navigation menu should expand
100
+ * horizontally or vertically. Possible values
101
+ * are `column` and `row`. If the input value
102
+ * does not match one of these, it will be
103
+ * defaulted to `column`.
104
+ * @param {Object} [options.translate] An optional translation of the navigation menu.
105
+ * @param {number} [options.translate.x=0] The navigation menu translation along the page
106
+ * x-axis.
107
+ * @param {number} [options.translate.y=0] The navigation menu translation along the page
108
+ * y-axis.
109
+ */
110
+ constructor(view) {
111
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
112
+ // ---------- BUILD PROPERTIES ACCORDING TO DEFAULT OPTIONS AND OPTIONS PASSED IN PARAMETERS : ----------
113
+
114
+ // Check if the view is supported.
115
+ if (!view.isGlobeView) {
116
+ throw new Error('\'Navigation\' plugin only supports \'GlobeView\'. Therefore, the \'view\' parameter must be a ' + '\'GlobeView\'.');
117
+ }
118
+
119
+ // `top`, `bottom`, `left` and `right` values for `position` option are not relevant for navigation widget.
120
+ if (['top', 'bottom', 'left', 'right'].includes(options.position)) {
121
+ console.warn('\'position\' optional parameter for \'Navigation\' is not a valid option. ' + `It will be set to '${DEFAULT_OPTIONS.position}'.`);
122
+ options.position = DEFAULT_OPTIONS.position;
123
+ }
124
+ super(view, options, DEFAULT_OPTIONS);
125
+ this.#_view = view;
126
+ this.direction = options.direction || DEFAULT_OPTIONS.direction;
127
+ if (!['column', 'row'].includes(this.direction)) {
128
+ console.warn('\'direction\' optional parameter for \'Navigation\' constructor is not a valid option. ' + `It will be set to '${DEFAULT_OPTIONS.direction}'.`);
129
+ this.direction = DEFAULT_OPTIONS.direction;
130
+ }
131
+ this.animationDuration = options.animationDuration === undefined ? DEFAULT_OPTIONS.animationDuration : options.animationDuration;
132
+
133
+ // ---------- CREATE A DomElement WITH id AND classes RELEVANT TO THE WIDGET PROPERTIES : ----------
134
+
135
+ // Create a div containing all widgets and add it to its specified parent.
136
+ this.domElement.id = 'widgets-navigation';
137
+
138
+ // Position widget div according to options.
139
+ this.domElement.classList.add(`${this.direction}-widget`);
140
+
141
+ // ---------- CREATE THE DEFAULT WIDGETS IF NOT HIDDEN (COMPASS, 3D AND ZOOM BUTTONS) : ----------
142
+
143
+ // Add a compass widget if requested.
144
+ if (options.displayCompass ?? DEFAULT_OPTIONS.displayCompass) {
145
+ this.compass = this.#_addDefaultButton(DEFAULT_BUTTONS.compass, () => {
146
+ this.#_action({
147
+ heading: 0,
148
+ tilt: 89.5
149
+ });
150
+ });
151
+
152
+ // Manage compass rotation when the view's camera is moved.
153
+ view.addEventListener(VIEW_EVENTS.CAMERA_MOVED, event => {
154
+ this.compass.style.transform = `rotate(${-event.heading}deg)`;
155
+ });
156
+ }
157
+
158
+ // Add a 3D toggle button if requested.
159
+ if (options.display3DToggle ?? DEFAULT_OPTIONS.display3DToggle) {
160
+ this.toggle3D = this.#_addDefaultButton(DEFAULT_BUTTONS.toggle3D, () => {
161
+ this.#_action({
162
+ tilt: this.#_view.controls.getTilt() < 89 ? 89.5 : 40
163
+ });
164
+ });
165
+
166
+ // Manage button content toggle when the view's camera is moved.
167
+ view.addEventListener(VIEW_EVENTS.CAMERA_MOVED, event => {
168
+ this.toggle3D.innerHTML = event.tilt < 89 ? '2D' : '3D';
169
+ });
170
+ }
171
+
172
+ // Add a zoom-in button if requested.
173
+ if (options.displayZoomIn ?? DEFAULT_OPTIONS.displayZoomIn) {
174
+ this.zoomIn = this.#_addDefaultButton(DEFAULT_BUTTONS.zoomIn, () => {
175
+ this.#_action({
176
+ zoom: Math.min(20, this.#_view.controls.getZoom() + 1)
177
+ });
178
+ });
179
+ }
180
+
181
+ // Add a zoom-out button if requested.
182
+ if (options.displayZoomOut ?? DEFAULT_OPTIONS.displayZoomOut) {
183
+ this.zoomOut = this.#_addDefaultButton(DEFAULT_BUTTONS.zoomOut, () => {
184
+ this.#_action({
185
+ zoom: Math.max(3, this.#_view.controls.getZoom() - 1)
186
+ });
187
+ });
188
+ }
189
+ }
190
+
191
+ /**
192
+ *
193
+ * @param {string} id The unique id the created button should be given.
194
+ * @param {string} [content=''] An HTML string defining the content of the button.
195
+ * @param {string} [title=''] An HTML string defining information on the button. This string will be
196
+ * displayed in a tooltip when hovering the button.
197
+ * @param {function} [onclick] The method that should be executed when the button is clicked on.
198
+ * @param {string} [parentId] The unique id of a button bar in which the created button should be added.
199
+ * A button bar is a group which contains one or several buttons. All
200
+ * buttons created with Navigation are in a button bar. If the given id does
201
+ * not match an already existing button bar, a new button bar will be created
202
+ * with this id. If no id is given, a button bar will be created with no id.
203
+ * The later case can be useful for creating isolated buttons.
204
+ *
205
+ * @returns {HTMLButtonElement} The created button.
206
+ */
207
+ addButton(id) {
208
+ let content = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
209
+ let title = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
210
+ let onclick = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : () => {};
211
+ let parentId = arguments.length > 4 ? arguments[4] : undefined;
212
+ let buttonBar = document.getElementById(parentId);
213
+ if (!buttonBar) {
214
+ buttonBar = this.addButtonBar(parentId);
215
+ }
216
+ const button = document.createElement('button');
217
+ button.className = 'widget-button';
218
+ button.id = id;
219
+ button.innerHTML = content;
220
+ button.title = title;
221
+ button.onclick = onclick;
222
+ buttonBar.appendChild(button);
223
+
224
+ // The buttons must not be focused using tab key.
225
+ button.tabIndex = -1;
226
+ // When releasing the mouse after clicking the button, we give the focus back to the view. Therefore, we can use
227
+ // key events on the view without having to click the view to grant it focus.
228
+ window.addEventListener('pointerup', () => {
229
+ if (document.activeElement === button) {
230
+ this.#_view.domElement.focus();
231
+ }
232
+ });
233
+ return button;
234
+ }
235
+ addButtonBar(id) {
236
+ const buttonBar = document.createElement('div');
237
+ buttonBar.className = 'widget-button-bar';
238
+ if (id) {
239
+ buttonBar.id = id;
240
+ }
241
+ this.domElement.appendChild(buttonBar);
242
+ return buttonBar;
243
+ }
244
+ }
245
+ export default Navigation;