forked from MapComplete/MapComplete
		
	Feature: first working version of inspecting 360° images
This commit is contained in:
		
							parent
							
								
									7d104b4266
								
							
						
					
					
						commit
						01ba98a820
					
				
					 24 changed files with 330 additions and 436 deletions
				
			
		|  | @ -2,6 +2,9 @@ | ||||||
|   "id": "geocoded_image", |   "id": "geocoded_image", | ||||||
|   "name": null, |   "name": null, | ||||||
|   "source": "special", |   "source": "special", | ||||||
|  |   "description": { | ||||||
|  |     "*": "Layer showing green dots where a geocoded image was found. See NearbyImages.svelte. Propreties: 'rotation':number,'spherical':'yes'|'no'" | ||||||
|  |   }, | ||||||
|   "pointRendering": [ |   "pointRendering": [ | ||||||
|     { |     { | ||||||
|       "location": [ |       "location": [ | ||||||
|  | @ -48,7 +51,15 @@ | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       ], |       ], | ||||||
|       "iconSize": "14,14" |       "iconSize": { | ||||||
|  |         "render": "14,14", | ||||||
|  |         "mappings": [ | ||||||
|  |           { | ||||||
|  |             "if": "spherical=yes", | ||||||
|  |             "then": "28,28" | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "location": [ |       "location": [ | ||||||
|  |  | ||||||
|  | @ -858,21 +858,24 @@ | ||||||
|             { |             { | ||||||
|               "question": { |               "question": { | ||||||
|                 "en": "All platforms", |                 "en": "All platforms", | ||||||
|                 "cs": "Všechny platformy" |                 "cs": "Všechny platformy", | ||||||
|  |                 "de": "Alle Plattformen" | ||||||
|               }, |               }, | ||||||
|               "quesiton": "All platforms" |               "quesiton": "All platforms" | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|               "question": { |               "question": { | ||||||
|                 "en": "Made with Android", |                 "en": "Made with Android", | ||||||
|                 "cs": "Vytvořeno s Androidem" |                 "cs": "Vytvořeno s Androidem", | ||||||
|  |                 "de": "Mit Android erstellt" | ||||||
|               }, |               }, | ||||||
|               "osmTags": "android=yes" |               "osmTags": "android=yes" | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|               "question": { |               "question": { | ||||||
|                 "en": "Made on the web", |                 "en": "Made on the web", | ||||||
|                 "cs": "Vytvořeno na webu" |                 "cs": "Vytvořeno na webu", | ||||||
|  |                 "de": "Im Internet erstellt" | ||||||
|               }, |               }, | ||||||
|               "osmTags": "android=" |               "osmTags": "android=" | ||||||
|             } |             } | ||||||
|  |  | ||||||
							
								
								
									
										117
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										117
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -15,7 +15,6 @@ | ||||||
|         "@comunica/core": "^3.0.1", |         "@comunica/core": "^3.0.1", | ||||||
|         "@comunica/query-sparql": "^3.0.1", |         "@comunica/query-sparql": "^3.0.1", | ||||||
|         "@comunica/query-sparql-link-traversal": "^0.3.0", |         "@comunica/query-sparql-link-traversal": "^0.3.0", | ||||||
|         "@photo-sphere-viewer/equirectangular-tiles-adapter": "^5.12.1", |  | ||||||
|         "@rapideditor/location-conflation": "^1.3.0", |         "@rapideditor/location-conflation": "^1.3.0", | ||||||
|         "@rgossiaux/svelte-headlessui": "^1.0.2", |         "@rgossiaux/svelte-headlessui": "^1.0.2", | ||||||
|         "@rgossiaux/svelte-heroicons": "^0.1.2", |         "@rgossiaux/svelte-heroicons": "^0.1.2", | ||||||
|  | @ -30,6 +29,7 @@ | ||||||
|         "@types/dompurify": "^3.0.2", |         "@types/dompurify": "^3.0.2", | ||||||
|         "@types/follow-redirects": "^1.14.4", |         "@types/follow-redirects": "^1.14.4", | ||||||
|         "@types/node": "^22.13.5", |         "@types/node": "^22.13.5", | ||||||
|  |         "@types/pannellum": "^2.5.0", | ||||||
|         "@types/pg": "^8.11.11", |         "@types/pg": "^8.11.11", | ||||||
|         "@types/qrcode-generator": "^1.0.6", |         "@types/qrcode-generator": "^1.0.6", | ||||||
|         "@types/showdown": "^2.0.0", |         "@types/showdown": "^2.0.0", | ||||||
|  | @ -71,11 +71,11 @@ | ||||||
|         "opening_hours": "^3.6.0", |         "opening_hours": "^3.6.0", | ||||||
|         "osm-auth": "^2.6.0", |         "osm-auth": "^2.6.0", | ||||||
|         "osmtogeojson": "^3.0.0-beta.5", |         "osmtogeojson": "^3.0.0-beta.5", | ||||||
|  |         "pannellum": "^2.5.6", | ||||||
|         "panoramax-js": "^0.4.8", |         "panoramax-js": "^0.4.8", | ||||||
|         "panzoom": "^9.4.3", |         "panzoom": "^9.4.3", | ||||||
|         "papaparse": "^5.5.2", |         "papaparse": "^5.5.2", | ||||||
|         "pg": "^8.11.3", |         "pg": "^8.11.3", | ||||||
|         "photo-sphere-viewer": "^4.8.1", |  | ||||||
|         "pic4carto": "^2.1.15", |         "pic4carto": "^2.1.15", | ||||||
|         "pluscodes": "^2.6.0", |         "pluscodes": "^2.6.0", | ||||||
|         "pmtiles": "^4.2.1", |         "pmtiles": "^4.2.1", | ||||||
|  | @ -6275,32 +6275,6 @@ | ||||||
|         "url": "https://opencollective.com/parcel" |         "url": "https://opencollective.com/parcel" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@photo-sphere-viewer/core": { |  | ||||||
|       "version": "5.12.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.12.1.tgz", |  | ||||||
|       "integrity": "sha512-aK+SueXdKOr5FQAMwjxswHaa2OZcpWi4tx5P4fjq1vWEDa8PtdaoSdQaAp3Csmthvd9DlfNDUb6c21fTudzM/w==", |  | ||||||
|       "license": "MIT", |  | ||||||
|       "peer": true, |  | ||||||
|       "dependencies": { |  | ||||||
|         "three": "^0.173.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@photo-sphere-viewer/core/node_modules/three": { |  | ||||||
|       "version": "0.173.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/three/-/three-0.173.0.tgz", |  | ||||||
|       "integrity": "sha512-AUwVmViIEUgBwxJJ7stnF0NkPpZxx1aZ6WiAbQ/Qq61h6I9UR4grXtZDmO8mnlaNORhHnIBlXJ1uBxILEKuVyw==", |  | ||||||
|       "license": "MIT", |  | ||||||
|       "peer": true |  | ||||||
|     }, |  | ||||||
|     "node_modules/@photo-sphere-viewer/equirectangular-tiles-adapter": { |  | ||||||
|       "version": "5.12.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-tiles-adapter/-/equirectangular-tiles-adapter-5.12.1.tgz", |  | ||||||
|       "integrity": "sha512-Z9oiPNQwBdkGD1m+bXe0EuuBgdZFzec+d7MKexYgEqzLLukgp1WJ4il+3omMaRP5HAhRVWR5vapVALag+8BmPg==", |  | ||||||
|       "license": "MIT", |  | ||||||
|       "peerDependencies": { |  | ||||||
|         "@photo-sphere-viewer/core": "5.12.1" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@pkgjs/parseargs": { |     "node_modules/@pkgjs/parseargs": { | ||||||
|       "version": "0.11.0", |       "version": "0.11.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", |       "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", | ||||||
|  | @ -11480,6 +11454,12 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", |       "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", | ||||||
|       "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" |       "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@types/pannellum": { | ||||||
|  |       "version": "2.5.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/pannellum/-/pannellum-2.5.0.tgz", | ||||||
|  |       "integrity": "sha512-iFVwMHmsTx91t74gU12bDmB1ty5lRgmfK6X+FxymQe8n0nuw3Pp/vk0nw73YdL9WqZgthrpf1KLPzQjZDUsj0g==", | ||||||
|  |       "license": "MIT" | ||||||
|  |     }, | ||||||
|     "node_modules/@types/papaparse": { |     "node_modules/@types/papaparse": { | ||||||
|       "version": "5.3.15", |       "version": "5.3.15", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.15.tgz", |       "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.15.tgz", | ||||||
|  | @ -23024,6 +23004,12 @@ | ||||||
|       "version": "1.0.0", |       "version": "1.0.0", | ||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/pannellum": { | ||||||
|  |       "version": "2.5.6", | ||||||
|  |       "resolved": "https://registry.npmjs.org/pannellum/-/pannellum-2.5.6.tgz", | ||||||
|  |       "integrity": "sha512-R4kSPpj36wQPlyIi9ZftxPfVYF11DEbNBATUEI+pkMGZDFYBV5Jxi6tYFVDdmxA2xaTeKZQHMIuIIj7njVSTQQ==", | ||||||
|  |       "license": "MIT" | ||||||
|  |     }, | ||||||
|     "node_modules/panoramax-js": { |     "node_modules/panoramax-js": { | ||||||
|       "version": "0.4.8", |       "version": "0.4.8", | ||||||
|       "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.4.8.tgz", |       "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.4.8.tgz", | ||||||
|  | @ -23258,17 +23244,6 @@ | ||||||
|         "split2": "^4.1.0" |         "split2": "^4.1.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/photo-sphere-viewer": { |  | ||||||
|       "version": "4.8.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/photo-sphere-viewer/-/photo-sphere-viewer-4.8.1.tgz", |  | ||||||
|       "integrity": "sha512-Yl1KZq1adtrajCOrf8Y79Qi4A35DfEu8atL779YOdA9XHoH2l2+sYovejnZlGgUM0hEbTyenRDoyXSy/MtioYg==", |  | ||||||
|       "deprecated": "Use @photo-sphere-viewer/core instead, see https://photo-sphere-viewer.js.org/guide/migration.html", |  | ||||||
|       "license": "MIT", |  | ||||||
|       "dependencies": { |  | ||||||
|         "three": "^0.147.0", |  | ||||||
|         "uevent": "^2.1.1" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/pic4carto": { |     "node_modules/pic4carto": { | ||||||
|       "version": "2.1.15", |       "version": "2.1.15", | ||||||
|       "license": "SEE LICENSE IN LICENSE.txt", |       "license": "SEE LICENSE IN LICENSE.txt", | ||||||
|  | @ -26747,12 +26722,6 @@ | ||||||
|         "node": ">=0.8" |         "node": ">=0.8" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/three": { |  | ||||||
|       "version": "0.147.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/three/-/three-0.147.0.tgz", |  | ||||||
|       "integrity": "sha512-LPTOslYQXFkmvceQjFTNnVVli2LaVF6C99Pv34fJypp8NbQLbTlu3KinZ0zURghS5zEehK+VQyvWuPZ/Sm8fzw==", |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/through": { |     "node_modules/through": { | ||||||
|       "version": "2.3.8", |       "version": "2.3.8", | ||||||
|       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", |       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", | ||||||
|  | @ -27639,12 +27608,6 @@ | ||||||
|         "node": ">=4.2.0" |         "node": ">=4.2.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/uevent": { |  | ||||||
|       "version": "2.2.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/uevent/-/uevent-2.2.0.tgz", |  | ||||||
|       "integrity": "sha512-48s5LF/c6R1fUmctGib/dWKhZjZLd4aK/85dwVAbwgHNBSO0k0UNp0ZKZpkSbU6633qYhgykYQPakTSuOxZopA==", |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/uglify-js": { |     "node_modules/uglify-js": { | ||||||
|       "version": "3.19.3", |       "version": "3.19.3", | ||||||
|       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", |       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", | ||||||
|  | @ -34198,29 +34161,6 @@ | ||||||
|       "version": "2.8.2", |       "version": "2.8.2", | ||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "@photo-sphere-viewer/core": { |  | ||||||
|       "version": "5.12.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.12.1.tgz", |  | ||||||
|       "integrity": "sha512-aK+SueXdKOr5FQAMwjxswHaa2OZcpWi4tx5P4fjq1vWEDa8PtdaoSdQaAp3Csmthvd9DlfNDUb6c21fTudzM/w==", |  | ||||||
|       "peer": true, |  | ||||||
|       "requires": { |  | ||||||
|         "three": "^0.173.0" |  | ||||||
|       }, |  | ||||||
|       "dependencies": { |  | ||||||
|         "three": { |  | ||||||
|           "version": "0.173.0", |  | ||||||
|           "resolved": "https://registry.npmjs.org/three/-/three-0.173.0.tgz", |  | ||||||
|           "integrity": "sha512-AUwVmViIEUgBwxJJ7stnF0NkPpZxx1aZ6WiAbQ/Qq61h6I9UR4grXtZDmO8mnlaNORhHnIBlXJ1uBxILEKuVyw==", |  | ||||||
|           "peer": true |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "@photo-sphere-viewer/equirectangular-tiles-adapter": { |  | ||||||
|       "version": "5.12.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-tiles-adapter/-/equirectangular-tiles-adapter-5.12.1.tgz", |  | ||||||
|       "integrity": "sha512-Z9oiPNQwBdkGD1m+bXe0EuuBgdZFzec+d7MKexYgEqzLLukgp1WJ4il+3omMaRP5HAhRVWR5vapVALag+8BmPg==", |  | ||||||
|       "requires": {} |  | ||||||
|     }, |  | ||||||
|     "@pkgjs/parseargs": { |     "@pkgjs/parseargs": { | ||||||
|       "version": "0.11.0", |       "version": "0.11.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", |       "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", | ||||||
|  | @ -38418,6 +38358,11 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", |       "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", | ||||||
|       "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" |       "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" | ||||||
|     }, |     }, | ||||||
|  |     "@types/pannellum": { | ||||||
|  |       "version": "2.5.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/pannellum/-/pannellum-2.5.0.tgz", | ||||||
|  |       "integrity": "sha512-iFVwMHmsTx91t74gU12bDmB1ty5lRgmfK6X+FxymQe8n0nuw3Pp/vk0nw73YdL9WqZgthrpf1KLPzQjZDUsj0g==" | ||||||
|  |     }, | ||||||
|     "@types/papaparse": { |     "@types/papaparse": { | ||||||
|       "version": "5.3.15", |       "version": "5.3.15", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.15.tgz", |       "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.15.tgz", | ||||||
|  | @ -46271,6 +46216,11 @@ | ||||||
|     "packet-reader": { |     "packet-reader": { | ||||||
|       "version": "1.0.0" |       "version": "1.0.0" | ||||||
|     }, |     }, | ||||||
|  |     "pannellum": { | ||||||
|  |       "version": "2.5.6", | ||||||
|  |       "resolved": "https://registry.npmjs.org/pannellum/-/pannellum-2.5.6.tgz", | ||||||
|  |       "integrity": "sha512-R4kSPpj36wQPlyIi9ZftxPfVYF11DEbNBATUEI+pkMGZDFYBV5Jxi6tYFVDdmxA2xaTeKZQHMIuIIj7njVSTQQ==" | ||||||
|  |     }, | ||||||
|     "panoramax-js": { |     "panoramax-js": { | ||||||
|       "version": "0.4.8", |       "version": "0.4.8", | ||||||
|       "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.4.8.tgz", |       "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.4.8.tgz", | ||||||
|  | @ -46429,15 +46379,6 @@ | ||||||
|         "split2": "^4.1.0" |         "split2": "^4.1.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "photo-sphere-viewer": { |  | ||||||
|       "version": "4.8.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/photo-sphere-viewer/-/photo-sphere-viewer-4.8.1.tgz", |  | ||||||
|       "integrity": "sha512-Yl1KZq1adtrajCOrf8Y79Qi4A35DfEu8atL779YOdA9XHoH2l2+sYovejnZlGgUM0hEbTyenRDoyXSy/MtioYg==", |  | ||||||
|       "requires": { |  | ||||||
|         "three": "^0.147.0", |  | ||||||
|         "uevent": "^2.1.1" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "pic4carto": { |     "pic4carto": { | ||||||
|       "version": "2.1.15", |       "version": "2.1.15", | ||||||
|       "requires": { |       "requires": { | ||||||
|  | @ -48832,11 +48773,6 @@ | ||||||
|         "thenify": ">= 3.1.0 < 4" |         "thenify": ">= 3.1.0 < 4" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "three": { |  | ||||||
|       "version": "0.147.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/three/-/three-0.147.0.tgz", |  | ||||||
|       "integrity": "sha512-LPTOslYQXFkmvceQjFTNnVVli2LaVF6C99Pv34fJypp8NbQLbTlu3KinZ0zURghS5zEehK+VQyvWuPZ/Sm8fzw==" |  | ||||||
|     }, |  | ||||||
|     "through": { |     "through": { | ||||||
|       "version": "2.3.8", |       "version": "2.3.8", | ||||||
|       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", |       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", | ||||||
|  | @ -49524,11 +49460,6 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", |       "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", | ||||||
|       "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" |       "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" | ||||||
|     }, |     }, | ||||||
|     "uevent": { |  | ||||||
|       "version": "2.2.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/uevent/-/uevent-2.2.0.tgz", |  | ||||||
|       "integrity": "sha512-48s5LF/c6R1fUmctGib/dWKhZjZLd4aK/85dwVAbwgHNBSO0k0UNp0ZKZpkSbU6633qYhgykYQPakTSuOxZopA==" |  | ||||||
|     }, |  | ||||||
|     "uglify-js": { |     "uglify-js": { | ||||||
|       "version": "3.19.3", |       "version": "3.19.3", | ||||||
|       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", |       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", | ||||||
|  |  | ||||||
|  | @ -156,7 +156,8 @@ | ||||||
|     "android:prepare": "./scripts/prepareAndroid.sh", |     "android:prepare": "./scripts/prepareAndroid.sh", | ||||||
|     "android:build": "./scripts/buildAndroid.sh", |     "android:build": "./scripts/buildAndroid.sh", | ||||||
|     "android:upload": "scp ./android/app/build/outputs/apk/release/app-release.apk hetzner:apk/mapcomplete-latest.apk && scp ./android/app/build/outputs/apk/release/app-release.apk hetzner:app/mapcomplete-latest.apk", |     "android:upload": "scp ./android/app/build/outputs/apk/release/app-release.apk hetzner:apk/mapcomplete-latest.apk && scp ./android/app/build/outputs/apk/release/app-release.apk hetzner:app/mapcomplete-latest.apk", | ||||||
|     "android:uninstall": "adb shell pm uninstall org.mapcomplete" |     "android:uninstall": "adb shell pm uninstall org.mapcomplete", | ||||||
|  |     "postinstall": "./scripts/fixPannellum.sh" | ||||||
|   }, |   }, | ||||||
|   "keywords": [ |   "keywords": [ | ||||||
|     "OpenStreetMap", |     "OpenStreetMap", | ||||||
|  | @ -177,7 +178,6 @@ | ||||||
|     "@comunica/core": "^3.0.1", |     "@comunica/core": "^3.0.1", | ||||||
|     "@comunica/query-sparql": "^3.0.1", |     "@comunica/query-sparql": "^3.0.1", | ||||||
|     "@comunica/query-sparql-link-traversal": "^0.3.0", |     "@comunica/query-sparql-link-traversal": "^0.3.0", | ||||||
|     "@photo-sphere-viewer/equirectangular-tiles-adapter": "^5.12.1", |  | ||||||
|     "@rapideditor/location-conflation": "^1.3.0", |     "@rapideditor/location-conflation": "^1.3.0", | ||||||
|     "@rgossiaux/svelte-headlessui": "^1.0.2", |     "@rgossiaux/svelte-headlessui": "^1.0.2", | ||||||
|     "@rgossiaux/svelte-heroicons": "^0.1.2", |     "@rgossiaux/svelte-heroicons": "^0.1.2", | ||||||
|  | @ -192,6 +192,7 @@ | ||||||
|     "@types/dompurify": "^3.0.2", |     "@types/dompurify": "^3.0.2", | ||||||
|     "@types/follow-redirects": "^1.14.4", |     "@types/follow-redirects": "^1.14.4", | ||||||
|     "@types/node": "^22.13.5", |     "@types/node": "^22.13.5", | ||||||
|  |     "@types/pannellum": "^2.5.0", | ||||||
|     "@types/pg": "^8.11.11", |     "@types/pg": "^8.11.11", | ||||||
|     "@types/qrcode-generator": "^1.0.6", |     "@types/qrcode-generator": "^1.0.6", | ||||||
|     "@types/showdown": "^2.0.0", |     "@types/showdown": "^2.0.0", | ||||||
|  | @ -233,11 +234,11 @@ | ||||||
|     "opening_hours": "^3.6.0", |     "opening_hours": "^3.6.0", | ||||||
|     "osm-auth": "^2.6.0", |     "osm-auth": "^2.6.0", | ||||||
|     "osmtogeojson": "^3.0.0-beta.5", |     "osmtogeojson": "^3.0.0-beta.5", | ||||||
|  |     "pannellum": "^2.5.6", | ||||||
|     "panoramax-js": "^0.4.8", |     "panoramax-js": "^0.4.8", | ||||||
|     "panzoom": "^9.4.3", |     "panzoom": "^9.4.3", | ||||||
|     "papaparse": "^5.5.2", |     "papaparse": "^5.5.2", | ||||||
|     "pg": "^8.11.3", |     "pg": "^8.11.3", | ||||||
|     "photo-sphere-viewer": "^4.8.1", |  | ||||||
|     "pic4carto": "^2.1.15", |     "pic4carto": "^2.1.15", | ||||||
|     "pluscodes": "^2.6.0", |     "pluscodes": "^2.6.0", | ||||||
|     "pmtiles": "^4.2.1", |     "pmtiles": "^4.2.1", | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 145 KiB | 
|  | @ -9,6 +9,7 @@ import xml2js from "xml2js" | ||||||
| export default class ScriptUtils { | export default class ScriptUtils { | ||||||
|     public static fixUtils() { |     public static fixUtils() { | ||||||
|         Utils.externalDownloadFunction = ScriptUtils.Download |         Utils.externalDownloadFunction = ScriptUtils.Download | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								scripts/fixPannellum.sh
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								scripts/fixPannellum.sh
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | #! /bin/bash | ||||||
|  | 
 | ||||||
|  | # Are you ready to feel really dirty? | ||||||
|  | # Pannellum is a direct import (not a module!) which uses window.pannellum = function... | ||||||
|  | # This breaks when importing this in nodeJS | ||||||
|  | # So, we patch it up... | ||||||
|  | echo "Fixing pannellum..." | ||||||
|  | sed -i 's/^window./if(typeof window !== "undefined")\n&/' "./node_modules/pannellum/build/pannellum.js" | ||||||
|  | @ -44,4 +44,8 @@ export default class GenericImageProvider extends ImageProvider { | ||||||
|     public DownloadAttribution(_) { |     public DownloadAttribution(_) { | ||||||
|         return undefined |         return undefined | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     getPanoramaInfo(image: { id: string }): undefined { | ||||||
|  |         return undefined | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,8 @@ | ||||||
| import { Store, Stores, UIEventSource } from "../UIEventSource" | import { Store, Stores } from "../UIEventSource" | ||||||
| import BaseUIElement from "../../UI/BaseUIElement" | import BaseUIElement from "../../UI/BaseUIElement" | ||||||
| import { LicenseInfo } from "./LicenseInfo" | import { LicenseInfo } from "./LicenseInfo" | ||||||
| import { Utils } from "../../Utils" | import { Utils } from "../../Utils" | ||||||
|  | import { Feature, Point } from "geojson" | ||||||
| 
 | 
 | ||||||
| export interface ProvidedImage { | export interface ProvidedImage { | ||||||
|     url: string |     url: string | ||||||
|  | @ -19,6 +20,17 @@ export interface ProvidedImage { | ||||||
|     lat?: number |     lat?: number | ||||||
|     lon?: number |     lon?: number | ||||||
|     host?: string |     host?: string | ||||||
|  |     isSpherical?: boolean | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface PanoramaView { | ||||||
|  |     url: string, | ||||||
|  |     /** | ||||||
|  |      * 0 - 359 | ||||||
|  |      * Degrees in which the picture is taken, with north = 0; going clockwise | ||||||
|  |      */ | ||||||
|  |     northOffset?: number, | ||||||
|  |     pitchOffset?: number | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default abstract class ImageProvider { | export default abstract class ImageProvider { | ||||||
|  | @ -89,6 +101,8 @@ export default abstract class ImageProvider { | ||||||
| 
 | 
 | ||||||
|     public abstract apiUrls(): string[] |     public abstract apiUrls(): string[] | ||||||
| 
 | 
 | ||||||
|  |     public abstract getPanoramaInfo(image: { id: string }): Promise<Feature<Point, PanoramaView>> | undefined; | ||||||
|  | 
 | ||||||
|     public static async offerImageAsDownload(image: ProvidedImage) { |     public static async offerImageAsDownload(image: ProvidedImage) { | ||||||
|         const response = await fetch(image.url_hd ?? image.url) |         const response = await fetch(image.url_hd ?? image.url) | ||||||
|         const blob = await response.blob() |         const blob = await response.blob() | ||||||
|  | @ -96,4 +110,5 @@ export default abstract class ImageProvider { | ||||||
|             mimetype: "image/jpg", |             mimetype: "image/jpg", | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -116,4 +116,8 @@ export class Imgur extends ImageProvider { | ||||||
| 
 | 
 | ||||||
|         return license |         return license | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     getPanoramaInfo(image: { id: string }): undefined { | ||||||
|  |         return undefined | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,10 +1,11 @@ | ||||||
| import ImageProvider, { ProvidedImage } from "./ImageProvider" | import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider" | ||||||
| import BaseUIElement from "../../UI/BaseUIElement" | import BaseUIElement from "../../UI/BaseUIElement" | ||||||
| import { Utils } from "../../Utils" | import { Utils } from "../../Utils" | ||||||
| import { LicenseInfo } from "./LicenseInfo" | import { LicenseInfo } from "./LicenseInfo" | ||||||
| import Constants from "../../Models/Constants" | import Constants from "../../Models/Constants" | ||||||
| import SvelteUIElement from "../../UI/Base/SvelteUIElement" | import SvelteUIElement from "../../UI/Base/SvelteUIElement" | ||||||
| import MapillaryIcon from "./MapillaryIcon.svelte" | import MapillaryIcon from "./MapillaryIcon.svelte" | ||||||
|  | import { Feature, Point } from "geojson" | ||||||
| 
 | 
 | ||||||
| export class Mapillary extends ImageProvider { | export class Mapillary extends ImageProvider { | ||||||
|     public static readonly singleton = new Mapillary() |     public static readonly singleton = new Mapillary() | ||||||
|  | @ -16,7 +17,7 @@ export class Mapillary extends ImageProvider { | ||||||
|         "http://mapillary.com", |         "http://mapillary.com", | ||||||
|         "https://mapillary.com", |         "https://mapillary.com", | ||||||
|         "http://www.mapillary.com", |         "http://www.mapillary.com", | ||||||
|         "https://www.mapillary.com", |         "https://www.mapillary.com" | ||||||
|     ] |     ] | ||||||
|     defaultKeyPrefixes = ["mapillary", "image"] |     defaultKeyPrefixes = ["mapillary", "image"] | ||||||
| 
 | 
 | ||||||
|  | @ -69,7 +70,7 @@ export class Mapillary extends ImageProvider { | ||||||
|             lat: location?.lat, |             lat: location?.lat, | ||||||
|             lng: location?.lon, |             lng: location?.lon, | ||||||
|             z: location === undefined ? undefined : Math.max((zoom ?? 2) - 1, 1), |             z: location === undefined ? undefined : Math.max((zoom ?? 2) - 1, 1), | ||||||
|             pKey, |             pKey | ||||||
|         } |         } | ||||||
|         const baselink = `https://www.mapillary.com/app/?` |         const baselink = `https://www.mapillary.com/app/?` | ||||||
|         const paramsStr = Utils.NoNull( |         const paramsStr = Utils.NoNull( | ||||||
|  | @ -137,6 +138,41 @@ export class Mapillary extends ImageProvider { | ||||||
|         return [img] |         return [img] | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Download data necessary for the 360°-viewer | ||||||
|  |      * @param pkey | ||||||
|  |      * @constructor | ||||||
|  |      */ | ||||||
|  |     public async getPanoramaInfo(image: { id: number | string }): Promise<Feature<Point, PanoramaView>> { | ||||||
|  |         const pkey = image.id | ||||||
|  |         const metadataUrl = | ||||||
|  |             "https://graph.mapillary.com/" + | ||||||
|  |             pkey + | ||||||
|  |             "?fields=computed_compass_angle,geometry,is_pano,thumb_2048_url,thumb_original_url&access_token=" + | ||||||
|  |             Constants.mapillary_client_token_v4 | ||||||
|  |         const response = await Utils.downloadJsonCached< | ||||||
|  |             { | ||||||
|  |                 computed_compass_angle: number, | ||||||
|  |                 geometry: Point, | ||||||
|  | 
 | ||||||
|  |                 is_pano: boolean, | ||||||
|  |                 thumb_2048_url: string, | ||||||
|  |                 thumb_original_url: string, | ||||||
|  |                 id: string, | ||||||
|  | 
 | ||||||
|  |             }>(metadataUrl, 60 * 60) | ||||||
|  |         return { | ||||||
|  |             type: "Feature", | ||||||
|  |             geometry: response.geometry, | ||||||
|  |             properties: { | ||||||
|  |                 url: response.thumb_2048_url, | ||||||
|  |                 northOffset: response.computed_compass_angle | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     public async DownloadAttribution(providedImage: { id: string }): Promise<LicenseInfo> { |     public async DownloadAttribution(providedImage: { id: string }): Promise<LicenseInfo> { | ||||||
|         const mapillaryId = providedImage.id |         const mapillaryId = providedImage.id | ||||||
|         const metadataUrl = |         const metadataUrl = | ||||||
|  | @ -182,7 +218,7 @@ export class Mapillary extends ImageProvider { | ||||||
|             key, |             key, | ||||||
|             rotation, |             rotation, | ||||||
|             lat: geometry.coordinates[1], |             lat: geometry.coordinates[1], | ||||||
|             lon: geometry.coordinates[0], |             lon: geometry.coordinates[0] | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import { ImageUploader } from "./ImageUploader" | import { ImageUploader } from "./ImageUploader" | ||||||
| import { AuthorizedPanoramax, ImageData, Panoramax, PanoramaxXYZ } from "panoramax-js/dist" | import { AuthorizedPanoramax, ImageData, Panoramax, PanoramaxXYZ } from "panoramax-js/dist" | ||||||
| import ExifReader from "exifreader" | import ExifReader from "exifreader" | ||||||
| import ImageProvider, { ProvidedImage } from "./ImageProvider" | import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider" | ||||||
| import BaseUIElement from "../../UI/BaseUIElement" | import BaseUIElement from "../../UI/BaseUIElement" | ||||||
| import { LicenseInfo } from "./LicenseInfo" | import { LicenseInfo } from "./LicenseInfo" | ||||||
| import { GeoOperations } from "../GeoOperations" | import { GeoOperations } from "../GeoOperations" | ||||||
|  | @ -10,6 +10,7 @@ import { Store, Stores, UIEventSource } from "../UIEventSource" | ||||||
| import SvelteUIElement from "../../UI/Base/SvelteUIElement" | import SvelteUIElement from "../../UI/Base/SvelteUIElement" | ||||||
| import Panoramax_bw from "../../assets/svg/Panoramax_bw.svelte" | import Panoramax_bw from "../../assets/svg/Panoramax_bw.svelte" | ||||||
| import Link from "../../UI/Base/Link" | import Link from "../../UI/Base/Link" | ||||||
|  | import { Feature, Point } from "geojson" | ||||||
| 
 | 
 | ||||||
| export default class PanoramaxImageProvider extends ImageProvider { | export default class PanoramaxImageProvider extends ImageProvider { | ||||||
|     public static readonly singleton: PanoramaxImageProvider = new PanoramaxImageProvider() |     public static readonly singleton: PanoramaxImageProvider = new PanoramaxImageProvider() | ||||||
|  | @ -187,6 +188,20 @@ export default class PanoramaxImageProvider extends ImageProvider { | ||||||
|         } |         } | ||||||
|         return new Panoramax(host) |         return new Panoramax(host) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     public async getPanoramaInfo(image: { id: string }): Promise<Feature<Point, PanoramaView>> | undefined { | ||||||
|  |         const imageInfo = await PanoramaxImageProvider.xyz.imageInfo(image.id) | ||||||
|  |         const url = (imageInfo.assets.sd ?? imageInfo.assets.thumb ?? imageInfo.assets.hd).href | ||||||
|  |         const northOffset = imageInfo.properties["view:azimuth"] | ||||||
|  |         const pitchOffset = Number(imageInfo.properties.exif["Xmp.GPano.PosePitchDegrees"]) | ||||||
|  |         return <Feature<Point, PanoramaView>>{ | ||||||
|  |             type: "Feature", | ||||||
|  |             geometry: imageInfo.geometry, | ||||||
|  |             properties: { | ||||||
|  |                 url, northOffset, pitchOffset | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class PanoramaxUploader implements ImageUploader { | export class PanoramaxUploader implements ImageUploader { | ||||||
|  |  | ||||||
|  | @ -1,42 +1,14 @@ | ||||||
| import { Utils } from "../../Utils" | import { Utils } from "../../Utils" | ||||||
| /** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */ | /** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */ | ||||||
| export class ThemeMetaTagging { | export class ThemeMetaTagging { | ||||||
|     public static readonly themeName = "usersettings" |    public static readonly themeName = "usersettings" | ||||||
| 
 | 
 | ||||||
|     public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) { |    public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) { | ||||||
|         Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () => |       Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )  | ||||||
|             feat.properties._description |       Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )  | ||||||
|                 .match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/) |       Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href   }) (feat)  )  | ||||||
|                 ?.at(1) |       Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat)  )  | ||||||
|         ) |       Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )  | ||||||
|         Utils.AddLazyProperty( |       feat.properties['__current_backgroun'] = 'initial_value' | ||||||
|             feat.properties, |    } | ||||||
|             "_d", | } | ||||||
|             () => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? "" |  | ||||||
|         ) |  | ||||||
|         Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () => |  | ||||||
|             ((feat) => { |  | ||||||
|                 const e = document.createElement("div") |  | ||||||
|                 e.innerHTML = feat.properties._d |  | ||||||
|                 return Array.from(e.getElementsByTagName("a")).filter( |  | ||||||
|                     (a) => a.href.match(/mastodon|en.osm.town/) !== null |  | ||||||
|                 )[0]?.href |  | ||||||
|             })(feat) |  | ||||||
|         ) |  | ||||||
|         Utils.AddLazyProperty(feat.properties, "_mastodon_link", () => |  | ||||||
|             ((feat) => { |  | ||||||
|                 const e = document.createElement("div") |  | ||||||
|                 e.innerHTML = feat.properties._d |  | ||||||
|                 return Array.from(e.getElementsByTagName("a")).filter( |  | ||||||
|                     (a) => a.getAttribute("rel")?.indexOf("me") >= 0 |  | ||||||
|                 )[0]?.href |  | ||||||
|             })(feat) |  | ||||||
|         ) |  | ||||||
|         Utils.AddLazyProperty( |  | ||||||
|             feat.properties, |  | ||||||
|             "_mastodon_candidate", |  | ||||||
|             () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a |  | ||||||
|         ) |  | ||||||
|         feat.properties["__current_backgroun"] = "initial_value" |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -7,8 +7,6 @@ import { BBox } from "../BBox" | ||||||
| import Constants from "../../Models/Constants" | import Constants from "../../Models/Constants" | ||||||
| import { Utils } from "../../Utils" | import { Utils } from "../../Utils" | ||||||
| import { Point } from "geojson" | import { Point } from "geojson" | ||||||
| import MvtSource from "../FeatureSource/Sources/MvtSource" |  | ||||||
| import AllImageProviders from "../ImageProviders/AllImageProviders" |  | ||||||
| import { Imgur } from "../ImageProviders/Imgur" | import { Imgur } from "../ImageProviders/Imgur" | ||||||
| import { Panoramax, PanoramaxXYZ } from "panoramax-js/dist" | import { Panoramax, PanoramaxXYZ } from "panoramax-js/dist" | ||||||
| 
 | 
 | ||||||
|  | @ -211,111 +209,6 @@ class ImagesFromPanoramaxFetcher implements ImageFetcher { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ImagesFromCacheServerFetcher implements ImageFetcher { |  | ||||||
|     private readonly _searchRadius: number |  | ||||||
|     public readonly name = "fromCacheServer" |  | ||||||
|     private readonly _serverUrl: string |  | ||||||
| 
 |  | ||||||
|     constructor(searchRadius: number = 500, serverUrl: string = Constants.VectorTileServer) { |  | ||||||
|         this._searchRadius = searchRadius |  | ||||||
|         this._serverUrl = serverUrl |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async fetchImages(lat: number, lon: number): Promise<P4CPicture[]> { |  | ||||||
|         return ( |  | ||||||
|             await Promise.all([ |  | ||||||
|                 this.fetchImagesForType(lat, lon, "lines"), |  | ||||||
|                 this.fetchImagesForType(lat, lon, "pois"), |  | ||||||
|                 this.fetchImagesForType(lat, lon, "polygons"), |  | ||||||
|             ]) |  | ||||||
|         ).flatMap((x) => x) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async fetchImagesForType( |  | ||||||
|         targetlat: number, |  | ||||||
|         targetlon: number, |  | ||||||
|         type: "lines" | "pois" | "polygons" |  | ||||||
|     ): Promise<P4CPicture[]> { |  | ||||||
|         const { x, y, z } = Tiles.embedded_tile(targetlat, targetlon, 14) |  | ||||||
| 
 |  | ||||||
|         const url = this._serverUrl |  | ||||||
| 
 |  | ||||||
|         async function getFeatures(x: number, y: number) { |  | ||||||
|             const src = new MvtSource( |  | ||||||
|                 Utils.SubstituteKeys(url, { |  | ||||||
|                     type, |  | ||||||
|                     x, |  | ||||||
|                     y, |  | ||||||
|                     z, |  | ||||||
|                     layer: "item_with_image", |  | ||||||
|                 }), |  | ||||||
|                 x, |  | ||||||
|                 y, |  | ||||||
|                 z |  | ||||||
|             ) |  | ||||||
|             await src.updateAsync() |  | ||||||
|             return src.features.data |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const features = ( |  | ||||||
|             await Promise.all([ |  | ||||||
|                 getFeatures(x, y), |  | ||||||
|                 getFeatures(x, y + 1), |  | ||||||
|                 getFeatures(x, y - 1), |  | ||||||
| 
 |  | ||||||
|                 getFeatures(x + 1, y + 1), |  | ||||||
|                 getFeatures(x + 1, y), |  | ||||||
|                 getFeatures(x + 1, y - 1), |  | ||||||
| 
 |  | ||||||
|                 getFeatures(x - 1, y - 1), |  | ||||||
|                 getFeatures(x - 1, y), |  | ||||||
|                 getFeatures(x - 1, y + 1), |  | ||||||
|             ]) |  | ||||||
|         ).flatMap((x) => x) |  | ||||||
| 
 |  | ||||||
|         const pics: P4CPicture[] = [] |  | ||||||
|         for (const f of features) { |  | ||||||
|             const [lng, lat] = GeoOperations.centerpointCoordinates(f) |  | ||||||
|             if ( |  | ||||||
|                 GeoOperations.distanceBetween([targetlon, targetlat], [lng, lat]) > |  | ||||||
|                 this._searchRadius |  | ||||||
|             ) { |  | ||||||
|                 return [] |  | ||||||
|             } |  | ||||||
|             for (let i = -1; i < 50; i++) { |  | ||||||
|                 let key = "image" |  | ||||||
|                 if (i >= 0) { |  | ||||||
|                     key += ":" + i |  | ||||||
|                 } |  | ||||||
|                 const v = f.properties[key] |  | ||||||
|                 console.log(v) |  | ||||||
|                 if (!v) { |  | ||||||
|                     continue |  | ||||||
|                 } |  | ||||||
|                 let provider = "unkown" |  | ||||||
|                 try { |  | ||||||
|                     provider = (await AllImageProviders.selectBestProvider("image", v))?.name |  | ||||||
|                 } catch (e) { |  | ||||||
|                     console.error("Could not detect provider for", "image", v) |  | ||||||
|                 } |  | ||||||
|                 pics.push({ |  | ||||||
|                     pictureUrl: v, |  | ||||||
|                     coordinates: { lat, lng }, |  | ||||||
|                     details: { |  | ||||||
|                         isSpherical: false, |  | ||||||
|                     }, |  | ||||||
|                     osmTags: { |  | ||||||
|                         image: v, |  | ||||||
|                     }, |  | ||||||
|                     thumbUrl: v, |  | ||||||
|                     provider, |  | ||||||
|                 }) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return pics |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class MapillaryFetcher implements ImageFetcher { | class MapillaryFetcher implements ImageFetcher { | ||||||
|     public readonly name = "mapillary_new" |     public readonly name = "mapillary_new" | ||||||
|     private readonly _panoramas: "only" | "no" | undefined |     private readonly _panoramas: "only" | "no" | undefined | ||||||
|  | @ -390,7 +283,7 @@ class MapillaryFetcher implements ImageFetcher { | ||||||
|                     mapillary: img.id, |                     mapillary: img.id, | ||||||
|                 }, |                 }, | ||||||
|                 details: { |                 details: { | ||||||
|                     isSpherical: img.is_pano, |                     isSpherical: this._panoramas === "only" | ||||||
|                 }, |                 }, | ||||||
|             }) |             }) | ||||||
|         } |         } | ||||||
|  | @ -407,15 +300,20 @@ export class CombinedFetcher { | ||||||
|     constructor(radius: number, maxage: Date, indexedFeatures: IndexedFeatureSource) { |     constructor(radius: number, maxage: Date, indexedFeatures: IndexedFeatureSource) { | ||||||
|         this.sources = [ |         this.sources = [ | ||||||
|             new ImagesInLoadedDataFetcher(indexedFeatures, radius), |             new ImagesInLoadedDataFetcher(indexedFeatures, radius), | ||||||
|             new ImagesFromCacheServerFetcher(radius), |  | ||||||
|             new ImagesFromPanoramaxFetcher(), |             new ImagesFromPanoramaxFetcher(), | ||||||
|             new ImagesFromPanoramaxFetcher(Constants.panoramax.url), |             new ImagesFromPanoramaxFetcher(Constants.panoramax.url), | ||||||
|  |             // For mapillary, we need to query both with and without panoramas. See https://www.mapillary.com/developer/api-documentation/
 | ||||||
|             new MapillaryFetcher({ |             new MapillaryFetcher({ | ||||||
|                 max_images: 25, |                 max_images: 25, | ||||||
|                 start_captured_at: maxage, |                 start_captured_at: maxage, | ||||||
|  |                 panoramas: "only" | ||||||
|             }), |             }), | ||||||
|             new P4CImageFetcher("mapillary"), |             new MapillaryFetcher({ | ||||||
|             new P4CImageFetcher("wikicommons"), |                 max_images: 25, | ||||||
|  |                 start_captured_at: maxage, | ||||||
|  |                 panoramas: "no" | ||||||
|  |             }), new P4CImageFetcher("mapillary"), | ||||||
|  |             new P4CImageFetcher("wikicommons") | ||||||
|         ].map((f) => new CachedFetcher(f)) |         ].map((f) => new CachedFetcher(f)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,8 @@ | ||||||
|    * Shows an image with attribution |    * Shows an image with attribution | ||||||
|    */ |    */ | ||||||
|   import ImageAttribution from "./ImageAttribution.svelte" |   import ImageAttribution from "./ImageAttribution.svelte" | ||||||
|  |   import { Store } from "../../Logic/UIEventSource" | ||||||
|  | 
 | ||||||
|   import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" |   import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" | ||||||
|   import { Mapillary } from "../../Logic/ImageProviders/Mapillary" |   import { Mapillary } from "../../Logic/ImageProviders/Mapillary" | ||||||
|   import { UIEventSource } from "../../Logic/UIEventSource" |   import { UIEventSource } from "../../Logic/UIEventSource" | ||||||
|  | @ -32,6 +34,8 @@ | ||||||
|   export let attributionFormat: "minimal" | "medium" | "large" = "medium" |   export let attributionFormat: "minimal" | "medium" | "large" = "medium" | ||||||
|   let previewedImage: UIEventSource<Partial<ProvidedImage>> = MenuState.previewedImage |   let previewedImage: UIEventSource<Partial<ProvidedImage>> = MenuState.previewedImage | ||||||
|   export let canZoom = previewedImage !== undefined |   export let canZoom = previewedImage !== undefined | ||||||
|  |   export let nearbyFeatures: Feature[] | Store<Feature[]> = [] | ||||||
|  | 
 | ||||||
|   let loaded = false |   let loaded = false | ||||||
|   let showBigPreview = new UIEventSource(false) |   let showBigPreview = new UIEventSource(false) | ||||||
|   onDestroy( |   onDestroy( | ||||||
|  | @ -74,9 +78,8 @@ | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <Popup shown={showBigPreview} bodyPadding="p-0" dismissable={true}> | <Popup shown={showBigPreview} bodyPadding="p-0" dismissable={true}> | ||||||
|   <div slot="close" /> |  | ||||||
|   <div style="height: 80vh"> |   <div style="height: 80vh"> | ||||||
|     <ImageOperations {image}> |     <ImageOperations {image} {nearbyFeatures}> | ||||||
|       <slot name="preview-action" /> |       <slot name="preview-action" /> | ||||||
|       <slot name="dot-menu-actions" slot="dot-menu-actions" /> |       <slot name="dot-menu-actions" slot="dot-menu-actions" /> | ||||||
|     </ImageOperations> |     </ImageOperations> | ||||||
|  |  | ||||||
|  | @ -14,9 +14,12 @@ | ||||||
|   import Tr from "../Base/Tr.svelte" |   import Tr from "../Base/Tr.svelte" | ||||||
|   import Translations from "../i18n/Translations" |   import Translations from "../i18n/Translations" | ||||||
|   import DotMenu from "../Base/DotMenu.svelte" |   import DotMenu from "../Base/DotMenu.svelte" | ||||||
|  |   import type { Feature } from "geojson" | ||||||
|  |   import { Store } from "../../Logic/UIEventSource" | ||||||
| 
 | 
 | ||||||
|   export let image: Partial<ProvidedImage> & { id: string; url: string } |   export let image: Partial<ProvidedImage> & { id: string; url: string } | ||||||
|   export let clss: string = undefined |   export let clss: string = undefined | ||||||
|  |   export let nearbyFeatures: Feature[] | Store<Feature[]> = [] | ||||||
| 
 | 
 | ||||||
|   let isLoaded = new UIEventSource(false) |   let isLoaded = new UIEventSource(false) | ||||||
| </script> | </script> | ||||||
|  | @ -28,7 +31,7 @@ | ||||||
|         <Loading /> |         <Loading /> | ||||||
|       </div> |       </div> | ||||||
|     {/if} |     {/if} | ||||||
|     <ImagePreview {image} {isLoaded} /> |     <ImagePreview {image} {isLoaded} {nearbyFeatures} /> | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|   <DotMenu dotsPosition="top-0 left-0" dotsSize="w-8 h-8" hideBackground> |   <DotMenu dotsPosition="top-0 left-0" dotsSize="w-8 h-8" hideBackground> | ||||||
|  |  | ||||||
|  | @ -6,23 +6,50 @@ | ||||||
|   import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" |   import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" | ||||||
|   import { UIEventSource } from "../../Logic/UIEventSource" |   import { UIEventSource } from "../../Logic/UIEventSource" | ||||||
|   import Zoomcontrol from "../Zoomcontrol" |   import Zoomcontrol from "../Zoomcontrol" | ||||||
|   import { onDestroy } from "svelte" |   import { getContext, onDestroy } from "svelte" | ||||||
|  |   import type { PanoramaView } from "./photoSphereViewerWrapper" | ||||||
|  |   import { PhotoSphereViewerWrapper } from "./photoSphereViewerWrapper" | ||||||
| 
 | 
 | ||||||
|  |   import type { Feature, Point } from "geojson" | ||||||
|  |   import { Store } from "../../Logic/UIEventSource" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   export let nearbyFeatures: Feature[] | Store<Feature[]> = [] | ||||||
|   export let image: Partial<ProvidedImage> |   export let image: Partial<ProvidedImage> | ||||||
|   let panzoomInstance = undefined |   let panzoomInstance = undefined | ||||||
|   let panzoomEl: HTMLElement |   let panzoomEl: HTMLElement | ||||||
|  |   let viewerEl: HTMLElement | ||||||
|  | 
 | ||||||
|   export let isLoaded: UIEventSource<boolean> = undefined |   export let isLoaded: UIEventSource<boolean> = undefined | ||||||
| 
 | 
 | ||||||
|   onDestroy(Zoomcontrol.createLock()) |   onDestroy(Zoomcontrol.createLock()) | ||||||
| 
 | 
 | ||||||
|  |   async function initPhotosphere() { | ||||||
|  |     let f: Feature<Point, PanoramaView> = await image.provider.getPanoramaInfo(image) | ||||||
|  | 
 | ||||||
|  |     const viewer = new PhotoSphereViewerWrapper(viewerEl, f) | ||||||
|  |     if (Array.isArray(nearbyFeatures)) { | ||||||
|  |       viewer.setNearbyFeatures(nearbyFeatures) | ||||||
|  |     } else { | ||||||
|  |       nearbyFeatures.addCallbackAndRunD(feats => { | ||||||
|  |         viewer.setNearbyFeatures(feats) | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |     isLoaded.set(true) | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   $: { |   $: { | ||||||
|     if (panzoomEl) { |     if (image.isSpherical) { | ||||||
|  | 
 | ||||||
|  |       initPhotosphere() | ||||||
|  |     } else if (panzoomEl) { | ||||||
|       panzoomInstance = panzoom(panzoomEl, { |       panzoomInstance = panzoom(panzoomEl, { | ||||||
|         bounds: true, |         bounds: true, | ||||||
|         boundsPadding: 0.49, |         boundsPadding: 0.49, | ||||||
|         minZoom: 0.1, |         minZoom: 0.1, | ||||||
|         maxZoom: 25, |         maxZoom: 25, | ||||||
|         initialZoom: 1.0, |         initialZoom: 1.0 | ||||||
|       }) |       }) | ||||||
|     } else { |     } else { | ||||||
|       panzoomInstance?.dispose() |       panzoomInstance?.dispose() | ||||||
|  | @ -30,11 +57,18 @@ | ||||||
|   } |   } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <img | <head> | ||||||
|   bind:this={panzoomEl} |   <link rel="stylesheet" href="./node_modules/pannellum/build/pannellum.css"> | ||||||
|   class="panzoom-image h-fit max-w-fit" | </head> | ||||||
|   on:load={() => { | {#if image.isSpherical} | ||||||
|  |   <div bind:this={viewerEl} class="w-full h-full" /> | ||||||
|  | {:else} | ||||||
|  |   <img | ||||||
|  |     bind:this={panzoomEl} | ||||||
|  |     class="panzoom-image h-fit max-w-fit" | ||||||
|  |     on:load={() => { | ||||||
|     isLoaded?.setData(true) |     isLoaded?.setData(true) | ||||||
|   }} |   }} | ||||||
|   src={image.url_hd ?? image.url} |     src={image.url_hd ?? image.url} | ||||||
| /> |   /> | ||||||
|  | {/if} | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import { UIEventSource } from "../../Logic/UIEventSource" |   import { UIEventSource } from "../../Logic/UIEventSource" | ||||||
|  |   import type { Store } from "../../Logic/UIEventSource" | ||||||
|   import type { OsmTags } from "../../Models/OsmFeature" |   import type { OsmTags } from "../../Models/OsmFeature" | ||||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization" |   import type { SpecialVisualizationState } from "../SpecialVisualization" | ||||||
|   import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch" |   import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch" | ||||||
|  | @ -16,7 +17,6 @@ | ||||||
|   import LoginToggle from "../Base/LoginToggle.svelte" |   import LoginToggle from "../Base/LoginToggle.svelte" | ||||||
|   import { onDestroy } from "svelte" |   import { onDestroy } from "svelte" | ||||||
|   import { Utils } from "../../Utils" |   import { Utils } from "../../Utils" | ||||||
| 
 |  | ||||||
|   export let tags: UIEventSource<OsmTags> |   export let tags: UIEventSource<OsmTags> | ||||||
|   export let state: SpecialVisualizationState |   export let state: SpecialVisualizationState | ||||||
|   export let image: P4CPicture |   export let image: P4CPicture | ||||||
|  | @ -24,7 +24,7 @@ | ||||||
|   export let layer: LayerConfig |   export let layer: LayerConfig | ||||||
| 
 | 
 | ||||||
|   export let highlighted: UIEventSource<string> = undefined |   export let highlighted: UIEventSource<string> = undefined | ||||||
| 
 |   export let nearbyFeatures: Feature[] | Store<Feature[]> = [] | ||||||
|   export let linkable = true |   export let linkable = true | ||||||
|   let targetValue = Object.values(image.osmTags)[0] |   let targetValue = Object.values(image.osmTags)[0] | ||||||
|   let isLinked = new UIEventSource(Object.values(tags.data).some((v) => targetValue === v)) |   let isLinked = new UIEventSource(Object.values(tags.data).some((v) => targetValue === v)) | ||||||
|  | @ -36,8 +36,8 @@ | ||||||
|     provider: AllImageProviders.byName(image.provider), |     provider: AllImageProviders.byName(image.provider), | ||||||
|     date: new Date(image.date), |     date: new Date(image.date), | ||||||
|     id: Object.values(image.osmTags)[0], |     id: Object.values(image.osmTags)[0], | ||||||
|  |     isSpherical: image.details.isSpherical | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   async function applyLink(isLinked: boolean) { |   async function applyLink(isLinked: boolean) { | ||||||
|     console.log("Applying linked image", isLinked, targetValue) |     console.log("Applying linked image", isLinked, targetValue) | ||||||
|     const currentTags = tags.data |     const currentTags = tags.data | ||||||
|  | @ -86,6 +86,7 @@ | ||||||
|   <AttributedImage |   <AttributedImage | ||||||
|     {state} |     {state} | ||||||
|     image={providedImage} |     image={providedImage} | ||||||
|  |     {nearbyFeatures} | ||||||
|     imgClass="max-h-64 w-auto sm:h-32 md:h-64" |     imgClass="max-h-64 w-auto sm:h-32 md:h-64" | ||||||
|     attributionFormat="minimal" |     attributionFormat="minimal" | ||||||
|   /> |   /> | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ | ||||||
|   import { onDestroy } from "svelte" |   import { onDestroy } from "svelte" | ||||||
|   import { BBox } from "../../Logic/BBox" |   import { BBox } from "../../Logic/BBox" | ||||||
|   import PanoramaxLink from "../BigComponents/PanoramaxLink.svelte" |   import PanoramaxLink from "../BigComponents/PanoramaxLink.svelte" | ||||||
|  |   import { GeoOperations } from "../../Logic/GeoOperations" | ||||||
| 
 | 
 | ||||||
|   export let tags: UIEventSource<OsmTags> |   export let tags: UIEventSource<OsmTags> | ||||||
|   export let state: SpecialVisualizationState |   export let state: SpecialVisualizationState | ||||||
|  | @ -45,8 +46,7 @@ | ||||||
|       pics |       pics | ||||||
|         .filter( |         .filter( | ||||||
|           (p: P4CPicture) => |           (p: P4CPicture) => | ||||||
|             !loadedImages.data.has(p.pictureUrl) && // We don't show any image which is already linked |             !loadedImages.data.has(p.pictureUrl) // We don't show any image which is already linked | ||||||
|             !p.details.isSpherical |  | ||||||
|         ) |         ) | ||||||
|         .slice(0, 25), |         .slice(0, 25), | ||||||
|     [loadedImages] |     [loadedImages] | ||||||
|  | @ -59,12 +59,13 @@ | ||||||
|           type: "Feature", |           type: "Feature", | ||||||
|           geometry: { |           geometry: { | ||||||
|             type: "Point", |             type: "Point", | ||||||
|             coordinates: [p4c.coordinates.lng, p4c.coordinates.lat], |             coordinates: [p4c.coordinates.lng, p4c.coordinates.lat] | ||||||
|           }, |           }, | ||||||
|           properties: { |           properties: { | ||||||
|             id: p4c.pictureUrl, |             id: p4c.pictureUrl, | ||||||
|             rotation: p4c.direction, |             rotation: p4c.direction, | ||||||
|           }, |             spherical: p4c.details.isSpherical ? "yes" : "no" | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|     ) |     ) | ||||||
|   ) |   ) | ||||||
|  | @ -76,14 +77,14 @@ | ||||||
|         type: "Feature", |         type: "Feature", | ||||||
|         geometry: { |         geometry: { | ||||||
|           type: "Point", |           type: "Point", | ||||||
|           coordinates: [s.coordinates.lng, s.coordinates.lat], |           coordinates: [s.coordinates.lng, s.coordinates.lat] | ||||||
|         }, |         }, | ||||||
|         properties: { |         properties: { | ||||||
|           id: s.pictureUrl, |           id: s.pictureUrl, | ||||||
|           selected: "yes", |           selected: "yes", | ||||||
|           rotation: s.direction, |           rotation: s.direction | ||||||
|         }, |         } | ||||||
|       }, |       } | ||||||
|     ] |     ] | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
|  | @ -108,7 +109,7 @@ | ||||||
|     rotation: state.mapProperties.rotation, |     rotation: state.mapProperties.rotation, | ||||||
|     pitch: state.mapProperties.pitch, |     pitch: state.mapProperties.pitch, | ||||||
|     zoom: new UIEventSource<number>(16), |     zoom: new UIEventSource<number>(16), | ||||||
|     location: new UIEventSource({ lon, lat }), |     location: new UIEventSource({ lon, lat }) | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
|   const geocodedImageLayer = new LayerConfig(<LayerConfigJson>geocoded_image) |   const geocodedImageLayer = new LayerConfig(<LayerConfigJson>geocoded_image) | ||||||
|  | @ -117,8 +118,9 @@ | ||||||
|     layer: geocodedImageLayer, |     layer: geocodedImageLayer, | ||||||
|     zoomToFeatures: true, |     zoomToFeatures: true, | ||||||
|     onClick: (feature) => { |     onClick: (feature) => { | ||||||
|  |       console.log("CLicked:", feature.properties) | ||||||
|       highlighted.set(feature.properties.id) |       highlighted.set(feature.properties.id) | ||||||
|     }, |     } | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
|   ShowDataLayer.showMultipleLayers(map, new StaticFeatureSource([feature]), state.theme.layers) |   ShowDataLayer.showMultipleLayers(map, new StaticFeatureSource([feature]), state.theme.layers) | ||||||
|  | @ -141,8 +143,17 @@ | ||||||
|     layer: geocodedImageLayer, |     layer: geocodedImageLayer, | ||||||
|     onClick: (feature) => { |     onClick: (feature) => { | ||||||
|       highlighted.set(feature.properties.id) |       highlighted.set(feature.properties.id) | ||||||
|     }, |     } | ||||||
|   }) |   }) | ||||||
|  |   let nearbyFeatures: Feature[] = [{ | ||||||
|  |     type: "Feature", | ||||||
|  |     geometry: { type: "Point", coordinates: GeoOperations.centerpointCoordinates(feature) }, | ||||||
|  |     properties: { | ||||||
|  |       name: layer.title?.GetRenderValue(feature.properties).Subs(feature.properties).txt | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   ] | ||||||
|  | 
 | ||||||
|   onDestroy( |   onDestroy( | ||||||
|     tags.addCallbackAndRunD((tags) => { |     tags.addCallbackAndRunD((tags) => { | ||||||
|       if ( |       if ( | ||||||
|  | @ -180,7 +191,7 @@ | ||||||
|             selected.set(undefined) |             selected.set(undefined) | ||||||
|           }} |           }} | ||||||
|         > |         > | ||||||
|           <LinkableImage {tags} {image} {state} {feature} {layer} {linkable} {highlighted} /> |           <LinkableImage {tags} {image} {state} {feature} {layer} {linkable} {highlighted} {nearbyFeatures} /> | ||||||
|         </span> |         </span> | ||||||
|       {/each} |       {/each} | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  | @ -1,54 +0,0 @@ | ||||||
| import { |  | ||||||
|     EquirectangularTilesAdapter, |  | ||||||
|     EquirectangularTilesAdapterConfig |  | ||||||
| } from "@photo-sphere-viewer/equirectangular-tiles-adapter" |  | ||||||
| 
 |  | ||||||
| export class PhotoAdapter extends EquirectangularTilesAdapter { |  | ||||||
|     // This code was shamelessly stolen from https://gitlab.com/panoramax/clients/web-viewer/-/blob/develop/src/utils/PhotoAdapter.js
 |  | ||||||
|     // MIT-license; thank you adrien!
 |  | ||||||
| 
 |  | ||||||
|     private readonly _shouldGoFast: () => boolean |  | ||||||
| 
 |  | ||||||
|     constructor(viewer, config?: EquirectangularTilesAdapterConfig & { shouldGoFast?: () => boolean }) { |  | ||||||
|         super(viewer, config) |  | ||||||
|         this._shouldGoFast = config?.shouldGoFast ?? (() => true) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Override to skip loading SD images according to shouldGoFast option. |  | ||||||
|      */ |  | ||||||
|     public async loadTexture(panorama, loader) { |  | ||||||
|         if (!panorama.origBaseUrl) { |  | ||||||
|             panorama.origBaseUrl = panorama.baseUrl |  | ||||||
|         } else { |  | ||||||
|             panorama.baseUrl = panorama.origBaseUrl |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Fast mode + thumbnail available + no HD image loaded yet + flat picture
 |  | ||||||
|         if ( |  | ||||||
|             this._shouldGoFast() |  | ||||||
|             && panorama.thumbUrl |  | ||||||
|             && !panorama.hdLoaded |  | ||||||
|             && panorama.rows == 1 |  | ||||||
|         ) { |  | ||||||
|             panorama.baseUrl = panorama.thumbUrl |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         let data = await super.loadTexture(panorama, loader) |  | ||||||
|         if (panorama.baseUrl === panorama.origBaseUrl) { |  | ||||||
|             panorama.hdLoaded = true |  | ||||||
|         } |  | ||||||
|         return data |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Override to skip loading tiles according to shouldGoFast option. |  | ||||||
|      * @private |  | ||||||
|      */ |  | ||||||
|     /* |  | ||||||
|    private __loadTiles(tiles) { |  | ||||||
|         if (!this._shouldGoFast()) { |  | ||||||
|             super.__loadTiles(tiles) |  | ||||||
|         } |  | ||||||
|     }*/ |  | ||||||
| } |  | ||||||
							
								
								
									
										67
									
								
								src/UI/Image/photoSphereViewerWrapper.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/UI/Image/photoSphereViewerWrapper.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | ||||||
|  | import "pannellum" | ||||||
|  | 
 | ||||||
|  | import { Feature, Point } from "geojson" | ||||||
|  | import { GeoOperations } from "../../Logic/GeoOperations" | ||||||
|  | import { PanoramaView } from "../../Logic/ImageProviders/ImageProvider" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export class PhotoSphereViewerWrapper { | ||||||
|  | 
 | ||||||
|  |     private readonly imageInfo: Feature<Point, PanoramaView> | ||||||
|  |     private readonly viewer: Pannellum.Viewer | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     constructor(container: HTMLElement, imageInfo: Feature<Point, PanoramaView>, nearbyFeatures?: Feature[]) { | ||||||
|  |         this.imageInfo = imageInfo | ||||||
|  |         this.viewer = pannellum.viewer(container, { | ||||||
|  |             type: "equirectangular", | ||||||
|  |             hfov: 110, | ||||||
|  |             panorama: imageInfo.properties.url, | ||||||
|  |             autoLoad: true, | ||||||
|  |             hotSpots: [], | ||||||
|  |             compass: true, | ||||||
|  |             showControls: false, | ||||||
|  |             northOffset: imageInfo.properties.northOffset, | ||||||
|  |             horizonPitch: imageInfo.properties.pitchOffset | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         /* for (let i = 0; i < 360; i += 45) { | ||||||
|  | 
 | ||||||
|  |              viewer.addHotSpot({ | ||||||
|  |                  type: "info", | ||||||
|  |                  yaw: i, | ||||||
|  |                  text: "YAW " + i | ||||||
|  |              }) | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          console.log("North offset:", imageInfo.properties.northOffset) | ||||||
|  |          viewer.addHotSpot({ | ||||||
|  |              type: "info", | ||||||
|  |              yaw: -northOffs, | ||||||
|  |              text: "Supposedely north " | ||||||
|  |          })*/ | ||||||
|  | 
 | ||||||
|  |         this.setNearbyFeatures(nearbyFeatures) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public setNearbyFeatures(nearbyFeatures: Feature[]) { | ||||||
|  |         const imageInfo = this.imageInfo | ||||||
|  |         const northOffs = imageInfo.properties.northOffset | ||||||
|  | 
 | ||||||
|  |         const hotspots = this.viewer.getConfig().hotSpots ?? [] | ||||||
|  |         for (const hotspot of hotspots) { | ||||||
|  |             this.viewer.removeHotSpot(hotspot.id) | ||||||
|  |         } | ||||||
|  |         // this.viewer.removeHotSpot()
 | ||||||
|  |         for (const f of nearbyFeatures ?? []) { | ||||||
|  |             const yaw = GeoOperations.bearing(imageInfo, GeoOperations.centerpoint(f)) | ||||||
|  |             this.viewer.addHotSpot({ | ||||||
|  |                 type: "info", | ||||||
|  |                 yaw: (yaw - northOffs) % 360, | ||||||
|  |                 text: f.properties.name | ||||||
|  |             }) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -3,111 +3,37 @@ | ||||||
|   import { onMount } from "svelte" |   import { onMount } from "svelte" | ||||||
| 
 | 
 | ||||||
|   export let imageInfo |   export let imageInfo | ||||||
|   // Tiles of the panorama, not geotiles |  | ||||||
|   let tilemeta = imageInfo?.asset_templates?.tiles_webp || imageInfo?.asset_templates?.tiles |  | ||||||
| 
 |  | ||||||
|   import "@photo-sphere-viewer/core/index.css" |  | ||||||
|   // import "@photo-sphere-viewer/virtual-tour-plugin/index.css" |  | ||||||
|   // import "@photo-sphere-viewertileUrl/gallery-plugin/index.css" |  | ||||||
|   // import "@photo-sphere-viewer/markers-plugin/index.css" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|   import { AbstractAdapter, Viewer as PSViewer } from "photo-sphere-viewer" |  | ||||||
|   import { PhotoAdapter } from "./Image/photoAdapter" |  | ||||||
| 
 |  | ||||||
|   export const PSV_DEFAULT_ZOOM = 30 |  | ||||||
|   export const PSV_ANIM_DURATION = 250 |  | ||||||
|   export const PIC_MAX_STAY_DURATION = 3000 |  | ||||||
| 
 |  | ||||||
|   const BASE_PANORAMA = { |  | ||||||
|     baseUrl: "./assets/loader_base.jpg", |  | ||||||
|     width: 1280, |  | ||||||
|     cols: 2, |  | ||||||
|     rows: 1, |  | ||||||
|     tileUrl: () => null |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|  |   import { PhotoSphereViewerWrapper } from "./Image/photoSphereViewerWrapper" | ||||||
| 
 | 
 | ||||||
|   let container: HTMLElement |   let container: HTMLElement | ||||||
| 
 | 
 | ||||||
|   let isSmall = () => container?.offsetWidth < 576 |  | ||||||
|   let shouldGoFast = () => true |  | ||||||
| 
 |  | ||||||
|   function constructPanoramaInfo() { |  | ||||||
|     const f = imageInfo |  | ||||||
| 
 |  | ||||||
|     const hdUrl = (Object.values(f.assets).find(a => a?.roles?.includes("data")) || {}).href |  | ||||||
|     const matrix = f?.properties?.["tiles:tile_matrix_sets"]?.geovisio |  | ||||||
|     const baseUrlWebp = Object.values(f.assets).find(a => a.roles?.includes("visual") && a.type === "image/webp") |  | ||||||
|     const baseUrlJpeg = Object.values(f.assets).find(a => a.roles?.includes("visual") && a.type === "image/jpeg") |  | ||||||
|     const baseUrl = (baseUrlWebp || baseUrlJpeg).href |  | ||||||
|     const thumbUrl = (Object.values(f.assets).find(a => a.roles?.includes("thumbnail") && a.type === "image/jpeg"))?.href |  | ||||||
| 
 |  | ||||||
|     let panorama = { |  | ||||||
|       baseUrl, |  | ||||||
|       origBaseUrl: baseUrl, |  | ||||||
|       basePanoData: (img) => ({ |  | ||||||
|         fullWidth: img.width, |  | ||||||
|         fullHeight: img.height |  | ||||||
|       }), |  | ||||||
|       hdUrl, |  | ||||||
|       thumbUrl, |  | ||||||
|       cols: matrix && matrix.tileMatrix[0].matrixWidth, |  | ||||||
|       rows: matrix && matrix.tileMatrix[0].matrixHeight, |  | ||||||
|       width: matrix && (matrix.tileMatrix[0].matrixWidth * matrix.tileMatrix[0].tileWidth), |  | ||||||
|       tileUrl: matrix && ((col, row) => tilemeta.href.replace(/\{TileCol}/g, col).replace(/\{TileRow}/g, row)) |  | ||||||
|     } |  | ||||||
|     return panorama |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|   PSViewer["useNewAnglesOrder"] = true |  | ||||||
| 
 | 
 | ||||||
|   onMount(() => { |   onMount(() => { | ||||||
|     const viewer = new PSViewer({ |     console.log("Creating viewer...") | ||||||
|       container, |     const features = [ | ||||||
| 
 |       { | ||||||
|       panorama: BASE_PANORAMA.baseUrl, |         "type": "Feature", | ||||||
| 
 |         "properties": { "name": "trap" }, | ||||||
|       adapter: [{ PhotoAdapter, prototype: AbstractAdapter } |         "geometry": { | ||||||
|         , { |           "coordinates": [ | ||||||
|           showErrorTile: false, |             3.742395038713312, | ||||||
|           baseBlur: false, |             51.05237592785801 | ||||||
|           resolution: isSmall() ? 32 : 64 |           ], | ||||||
|           // shouldGoFast |           "type": "Point" | ||||||
|         }], |         } | ||||||
| 
 |       } | ||||||
|       //withCredentials: parent._options?.fetchOptions?.credentials == "include", |     ] | ||||||
|       //requestHeaders: parent._options?.fetchOptions?.headers, |     const viewer = new PhotoSphereViewerWrapper(container, imageInfo, features) | ||||||
|       //lang: parent._t.psv, |  | ||||||
|       minFov: 5, |  | ||||||
|       loadingTxt: " ", |  | ||||||
|       navbar: null |  | ||||||
|     }) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     const panorama = constructPanoramaInfo() |     // console.log(panorama, container) | ||||||
|     console.log(panorama, container) |  | ||||||
| 
 |  | ||||||
|     viewer.setOptions({ |  | ||||||
|       adapter: [PhotoAdapter, { |  | ||||||
|         showErrorTile: false, |  | ||||||
|         baseBlur: false, |  | ||||||
|         resolution: isSmall() ? 32 : 64 |  | ||||||
|       }] |  | ||||||
|     }) |  | ||||||
|     viewer.setPanorama(panorama.hdUrl, { |  | ||||||
|       zoom: 0, longitude: 45, latitude: -45 |  | ||||||
|     }) |  | ||||||
| 
 |  | ||||||
|     window.setTimeout(() => { |  | ||||||
|       console.log(viewer.getPosition()) |  | ||||||
|     }, 2000) |  | ||||||
| 
 |  | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| 
 | <head> | ||||||
| <div bind:this={container} class="h-screen w-screen"></div> |   <link rel="stylesheet" href="./node_modules/pannellum/build/pannellum.css"> | ||||||
|  | </head> | ||||||
|  | <div bind:this={container} class="h-screen w-screen border" style="height: 500px"></div> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ | ||||||
|   import LevelSelector from "./BigComponents/LevelSelector.svelte" |   import LevelSelector from "./BigComponents/LevelSelector.svelte" | ||||||
|   import type { RasterLayerPolygon } from "../Models/RasterLayers" |   import type { RasterLayerPolygon } from "../Models/RasterLayers" | ||||||
|   import { AvailableRasterLayers } from "../Models/RasterLayers" |   import { AvailableRasterLayers } from "../Models/RasterLayers" | ||||||
|   import { onDestroy } from "svelte" |   import { onDestroy, setContext } from "svelte" | ||||||
|   import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte" |   import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte" | ||||||
|   import StateIndicator from "./BigComponents/StateIndicator.svelte" |   import StateIndicator from "./BigComponents/StateIndicator.svelte" | ||||||
|   import UploadingImageCounter from "./Image/UploadingImageCounter.svelte" |   import UploadingImageCounter from "./Image/UploadingImageCounter.svelte" | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								src/test.ts
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								src/test.ts
									
										
									
									
									
								
							|  | @ -1,13 +1,17 @@ | ||||||
|  | import { Mapillary } from "./Logic/ImageProviders/Mapillary" | ||||||
| import Test from "./UI/Test.svelte" | import Test from "./UI/Test.svelte" | ||||||
| import { ImageData, PanoramaxXYZ } from "panoramax-js" |  | ||||||
| 
 | 
 | ||||||
| const target = document.getElementById("maindiv") | const target = document.getElementById("maindiv") | ||||||
| target.innerHTML = "" | target.innerHTML = "" | ||||||
| let img = "https://panoramax-storage-public-fast.s3.gra.perf.cloud.ovh.net/main-pictures/d2/8c/ba/cf/c807-4dbf-b8c8-b1c3aa89182d.jpg" | /* | ||||||
| let imgId = "d28cbacf-c807-4dbf-b8c8-b1c3aa89182d" | let imgId = "8af265ba-3521-4c46-b2a9-c072215c1de3" | ||||||
| let panoramax = new PanoramaxXYZ() | let panoramax = new PanoramaxXYZ() | ||||||
| panoramax.imageInfo(imgId).then((imageInfo: ImageData) => { | panoramax.imageInfo(imgId).then((imageInfo: ImageData) => { | ||||||
|     console.log("IMG INFO: ", imageInfo) |     console.log("IMG INFO: ", imageInfo) | ||||||
|     new Test({ target, props: { imageInfo } }) |     new Test({ target, props: { imageInfo } }) | ||||||
| }) | })*/ | ||||||
| 
 | 
 | ||||||
|  | let pkey = 1199645818028177 | ||||||
|  | new Mapillary().DownloadImageInfo(pkey).then(imageInfo => { | ||||||
|  |     new Test({ target, props: { imageInfo } }) | ||||||
|  | }) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue