@eturnity/eturnity_3d 0.0.4
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/.babelrc +3 -0
- package/.env +0 -0
- package/.eslintrc +18 -0
- package/.eslintrc.json +18 -0
- package/.prettierrc +7 -0
- package/Dockerfile +17 -0
- package/README.md +18 -0
- package/bitbucket-pipelines.yml +681 -0
- package/dist/assets/images/panels/longiSolarLR4_60HPH_350M.png +0 -0
- package/dist/assets/images/panels/longiSolarLR4_60HPH_370M.png +0 -0
- package/dist/assets/images/panels/reneSola_Virtus_2_JC320S_24_Bbw copy.png +0 -0
- package/dist/assets/images/panels/reneSola_Virtus_2_JC320S_24_Bbw.png +0 -0
- package/dist/assets/images/panels/uv.png +0 -0
- package/dist/assets/panels/longiSolarLR4_60HPH_350M.png +0 -0
- package/dist/assets/panels/longiSolarLR4_60HPH_370M.png +0 -0
- package/dist/assets/panels/reneSola_Virtus_2_JC320S_24_Bbw.png +0 -0
- package/dist/assets/panels/uv.png +0 -0
- package/dist/assets/theme.js +43 -0
- package/dist/assets/vue.svg +1 -0
- package/dist/css/main.1f8fe45d.css +1 -0
- package/dist/img/longiSolarLR4_60HPH_350M.85a84b2e.png +0 -0
- package/dist/img/longiSolarLR4_60HPH_370M.bf6b1f4e.png +0 -0
- package/dist/img/reneSola_Virtus_2_JC320S_24_Bbw.8b1892e0.png +0 -0
- package/dist/index.html +26 -0
- package/dist/main.js +60230 -0
- package/dist/main.js.map +1 -0
- package/dist/vite.svg +1 -0
- package/docker_conf/nginx/nginx.conf +16 -0
- package/index.html +26 -0
- package/package.json +71 -0
- package/postcss.config.js +1 -0
- package/preview.html +55 -0
- package/public/assets/images/panels/longiSolarLR4_60HPH_350M.png +0 -0
- package/public/assets/images/panels/longiSolarLR4_60HPH_370M.png +0 -0
- package/public/assets/images/panels/reneSola_Virtus_2_JC320S_24_Bbw copy.png +0 -0
- package/public/assets/images/panels/reneSola_Virtus_2_JC320S_24_Bbw.png +0 -0
- package/public/assets/images/panels/uv.png +0 -0
- package/public/assets/panels/longiSolarLR4_60HPH_350M.png +0 -0
- package/public/assets/panels/longiSolarLR4_60HPH_370M.png +0 -0
- package/public/assets/panels/reneSola_Virtus_2_JC320S_24_Bbw.png +0 -0
- package/public/assets/panels/uv.png +0 -0
- package/public/assets/theme.js +43 -0
- package/public/assets/vue.svg +1 -0
- package/public/vite.svg +1 -0
- package/server.js +17 -0
- package/src/App.vue +28 -0
- package/src/assets/images/panels/longiSolarLR4_60HPH_350M.png +0 -0
- package/src/assets/images/panels/longiSolarLR4_60HPH_370M.png +0 -0
- package/src/assets/images/panels/reneSola_Virtus_2_JC320S_24_Bbw copy.png +0 -0
- package/src/assets/images/panels/reneSola_Virtus_2_JC320S_24_Bbw.png +0 -0
- package/src/assets/images/panels/uv.png +0 -0
- package/src/assets/images/white_tile.png +0 -0
- package/src/assets/theme.js +43 -0
- package/src/components/ThreeCanvasViewer.vue +247 -0
- package/src/config.js +104 -0
- package/src/functions.js +2 -0
- package/src/helper/DFS.js +80 -0
- package/src/helper/MapView.js +253 -0
- package/src/helper/UpdateRoofModuleFieldRelations.js +119 -0
- package/src/helper/UpdateRoofObstacleRelations.js +86 -0
- package/src/helper/clearThreeObjects.js +21 -0
- package/src/helper/customProvider.js +57 -0
- package/src/helper/geometryHandler.js +606 -0
- package/src/helper/graphCreation.js +43 -0
- package/src/helper/materials.js +55 -0
- package/src/helper/projectedMaterial.js +502 -0
- package/src/helper/renderMixin.js +1132 -0
- package/src/helper/threeMixin.js +171 -0
- package/src/main.js +28 -0
- package/src/store/AddMargin.js +107 -0
- package/src/store/hydrateData.js +209 -0
- package/src/store/nodesEdgesCreation.js +216 -0
- package/src/store/store.js +10 -0
- package/src/store/three-d-module.js +142 -0
- package/src/style.css +96 -0
- package/src/utils/layers.js +265 -0
- package/src/utils/leaflet-config.service.js +67 -0
- package/vue.config.js +46 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import {Mesh, MeshBasicMaterial} from 'three';
|
|
2
|
+
import {MapNode,LODRaycast,MapPlaneNode} from 'geo-three';
|
|
3
|
+
/**
|
|
4
|
+
* Map viewer is used to read and display map tiles from a server.
|
|
5
|
+
*
|
|
6
|
+
* It was designed to work with a OpenMapTiles but can also be used with another map tiles.
|
|
7
|
+
*
|
|
8
|
+
* The map is drawn in plane map nodes using a quad tree that is subdivided as necessary to guaratee good map quality.
|
|
9
|
+
*/
|
|
10
|
+
export class MapView extends Mesh
|
|
11
|
+
{
|
|
12
|
+
/**
|
|
13
|
+
* Planar map projection.
|
|
14
|
+
*/
|
|
15
|
+
PLANAR = 200;
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
mapModes = new Map([
|
|
19
|
+
[MapView.PLANAR, MapPlaneNode]
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* LOD control object used to defined how tiles are loaded in and out of memory.
|
|
24
|
+
*/
|
|
25
|
+
lod = null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Map tile color layer provider.
|
|
29
|
+
*/
|
|
30
|
+
provider = null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Map height (terrain elevation) layer provider.
|
|
34
|
+
*
|
|
35
|
+
* Only used for HEIGHT, HEIGHT_SHADER and MARTINI map modes.
|
|
36
|
+
*/
|
|
37
|
+
heightProvider = null;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Define the type of map node in use, defined how the map is presented.
|
|
41
|
+
*
|
|
42
|
+
* Should only be set on creation.
|
|
43
|
+
*/
|
|
44
|
+
root = null;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Indicate if the nodes should cache its children when it is simplified. Nodes that are no longer in use should be kept in memory.
|
|
48
|
+
*
|
|
49
|
+
* Usefull for fast moving scenarios to prevent reparsing data in fast moving scenes.
|
|
50
|
+
*
|
|
51
|
+
* Should only be used if the child generation process is time consuming. Should be kept off unless required.
|
|
52
|
+
*/
|
|
53
|
+
cacheTiles= false;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Constructor for the map view objects.
|
|
57
|
+
*
|
|
58
|
+
* @param root - Map view node modes can be SPHERICAL, HEIGHT or PLANAR. PLANAR is used by default. Can also be a custom MapNode instance.
|
|
59
|
+
* @param provider - Map color tile provider by default a OSM maps provider is used if none specified.
|
|
60
|
+
* @param heightProvider - Map height tile provider, by default no height provider is used.
|
|
61
|
+
*/
|
|
62
|
+
constructor(root = MapView.PLANAR, provider = new OpenStreetMapsProvider(), heightProvider = null)
|
|
63
|
+
{
|
|
64
|
+
super(undefined, new MeshBasicMaterial({transparent: true, opacity: 0.0}));
|
|
65
|
+
this.lod = new LODRaycast();
|
|
66
|
+
this.provider = provider;
|
|
67
|
+
this.heightProvider = heightProvider;
|
|
68
|
+
this.setRoot(root);
|
|
69
|
+
this.preSubdivide();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Ajust node configuration depending on the camera distance.
|
|
74
|
+
*
|
|
75
|
+
* Called everytime automatically before render by the renderer.
|
|
76
|
+
*/
|
|
77
|
+
onBeforeRender=(renderer, scene, camera) =>
|
|
78
|
+
{
|
|
79
|
+
this.lod.updateLOD(this, camera, renderer, scene);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Set the root of the map view.
|
|
84
|
+
*
|
|
85
|
+
* Is set by the constructor by default, can be changed in runtime.
|
|
86
|
+
*
|
|
87
|
+
* @param root - Map node to be used as root.
|
|
88
|
+
*/
|
|
89
|
+
setRoot(root)
|
|
90
|
+
{
|
|
91
|
+
console.log('root',root)
|
|
92
|
+
|
|
93
|
+
if (typeof root === 'number')
|
|
94
|
+
{
|
|
95
|
+
const rootConstructor = MapPlaneNode//MapView.mapModes.get(root);
|
|
96
|
+
console.log('rootConstructor',rootConstructor)
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
root = new rootConstructor(null, this);
|
|
99
|
+
}
|
|
100
|
+
console.log(root)
|
|
101
|
+
|
|
102
|
+
// Remove old root
|
|
103
|
+
if (!!this.root )
|
|
104
|
+
{
|
|
105
|
+
this.remove(this.root);
|
|
106
|
+
this.root = null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
this.root = root;
|
|
111
|
+
|
|
112
|
+
// Initialize root node
|
|
113
|
+
if (!!this.root )
|
|
114
|
+
{
|
|
115
|
+
// @ts-ignore
|
|
116
|
+
this.geometry = this.root.constructor.baseGeometry;
|
|
117
|
+
// // @ts-ignore
|
|
118
|
+
this.scale.copy(this.root.constructor.baseScale);
|
|
119
|
+
|
|
120
|
+
this.root.mapView = this;
|
|
121
|
+
this.add(this.root);
|
|
122
|
+
this.root.initialize();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Pre-subdivide map tree to create nodes of levels not available in the provider.
|
|
128
|
+
*
|
|
129
|
+
* Checks for the minimum zoom level in the providers attached to the map view.
|
|
130
|
+
*/
|
|
131
|
+
preSubdivide()
|
|
132
|
+
{
|
|
133
|
+
console.log('preSbdivide')
|
|
134
|
+
const minZoom = this.provider.minZoom
|
|
135
|
+
const maxZoom = this.provider.maxZoom
|
|
136
|
+
function subdivide(node, depth)
|
|
137
|
+
{
|
|
138
|
+
if (depth <= 0)
|
|
139
|
+
{
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
node.subdivide();
|
|
145
|
+
|
|
146
|
+
for (let i = 0; i < node.children.length; i++)
|
|
147
|
+
{
|
|
148
|
+
if (node.children[i] instanceof MapNode)
|
|
149
|
+
{
|
|
150
|
+
const child = node.children[i];
|
|
151
|
+
subdivide(child, depth - 1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
console.log("minmax zoom",minZoom,maxZoom)
|
|
158
|
+
if (minZoom > 0)
|
|
159
|
+
{
|
|
160
|
+
subdivide(this.root, minZoom);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Change the map provider of this map view.
|
|
166
|
+
*
|
|
167
|
+
* Will discard all the tiles already loaded using the old provider.
|
|
168
|
+
*/
|
|
169
|
+
setProvider(provider)
|
|
170
|
+
{
|
|
171
|
+
if (provider !== this.provider)
|
|
172
|
+
{
|
|
173
|
+
this.provider = provider;
|
|
174
|
+
this.clear();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Change the map height provider of this map view.
|
|
180
|
+
*
|
|
181
|
+
* Will discard all the tiles already loaded using the old provider.
|
|
182
|
+
*/
|
|
183
|
+
setHeightProvider(heightProvider)
|
|
184
|
+
{
|
|
185
|
+
if (heightProvider !== this.heightProvider)
|
|
186
|
+
{
|
|
187
|
+
this.heightProvider = heightProvider;
|
|
188
|
+
this.clear();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Clears all tiles from memory and reloads data. Used when changing the provider.
|
|
194
|
+
*
|
|
195
|
+
* Should be called manually if any changed to the provider are made without setting the provider.
|
|
196
|
+
*/
|
|
197
|
+
clear()
|
|
198
|
+
{
|
|
199
|
+
this.traverse(function(children)
|
|
200
|
+
{
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
if (children.childrenCache)
|
|
203
|
+
{
|
|
204
|
+
// @ts-ignore
|
|
205
|
+
children.childrenCache = null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// @ts-ignore
|
|
209
|
+
if (children.initialize)
|
|
210
|
+
{
|
|
211
|
+
// @ts-ignore
|
|
212
|
+
children.initialize();
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get the minimum zoom level available in the providers attached to the map view.
|
|
221
|
+
*
|
|
222
|
+
* @returns Minimum zoom level available.
|
|
223
|
+
*/
|
|
224
|
+
minZoom()
|
|
225
|
+
{
|
|
226
|
+
return 15
|
|
227
|
+
return Math.max(this.provider.minZoom, -Infinity);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get the maximum zoom level available in the providers attached to the map view.
|
|
232
|
+
*
|
|
233
|
+
* @returns Maximum zoom level available.
|
|
234
|
+
*/
|
|
235
|
+
maxZoom()
|
|
236
|
+
{
|
|
237
|
+
return 18
|
|
238
|
+
return Math.min(this.provider.maxZoom, Infinity);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get map meta data from server if supported.
|
|
243
|
+
*/
|
|
244
|
+
getMetaData()
|
|
245
|
+
{
|
|
246
|
+
this.provider.getMetaData();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
raycast(raycaster, intersects)
|
|
250
|
+
{
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isInsidePolygon,
|
|
3
|
+
verticalProjectionOnPlane,
|
|
4
|
+
} from '@eturnity/eturnity_maths'
|
|
5
|
+
//this function just update the "roof" and "moduleField(s)" properties of panels,moduleFields and roofs
|
|
6
|
+
export function UpdateRoofModuleFieldRelations(state) {
|
|
7
|
+
const roofPolygons = state.polygons.filter((poly) => poly.layer == 'roof')
|
|
8
|
+
const moduleFieldPolygons = state.polygons.filter(
|
|
9
|
+
(poly) => poly.layer == 'moduleField'
|
|
10
|
+
)
|
|
11
|
+
const panelPolygons = state.polygons.filter((poly) =>
|
|
12
|
+
['panel', 'user_deactivated_panel'].includes(poly.layer)
|
|
13
|
+
)
|
|
14
|
+
//let's remove all moduleFields from roofs
|
|
15
|
+
roofPolygons.forEach((roofPolygon) => {
|
|
16
|
+
//breaking circular reference for memory management
|
|
17
|
+
for (let k in roofPolygon.moduleFields) {
|
|
18
|
+
roofPolygon.moduleFields[k] = null
|
|
19
|
+
}
|
|
20
|
+
roofPolygon.moduleFields = []
|
|
21
|
+
})
|
|
22
|
+
moduleFieldPolygons.forEach((moduleFieldPolygon) => {
|
|
23
|
+
moduleFieldPolygon.roof = null
|
|
24
|
+
})
|
|
25
|
+
panelPolygons.forEach((panelPolygon) => {
|
|
26
|
+
panelPolygon.roof = null
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
for (let k in moduleFieldPolygons) {
|
|
30
|
+
const moduleField = moduleFieldPolygons[k]
|
|
31
|
+
//check if everypoint is in the same roof and if they are not too far from the flat surface.(at least above)
|
|
32
|
+
const roofPolygonCandidates = roofPolygons.filter((roof) =>
|
|
33
|
+
{
|
|
34
|
+
return moduleField.outline.every(p=>isInsidePolygon(p, roof.margins.innerOutline))
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
//roofs on roof can lead to multiple roof under a point. moduleField is in the smallest
|
|
38
|
+
let smallestRoof = null
|
|
39
|
+
for (let roof of roofPolygonCandidates) {
|
|
40
|
+
if (!smallestRoof || smallestRoof.area > roof.area) {
|
|
41
|
+
smallestRoof = roof
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let roofPolygon = smallestRoof
|
|
45
|
+
|
|
46
|
+
if (!roofPolygon) {
|
|
47
|
+
//no roof found under first node of moduleField => moduleField will be deleted
|
|
48
|
+
continue
|
|
49
|
+
}
|
|
50
|
+
let moduleFieldBelongsToRoof = true
|
|
51
|
+
moduleField.outline.forEach((p) => {
|
|
52
|
+
const distanceToFlatRoof =
|
|
53
|
+
p.z -
|
|
54
|
+
verticalProjectionOnPlane(
|
|
55
|
+
p,
|
|
56
|
+
roofPolygon.normalVector,
|
|
57
|
+
roofPolygon.flatOutline[0]
|
|
58
|
+
).z
|
|
59
|
+
if (
|
|
60
|
+
!isInsidePolygon(p, roofPolygon.margins.innerOutline) ||
|
|
61
|
+
distanceToFlatRoof < -1 ||
|
|
62
|
+
distanceToFlatRoof > 50
|
|
63
|
+
) {
|
|
64
|
+
// one point from moduleField is not inside the roof or too far from it's flat surface => moduleField will be deleted
|
|
65
|
+
moduleFieldBelongsToRoof = false
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
if (moduleFieldBelongsToRoof) {
|
|
69
|
+
roofPolygon.moduleFields.push(moduleField)
|
|
70
|
+
moduleField.roof = roofPolygon
|
|
71
|
+
moduleField.panels.forEach((panel) => (panel.roof = roofPolygon))
|
|
72
|
+
panelPolygons.forEach((p) => {
|
|
73
|
+
if (p.moduleField.id == moduleField.id) {
|
|
74
|
+
p.roof = roofPolygon
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let newPolygons = state.polygons.map((p) => {
|
|
81
|
+
if (
|
|
82
|
+
!['roof', 'moduleField', 'panel', 'user_deactivated_panel'].includes(
|
|
83
|
+
p.layer
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
return p
|
|
87
|
+
if (p.layer == 'roof') return roofPolygons.find((r) => r.id == p.id)
|
|
88
|
+
if (p.layer == 'moduleField')
|
|
89
|
+
return moduleFieldPolygons.find((mf) => mf.id == p.id)
|
|
90
|
+
if (['panel', 'user_deactivated_panel'].includes(p.layer))
|
|
91
|
+
return panelPolygons.find((panel) => panel.id == p.id)
|
|
92
|
+
})
|
|
93
|
+
state = { ...state, polygons: newPolygons }
|
|
94
|
+
state = removeModuleFieldAndPanelsOnNoOrMultipleRoofs(state)
|
|
95
|
+
return state
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function removeModuleFieldAndPanelsOnNoOrMultipleRoofs(state) {
|
|
99
|
+
const remainingPolygons = state.polygons.filter((polygon) =>
|
|
100
|
+
['roof', 'obstacle'].includes(polygon.layer)
|
|
101
|
+
)
|
|
102
|
+
const remainingModuleFields = state.polygons.filter(
|
|
103
|
+
(polygon) => polygon.layer == 'moduleField' && !!polygon.roof
|
|
104
|
+
)
|
|
105
|
+
const remainingPanels = state.polygons.filter(
|
|
106
|
+
(polygon) => polygon.layer == 'panel' && !!polygon.roof
|
|
107
|
+
)
|
|
108
|
+
const remainingUserDeactivatedPanels = state.polygons.filter(
|
|
109
|
+
(polygon) => polygon.layer == 'user_deactivated_panel' && !!polygon.roof
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
state.polygons = [
|
|
113
|
+
...remainingPolygons,
|
|
114
|
+
...remainingModuleFields,
|
|
115
|
+
...remainingPanels,
|
|
116
|
+
...remainingUserDeactivatedPanels
|
|
117
|
+
]
|
|
118
|
+
return state
|
|
119
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
verticalProjectionOnPlane,
|
|
3
|
+
calculateArea
|
|
4
|
+
} from '@eturnity/eturnity_maths'
|
|
5
|
+
import {
|
|
6
|
+
intersectOutlines
|
|
7
|
+
} from '@eturnity/eturnity_maths'
|
|
8
|
+
//this function just update the "roof" and "holes" properties of roofs and obstacles
|
|
9
|
+
export function UpdateRoofObstacleRelations(state,updateVersionEnable=true) {
|
|
10
|
+
//let's remove all holes from roofs which are not obstacle then check for every obstacle and add them to roofs
|
|
11
|
+
const roofPolygons = state.polygons.filter((poly) => poly.layer == 'roof')
|
|
12
|
+
const initialRelation={}
|
|
13
|
+
|
|
14
|
+
roofPolygons.forEach((roofPolygon) => {
|
|
15
|
+
initialRelation[roofPolygon.id]=[]
|
|
16
|
+
//breaking circular reference for memory management
|
|
17
|
+
for (let k in roofPolygon.holes) {
|
|
18
|
+
initialRelation[roofPolygon.id].push(roofPolygon.holes.id)
|
|
19
|
+
roofPolygon.holes[k] = null
|
|
20
|
+
}
|
|
21
|
+
roofPolygon.holes = []
|
|
22
|
+
})
|
|
23
|
+
const holePolygons = state.polygons.filter((poly) =>
|
|
24
|
+
['roof','obstacle'].includes(poly.layer)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
holePolygons.forEach((obstacle) => {
|
|
28
|
+
//breaking circular reference for memory management
|
|
29
|
+
for (let k in obstacle.roofs) {
|
|
30
|
+
obstacle.roofs[k] = null
|
|
31
|
+
}
|
|
32
|
+
obstacle.roofs = []
|
|
33
|
+
|
|
34
|
+
let roofsWithObstacle=roofPolygons.filter(roof=>{
|
|
35
|
+
let intersection=intersectOutlines(obstacle.outline,roof.outline)[0]||[]
|
|
36
|
+
if(intersection.length==0)return false
|
|
37
|
+
if(obstacle.layer=='obstacle')return true
|
|
38
|
+
|
|
39
|
+
let deltaArea=calculateArea(roof.outline)-calculateArea(intersection)
|
|
40
|
+
if(deltaArea<100)return false
|
|
41
|
+
return true
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
let heightReference = 0
|
|
45
|
+
for (let roof of roofsWithObstacle) {
|
|
46
|
+
|
|
47
|
+
heightReference = obstacle.outline.reduce((acc,cur)=>{
|
|
48
|
+
acc=Math.max(
|
|
49
|
+
acc,
|
|
50
|
+
verticalProjectionOnPlane(cur, roof.normalVector, roof.flatOutline[0])
|
|
51
|
+
.z
|
|
52
|
+
)
|
|
53
|
+
return acc
|
|
54
|
+
},0)
|
|
55
|
+
|
|
56
|
+
if (!obstacle.roofs.map((r) => r.id).includes(roof.id) && obstacle.id!=roof.id) {
|
|
57
|
+
obstacle.roofs.push(roof)
|
|
58
|
+
}
|
|
59
|
+
if (!roof.holes.map((h) => h.id).includes(obstacle.id) && obstacle.id!=roof.id) {
|
|
60
|
+
roof.holes.push(obstacle)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
obstacle.heightReference = heightReference
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
roofPolygons.forEach((roof)=>{
|
|
69
|
+
if(roof.holes.map(h=>h.id).sort().join()!=initialRelation[roof.id].sort().join()){
|
|
70
|
+
if(updateVersionEnable){
|
|
71
|
+
roof.version++
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
state = removeStandaloneObstacles(state)
|
|
77
|
+
|
|
78
|
+
return state
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function removeStandaloneObstacles(state) {
|
|
82
|
+
state.polygons = state.polygons.filter(
|
|
83
|
+
(polygon) => polygon.layer != 'obstacle' || polygon.roofs.length > 0
|
|
84
|
+
)
|
|
85
|
+
return state
|
|
86
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export default function clearThreeObject(obj) {
|
|
2
|
+
while (obj.children.length > 0) {
|
|
3
|
+
clearThreeObject(obj.children[0])
|
|
4
|
+
obj.remove(obj.children[0])
|
|
5
|
+
}
|
|
6
|
+
if (obj.geometry) obj.geometry.dispose()
|
|
7
|
+
if (obj.texture) obj.texture.dispose()
|
|
8
|
+
|
|
9
|
+
if (obj.material) {
|
|
10
|
+
//in case of map, bumpMap, normalMap, envMap ...
|
|
11
|
+
Object.keys(obj.material).forEach((prop) => {
|
|
12
|
+
if (!obj.material[prop]) return
|
|
13
|
+
if (
|
|
14
|
+
obj.material[prop] !== null &&
|
|
15
|
+
typeof obj.material[prop].dispose === 'function'
|
|
16
|
+
)
|
|
17
|
+
obj.material[prop].dispose()
|
|
18
|
+
})
|
|
19
|
+
obj.material.dispose()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MapProvider,
|
|
3
|
+
} from 'geo-three'
|
|
4
|
+
import getLayers from '../utils/layers'
|
|
5
|
+
export class CustomProvider extends MapProvider {
|
|
6
|
+
constructor(address, map_layer) {
|
|
7
|
+
super()
|
|
8
|
+
|
|
9
|
+
const layer = getLayers().find((layer) => layer.key == map_layer)
|
|
10
|
+
this.layer = layer
|
|
11
|
+
// if (layer && layer.layerData && layer.layerData.options) {
|
|
12
|
+
// this.minZoom = layer.layerData.options.minZoom || 9
|
|
13
|
+
// this.maxZoom = layer.layerData.options.maxNativeZoom || 21
|
|
14
|
+
// } else {
|
|
15
|
+
// this.minZoom = 9
|
|
16
|
+
// this.maxZoom = 21
|
|
17
|
+
// }
|
|
18
|
+
this.defaultImage = document.createElement('img')
|
|
19
|
+
this.defaultImage.src=require('../assets/images/white_tile.png')
|
|
20
|
+
this.defaultImage.crossOrigin = 'Anonymous'
|
|
21
|
+
this.url = layer
|
|
22
|
+
? layer.layerData._url
|
|
23
|
+
: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'
|
|
24
|
+
this.address = address
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fetchTile(zoom, x, y) {
|
|
28
|
+
const minZoomLayer = this.layer.layerData.options.minZoom || 9
|
|
29
|
+
const maxZoomLayer = this.layer.layerData.options.maxNativeZoom || 20
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
|
|
32
|
+
if (zoom < minZoomLayer) {
|
|
33
|
+
resolve(this.defaultImage)
|
|
34
|
+
} else if(zoom > maxZoomLayer){
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
var image = document.createElement('img')
|
|
39
|
+
image.onload = function () {
|
|
40
|
+
resolve(image)
|
|
41
|
+
}
|
|
42
|
+
image.onerror = function () {
|
|
43
|
+
resolve(this.defaultImage)
|
|
44
|
+
}
|
|
45
|
+
image.crossOrigin = 'Anonymous'
|
|
46
|
+
let imageSrc = this.url
|
|
47
|
+
imageSrc = imageSrc.replace('{x}', x)
|
|
48
|
+
imageSrc = imageSrc.replace('{y}', y)
|
|
49
|
+
imageSrc = imageSrc.replace('{z}', zoom)
|
|
50
|
+
imageSrc = imageSrc.replace('{s}', 'a')
|
|
51
|
+
imageSrc = imageSrc.replace('{format}', 'image/png')
|
|
52
|
+
imageSrc = imageSrc.replace('{tileMatrixSet}', 'PM')
|
|
53
|
+
image.src = imageSrc
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
}
|