@eturnity/eturnity_3d 7.42.3 → 7.48.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eturnity/eturnity_3d",
3
3
  "private": false,
4
- "version": "7.42.3",
4
+ "version": "7.48.1",
5
5
  "files": [
6
6
  "dist",
7
7
  "src"
@@ -15,7 +15,7 @@
15
15
  "prettier": "prettier --write \"**/*.{js,vue}\""
16
16
  },
17
17
  "dependencies": {
18
- "@eturnity/eturnity_maths": "7.42.2",
18
+ "@eturnity/eturnity_maths": "7.48.0",
19
19
  "@originjs/vite-plugin-commonjs": "1.0.3",
20
20
  "core-js": "3.30.2",
21
21
  "cors": "2.8.5",
@@ -0,0 +1,67 @@
1
+ class ImageHelper {
2
+ /**
3
+ * Wrapper for image loading
4
+ * @param {String} src - source url
5
+ * @returns image
6
+ */
7
+ static loadImage(src) {
8
+ return new Promise((resolve, reject) => {
9
+ const image = new Image()
10
+ image.crossOrigin = 'Anonymous'
11
+ image.onload = () => {
12
+ resolve(image)
13
+ }
14
+ image.onerror = reject
15
+ image.src = src
16
+ })
17
+ }
18
+
19
+ /**
20
+ * Overlays images on top of one another
21
+ * @param {Array<Images>} images
22
+ * @returns overlapped image
23
+ */
24
+ static overlayImages(images = []) {
25
+ return new Promise((resolve, reject) => {
26
+ const canvas = document.createElement('canvas')
27
+ canvas.width = images[0].width
28
+ canvas.height = images[0].height
29
+ const ctx = canvas.getContext('2d')
30
+
31
+ // last element of the array will be the top most layer
32
+ images.forEach((image) => {
33
+ ctx.drawImage(image, 0, 0)
34
+ })
35
+
36
+ const newImage = new Image()
37
+ newImage.src = canvas.toDataURL('image/png')
38
+ newImage.onload = () => {
39
+ resolve(newImage)
40
+ }
41
+ newImage.onerror = reject
42
+ })
43
+ }
44
+
45
+ /**
46
+ * Checks if an image is fully transparent
47
+ * @param {Image} image
48
+ * @returns Boolean
49
+ */
50
+ static isFullyTransparent(image) {
51
+ const canvas = document.createElement('canvas')
52
+ canvas.width = image.width
53
+ canvas.height = image.height
54
+ const ctx = canvas.getContext('2d')
55
+ ctx.drawImage(image, 0, 0)
56
+ const imageData = ctx.getImageData(0, 0, image.width, image.height)
57
+ const pixels = imageData.data
58
+ for (let i = 3; i < pixels.length; i += 4) {
59
+ if (pixels[i] !== 0) {
60
+ return false
61
+ }
62
+ }
63
+ return true
64
+ }
65
+ }
66
+
67
+ export default ImageHelper
@@ -1,5 +1,8 @@
1
1
  import { MapProvider } from 'geo-three'
2
2
  import getLayers from '../utils/layers'
3
+ import ImageHelper from './ImageHelper'
4
+ import { MAP_LAYER_TYPE } from '../utils/constants'
5
+
3
6
  export class CustomProvider extends MapProvider {
4
7
  constructor(address, map_layer) {
5
8
  super()
@@ -9,77 +12,114 @@ export class CustomProvider extends MapProvider {
9
12
  this.mapLayer = map_layer
10
13
  this.maxZoom = this.layer.layerData.options.maxNativeZoom || 20
11
14
  this.minZoom = this.layer.layerData.options.minZoom || 17
12
- this.defaultImage = document.createElement('img')
13
- this.defaultImage.src = require('../assets/images/white_tile.png')
14
- this.defaultImage.crossOrigin = 'Anonymous'
15
+ const imgSrc = require('../assets/images/white_tile.png')
16
+ this.defaultImage = ImageHelper.loadImage(imgSrc.default || imgSrc)
15
17
  this.url = layer
16
18
  ? layer.layerData._url
17
19
  : 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'
18
20
  this.address = address
19
21
  }
20
22
 
21
- fetchTile(zoom, x, y) {
22
- const minZoomLayer = this.layer.layerData.options.minZoom || 9
23
- const maxZoomLayer = this.layer.layerData.options.maxNativeZoom || 20
24
- return new Promise((resolve, reject) => {
25
- if (zoom < minZoomLayer) {
26
- resolve(this.defaultImage)
27
- } else {
28
- const image = document.createElement('img')
29
-
30
- image.onload = function () {
31
- image.style.backgroundColor = 'red'
32
- resolve(image)
33
- }
34
- image.onerror = function () {
35
- resolve(this.defaultImage)
36
- }
23
+ getImageSrc(layerKey, layer, position) {
24
+ const { x, y, zoom } = position
25
+ let imageSrc = layer._url
26
+ if (layerKey == 'bkgHexagonGermanyLayer') {
27
+ const numTiles = Math.pow(2, zoom)
28
+ const tileX = x
29
+ const tileY = numTiles - 1 - y
30
+ const earthPerimeter = 6378137 * Math.PI * 2
31
+ const resolution_mPerTile = earthPerimeter / numTiles
37
32
 
38
- image.crossOrigin = 'Anonymous'
39
- let imageSrc = this.url
40
- if (this.layer.key == 'bkgHexagonGermanyLayer') {
41
- const numTiles = Math.pow(2, zoom)
42
- const tileX = x
43
- const tileY = numTiles - 1 - y
44
- const earthPerimeter = 6378137 * Math.PI * 2
45
- const resolution_mPerTile = earthPerimeter / numTiles
33
+ const xMin = tileX * resolution_mPerTile - earthPerimeter / 2
34
+ const xMax = xMin + resolution_mPerTile
35
+ const yMin = tileY * resolution_mPerTile - earthPerimeter / 2
36
+ const yMax = yMin + resolution_mPerTile
37
+ const { kid, layers } = layer.options
38
+ imageSrc =
39
+ imageSrc +
40
+ '?service=WMS' +
41
+ '&request=GetMap' +
42
+ `&layers=${layers}` +
43
+ `&kid=${kid}` +
44
+ '&format=image%2Fpng' +
45
+ '&transparent=true' +
46
+ '&version=1.3.0' +
47
+ '&width=256' +
48
+ '&height=256' +
49
+ '&crs=EPSG%3A3857' +
50
+ `&bbox=${xMin},${yMin},${xMax},${yMax}`
51
+ } else if (layerKey == 'azureSatelliteLayer') {
52
+ const { apiVersion, tilesetId, subscriptionKey } = layer.options
53
+ imageSrc = imageSrc
54
+ .replace('{x}', x)
55
+ .replace('{y}', y)
56
+ .replace('{z}', zoom)
57
+ .replace('{apiVersion}', apiVersion)
58
+ .replace('{tilesetId}', tilesetId)
59
+ .replace('{subscriptionKey}', subscriptionKey)
60
+ imageSrc = imageSrc + `&no-cache=${Date.now()}`
61
+ } else {
62
+ imageSrc = imageSrc.replace('{x}', x)
63
+ imageSrc = imageSrc.replace('{y}', y)
64
+ imageSrc = imageSrc.replace('{z}', zoom)
65
+ if (layer.options.subdomains) {
66
+ const subDomain =
67
+ layer.options.subdomains[
68
+ Math.abs((x << zoom) + y) % layer.options.subdomains.length
69
+ ]
70
+ imageSrc = imageSrc.replace('{s}', subDomain)
71
+ }
72
+ imageSrc = imageSrc.replace('{format}', 'image/jpeg')
73
+ imageSrc = imageSrc.replace('{tileMatrixSet}', 'PM')
74
+ }
75
+ return imageSrc
76
+ }
46
77
 
47
- const xMin = tileX * resolution_mPerTile - earthPerimeter / 2
48
- const xMax = xMin + resolution_mPerTile
49
- const yMin = tileY * resolution_mPerTile - earthPerimeter / 2
50
- const yMax = yMin + resolution_mPerTile
78
+ async fetchTile(zoom, x, y) {
79
+ try {
80
+ const minZoomLayer = this.layer.layerData.options.minZoom || 9
81
+ const position = {
82
+ x,
83
+ y,
84
+ zoom,
85
+ }
86
+ if (zoom < minZoomLayer) {
87
+ return this.defaultImage
88
+ } else if (this.layer.key === MAP_LAYER_TYPE.HEXAGON_SATELLITE) {
89
+ const layerGroup = this.layer.layerData
90
+ const layers = layerGroup
91
+ .getLayers()
92
+ .sort((a, b) => a.options.order - b.options.order)
51
93
 
52
- imageSrc += `&service=WMS&request=GetMap&layers=rgb&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&id=bkg_image&layerTypeAPI=bkgHexagonGermanyLayer&width=256&height=256&srs=EPSG%3A3857&bbox=${xMin},${yMin},${xMax},${yMax}`
53
- } else if (this.layer.key == 'azureSatelliteLayer') {
54
- const { apiVersion, tilesetId, subscriptionKey } =
55
- this.layer.layerData.options
56
- imageSrc = imageSrc
57
- .replace('{x}', x)
58
- .replace('{y}', y)
59
- .replace('{z}', zoom)
60
- .replace('{apiVersion}', apiVersion)
61
- .replace('{tilesetId}', tilesetId)
62
- .replace('{subscriptionKey}', subscriptionKey)
63
- imageSrc = imageSrc + `&no-cache=${Date.now()}`
64
- } else {
65
- imageSrc = imageSrc.replace('{x}', x)
66
- imageSrc = imageSrc.replace('{y}', y)
67
- imageSrc = imageSrc.replace('{z}', zoom)
68
- //imageSrc = imageSrc.replace('{s}', 'a')
69
- if (this.layer.layerData.options.subdomains) {
70
- const subDomain =
71
- this.layer.layerData.options.subdomains[
72
- Math.abs((x << zoom) + y) %
73
- this.layer.layerData.options.subdomains.length
74
- ]
75
- imageSrc = imageSrc.replace('{s}', subDomain)
76
- }
77
- imageSrc = imageSrc.replace('{format}', 'image/jpeg')
78
- //imageSrc = imageSrc.replace('{format}', 'image/png')
79
- imageSrc = imageSrc.replace('{tileMatrixSet}', 'PM')
94
+ const promiseArray = layers.map((layer) => {
95
+ const imagePromise = ImageHelper.loadImage(
96
+ this.getImageSrc(this.layer.key, layer, position)
97
+ )
98
+ return imagePromise.catch((error) => error)
99
+ })
100
+ const images = await Promise.all(promiseArray)
101
+ const validImages = images.filter((image) => {
102
+ return image instanceof Image
103
+ })
104
+ if (!validImages.length) {
105
+ throw 'No valid image'
80
106
  }
81
- image.src = imageSrc
107
+ const result =
108
+ validImages.length === 1
109
+ ? validImages[0]
110
+ : await ImageHelper.overlayImages(validImages)
111
+ return result
112
+ } else {
113
+ const imageSrc = this.getImageSrc(
114
+ this.layer.key,
115
+ this.layer.layerData,
116
+ position
117
+ )
118
+ const image = await ImageHelper.loadImage(imageSrc)
119
+ return image
82
120
  }
83
- })
121
+ } catch (error) {
122
+ return this.defaultImage
123
+ }
84
124
  }
85
125
  }
@@ -151,7 +151,7 @@ export default {
151
151
  mesh.userData.version = roofPolygon.version
152
152
  mesh.userData.type = 'flatRoofMeshes'
153
153
  mesh.userData.meshId = roofPolygon.id
154
-
154
+ mesh.renderOrder = 10
155
155
  mesh.visible = roofPolygon.maximumGap > maximumGapLimit
156
156
  this.scene.add(mesh)
157
157
  this.meshes.flatRoofMeshes[roofPolygon.id] = mesh
@@ -229,7 +229,16 @@ export default {
229
229
  }
230
230
 
231
231
  let mesh = this.meshes.tileMeshes[meshId]
232
- if (mesh && this.provider.mapLayer == mesh.userData.mapLayer) {
232
+ const materialId =
233
+ 'material_' + x + '_' + y + '_' + zoomLvl + '_' + mapLayer
234
+ const tileProjectionMaterial = this.material.projectionTiles.find(
235
+ (m) => m.userData.materialId === materialId
236
+ )
237
+ if (
238
+ mesh &&
239
+ this.provider.mapLayer == mesh.userData.mapLayer &&
240
+ (!isTileOnRoofBound || tileProjectionMaterial)
241
+ ) {
233
242
  resolve()
234
243
  return
235
244
  }
@@ -263,6 +272,8 @@ export default {
263
272
  }
264
273
  texture.generateMipmaps = false
265
274
  texture.format = THREE.RGBAFormat
275
+ texture.encoding = THREE.sRGBEncoding
276
+ texture.colorSpace = THREE.SRGBColorSpace
266
277
  texture.needsUpdate = true
267
278
  let corners = tileToCorners(x, y, zoomLvl, latitude, longitude)
268
279
  corners = corners.map((c) => {
@@ -284,8 +295,6 @@ export default {
284
295
  mesh.castShadow = false
285
296
  mesh.updateMatrix()
286
297
  } else {
287
- texture.encoding = THREE.sRGBEncoding
288
- texture.colorSpace = THREE.SRGBColorSpace
289
298
  material = new THREE.MeshPhongMaterial({
290
299
  map: texture,
291
300
  color: 0xffffff,
@@ -490,6 +499,7 @@ export default {
490
499
  t.imagePromise.cancel()
491
500
  return false
492
501
  }
502
+ return true
493
503
  })
494
504
  },
495
505
  },
@@ -11,6 +11,6 @@ export const MAP_LAYER_TYPE = {
11
11
  BASEMAP_SATELLITE: 'baseMapLayerStreetNumber',
12
12
  IGN_SATELLITE: 'ignSpainLayer_pnoa_image',
13
13
  IGN_ROAD: 'ignSpainLayer_map',
14
- BKG_SATELLITE: 'bkgHexagonGermanyLayer',
14
+ HEXAGON_SATELLITE: 'bkgHexagonGermanyLayer',
15
15
  GEOPORTAL_SATELLITE: 'geoportail_fr_wmts_ortho_layer',
16
16
  }