forked from MapComplete/MapComplete
		
	LayerServer: first version which can use a local MVT-server
This commit is contained in:
		
							parent
							
								
									35228daa8f
								
							
						
					
					
						commit
						ef2f1487c6
					
				
					 17 changed files with 1009 additions and 82 deletions
				
			
		|  | @ -25,13 +25,13 @@ Use scripts/osm2pgsl | |||
| To seed the database: | ||||
| 
 | ||||
| ```` | ||||
| osm2pgsql -O flex -S drinking_water.lua -s  --flat-nodes=import-help-file -d postgresql://user:none@localhost:5444/osm-poi andorra-latest.osm.pbf  | ||||
| osm2pgsql -O flex -E 4326 -S build_db.lua -s  --flat-nodes=import-help-file -d postgresql://user:password@localhost:5444/osm-poi andorra-latest.osm.pbf  | ||||
| ```` | ||||
| 
 | ||||
| ## Deploying a tile server | ||||
| 
 | ||||
| ```` | ||||
| export DATABASE_URL=postgresql://user:none@localhost:5444/osm-poi | ||||
| export DATABASE_URL=postgresql://user:password@localhost:5444/osm-poi | ||||
| ./pg_tileserv | ||||
| ```` | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ | |||
|   "source": { | ||||
|     "osmTags": "amenity=shower" | ||||
|   }, | ||||
|   "minzoom": 12, | ||||
|   "minzoom": 8, | ||||
|   "title": { | ||||
|     "render": { | ||||
|       "en": "Shower", | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ | |||
|   "source": { | ||||
|     "osmTags": "amenity=toilets" | ||||
|   }, | ||||
|   "minzoom": 12, | ||||
|   "minzoom": 9, | ||||
|   "title": { | ||||
|     "render": { | ||||
|       "en": "Toilet", | ||||
|  |  | |||
|  | @ -44,4 +44,4 @@ | |||
|     "shower" | ||||
|   ], | ||||
|   "widenFactor": 3 | ||||
| } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										386
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										386
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -20,6 +20,7 @@ | |||
|         "@turf/length": "^6.5.0", | ||||
|         "@turf/turf": "^6.5.0", | ||||
|         "@types/dompurify": "^3.0.2", | ||||
|         "@types/pg": "^8.10.9", | ||||
|         "@types/qrcode-generator": "^1.0.6", | ||||
|         "@types/showdown": "^2.0.0", | ||||
|         "chart.js": "^3.8.0", | ||||
|  | @ -50,6 +51,8 @@ | |||
|         "osmtogeojson": "^3.0.0-beta.5", | ||||
|         "panzoom": "^9.4.3", | ||||
|         "papaparse": "^5.3.1", | ||||
|         "pbf": "^3.2.1", | ||||
|         "pg": "^8.11.3", | ||||
|         "pic4carto": "^2.1.15", | ||||
|         "prompt-sync": "^4.2.0", | ||||
|         "qrcode-generator": "^1.4.4", | ||||
|  | @ -4223,6 +4226,68 @@ | |||
|       "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.3.tgz", | ||||
|       "integrity": "sha512-hw6bDMjvm+QTvEC+pRLpnTknQXoPu8Fnf+A+zX9HB7j/7RfYajFSbdukabo3adPwvvEHhIMafQl0R0Tpej7clQ==" | ||||
|     }, | ||||
|     "node_modules/@types/pg": { | ||||
|       "version": "8.10.9", | ||||
|       "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz", | ||||
|       "integrity": "sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==", | ||||
|       "dependencies": { | ||||
|         "@types/node": "*", | ||||
|         "pg-protocol": "*", | ||||
|         "pg-types": "^4.0.1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/pg/node_modules/pg-types": { | ||||
|       "version": "4.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz", | ||||
|       "integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==", | ||||
|       "dependencies": { | ||||
|         "pg-int8": "1.0.1", | ||||
|         "pg-numeric": "1.0.2", | ||||
|         "postgres-array": "~3.0.1", | ||||
|         "postgres-bytea": "~3.0.0", | ||||
|         "postgres-date": "~2.0.1", | ||||
|         "postgres-interval": "^3.0.0", | ||||
|         "postgres-range": "^1.1.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/pg/node_modules/postgres-array": { | ||||
|       "version": "3.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", | ||||
|       "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/pg/node_modules/postgres-bytea": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", | ||||
|       "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", | ||||
|       "dependencies": { | ||||
|         "obuf": "~1.1.2" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/pg/node_modules/postgres-date": { | ||||
|       "version": "2.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz", | ||||
|       "integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==", | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/pg/node_modules/postgres-interval": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", | ||||
|       "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/prompt-sync": { | ||||
|       "version": "4.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.2.0.tgz", | ||||
|  | @ -5277,6 +5342,14 @@ | |||
|       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", | ||||
|       "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" | ||||
|     }, | ||||
|     "node_modules/buffer-writer": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", | ||||
|       "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/bytewise": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", | ||||
|  | @ -9606,6 +9679,11 @@ | |||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/obuf": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", | ||||
|       "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" | ||||
|     }, | ||||
|     "node_modules/once": { | ||||
|       "version": "1.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", | ||||
|  | @ -9845,6 +9923,11 @@ | |||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/packet-reader": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", | ||||
|       "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" | ||||
|     }, | ||||
|     "node_modules/panzoom": { | ||||
|       "version": "9.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/panzoom/-/panzoom-9.4.3.tgz", | ||||
|  | @ -9954,6 +10037,97 @@ | |||
|       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", | ||||
|       "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" | ||||
|     }, | ||||
|     "node_modules/pg": { | ||||
|       "version": "8.11.3", | ||||
|       "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", | ||||
|       "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", | ||||
|       "dependencies": { | ||||
|         "buffer-writer": "2.0.0", | ||||
|         "packet-reader": "1.0.0", | ||||
|         "pg-connection-string": "^2.6.2", | ||||
|         "pg-pool": "^3.6.1", | ||||
|         "pg-protocol": "^1.6.0", | ||||
|         "pg-types": "^2.1.0", | ||||
|         "pgpass": "1.x" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 8.0.0" | ||||
|       }, | ||||
|       "optionalDependencies": { | ||||
|         "pg-cloudflare": "^1.1.1" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "pg-native": ">=3.0.1" | ||||
|       }, | ||||
|       "peerDependenciesMeta": { | ||||
|         "pg-native": { | ||||
|           "optional": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pg-cloudflare": { | ||||
|       "version": "1.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", | ||||
|       "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", | ||||
|       "optional": true | ||||
|     }, | ||||
|     "node_modules/pg-connection-string": { | ||||
|       "version": "2.6.2", | ||||
|       "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", | ||||
|       "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" | ||||
|     }, | ||||
|     "node_modules/pg-int8": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", | ||||
|       "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", | ||||
|       "engines": { | ||||
|         "node": ">=4.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pg-numeric": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", | ||||
|       "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pg-pool": { | ||||
|       "version": "3.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", | ||||
|       "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", | ||||
|       "peerDependencies": { | ||||
|         "pg": ">=8.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pg-protocol": { | ||||
|       "version": "1.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", | ||||
|       "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" | ||||
|     }, | ||||
|     "node_modules/pg-types": { | ||||
|       "version": "2.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", | ||||
|       "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", | ||||
|       "dependencies": { | ||||
|         "pg-int8": "1.0.1", | ||||
|         "postgres-array": "~2.0.0", | ||||
|         "postgres-bytea": "~1.0.0", | ||||
|         "postgres-date": "~1.0.4", | ||||
|         "postgres-interval": "^1.1.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pgpass": { | ||||
|       "version": "1.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", | ||||
|       "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", | ||||
|       "dependencies": { | ||||
|         "split2": "^4.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pic4carto": { | ||||
|       "version": "2.1.15", | ||||
|       "resolved": "https://registry.npmjs.org/pic4carto/-/pic4carto-2.1.15.tgz", | ||||
|  | @ -10138,6 +10312,46 @@ | |||
|         "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postgres-array": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", | ||||
|       "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postgres-bytea": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", | ||||
|       "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postgres-date": { | ||||
|       "version": "1.0.7", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", | ||||
|       "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postgres-interval": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", | ||||
|       "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", | ||||
|       "dependencies": { | ||||
|         "xtend": "^4.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postgres-range": { | ||||
|       "version": "1.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz", | ||||
|       "integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==" | ||||
|     }, | ||||
|     "node_modules/potpack": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", | ||||
|  | @ -11461,6 +11675,14 @@ | |||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/split2": { | ||||
|       "version": "4.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", | ||||
|       "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", | ||||
|       "engines": { | ||||
|         "node": ">= 10.x" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/sshpk": { | ||||
|       "version": "1.17.0", | ||||
|       "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", | ||||
|  | @ -16835,6 +17057,55 @@ | |||
|       "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.3.tgz", | ||||
|       "integrity": "sha512-hw6bDMjvm+QTvEC+pRLpnTknQXoPu8Fnf+A+zX9HB7j/7RfYajFSbdukabo3adPwvvEHhIMafQl0R0Tpej7clQ==" | ||||
|     }, | ||||
|     "@types/pg": { | ||||
|       "version": "8.10.9", | ||||
|       "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz", | ||||
|       "integrity": "sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==", | ||||
|       "requires": { | ||||
|         "@types/node": "*", | ||||
|         "pg-protocol": "*", | ||||
|         "pg-types": "^4.0.1" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "pg-types": { | ||||
|           "version": "4.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz", | ||||
|           "integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==", | ||||
|           "requires": { | ||||
|             "pg-int8": "1.0.1", | ||||
|             "pg-numeric": "1.0.2", | ||||
|             "postgres-array": "~3.0.1", | ||||
|             "postgres-bytea": "~3.0.0", | ||||
|             "postgres-date": "~2.0.1", | ||||
|             "postgres-interval": "^3.0.0", | ||||
|             "postgres-range": "^1.1.1" | ||||
|           } | ||||
|         }, | ||||
|         "postgres-array": { | ||||
|           "version": "3.0.2", | ||||
|           "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", | ||||
|           "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==" | ||||
|         }, | ||||
|         "postgres-bytea": { | ||||
|           "version": "3.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", | ||||
|           "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", | ||||
|           "requires": { | ||||
|             "obuf": "~1.1.2" | ||||
|           } | ||||
|         }, | ||||
|         "postgres-date": { | ||||
|           "version": "2.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz", | ||||
|           "integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==" | ||||
|         }, | ||||
|         "postgres-interval": { | ||||
|           "version": "3.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", | ||||
|           "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "@types/prompt-sync": { | ||||
|       "version": "4.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.2.0.tgz", | ||||
|  | @ -17608,6 +17879,11 @@ | |||
|       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", | ||||
|       "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" | ||||
|     }, | ||||
|     "buffer-writer": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", | ||||
|       "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" | ||||
|     }, | ||||
|     "bytewise": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", | ||||
|  | @ -20874,6 +21150,11 @@ | |||
|       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", | ||||
|       "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" | ||||
|     }, | ||||
|     "obuf": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", | ||||
|       "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" | ||||
|     }, | ||||
|     "once": { | ||||
|       "version": "1.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", | ||||
|  | @ -21049,6 +21330,11 @@ | |||
|         "p-limit": "^3.0.2" | ||||
|       } | ||||
|     }, | ||||
|     "packet-reader": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", | ||||
|       "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" | ||||
|     }, | ||||
|     "panzoom": { | ||||
|       "version": "9.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/panzoom/-/panzoom-9.4.3.tgz", | ||||
|  | @ -21134,6 +21420,73 @@ | |||
|       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", | ||||
|       "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" | ||||
|     }, | ||||
|     "pg": { | ||||
|       "version": "8.11.3", | ||||
|       "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", | ||||
|       "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", | ||||
|       "requires": { | ||||
|         "buffer-writer": "2.0.0", | ||||
|         "packet-reader": "1.0.0", | ||||
|         "pg-cloudflare": "^1.1.1", | ||||
|         "pg-connection-string": "^2.6.2", | ||||
|         "pg-pool": "^3.6.1", | ||||
|         "pg-protocol": "^1.6.0", | ||||
|         "pg-types": "^2.1.0", | ||||
|         "pgpass": "1.x" | ||||
|       } | ||||
|     }, | ||||
|     "pg-cloudflare": { | ||||
|       "version": "1.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", | ||||
|       "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", | ||||
|       "optional": true | ||||
|     }, | ||||
|     "pg-connection-string": { | ||||
|       "version": "2.6.2", | ||||
|       "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", | ||||
|       "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" | ||||
|     }, | ||||
|     "pg-int8": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", | ||||
|       "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" | ||||
|     }, | ||||
|     "pg-numeric": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", | ||||
|       "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==" | ||||
|     }, | ||||
|     "pg-pool": { | ||||
|       "version": "3.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", | ||||
|       "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", | ||||
|       "requires": {} | ||||
|     }, | ||||
|     "pg-protocol": { | ||||
|       "version": "1.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", | ||||
|       "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" | ||||
|     }, | ||||
|     "pg-types": { | ||||
|       "version": "2.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", | ||||
|       "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", | ||||
|       "requires": { | ||||
|         "pg-int8": "1.0.1", | ||||
|         "postgres-array": "~2.0.0", | ||||
|         "postgres-bytea": "~1.0.0", | ||||
|         "postgres-date": "~1.0.4", | ||||
|         "postgres-interval": "^1.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "pgpass": { | ||||
|       "version": "1.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", | ||||
|       "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", | ||||
|       "requires": { | ||||
|         "split2": "^4.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "pic4carto": { | ||||
|       "version": "2.1.15", | ||||
|       "resolved": "https://registry.npmjs.org/pic4carto/-/pic4carto-2.1.15.tgz", | ||||
|  | @ -21238,6 +21591,34 @@ | |||
|         "util-deprecate": "^1.0.2" | ||||
|       } | ||||
|     }, | ||||
|     "postgres-array": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", | ||||
|       "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" | ||||
|     }, | ||||
|     "postgres-bytea": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", | ||||
|       "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" | ||||
|     }, | ||||
|     "postgres-date": { | ||||
|       "version": "1.0.7", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", | ||||
|       "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" | ||||
|     }, | ||||
|     "postgres-interval": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", | ||||
|       "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", | ||||
|       "requires": { | ||||
|         "xtend": "^4.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "postgres-range": { | ||||
|       "version": "1.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz", | ||||
|       "integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==" | ||||
|     }, | ||||
|     "potpack": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", | ||||
|  | @ -22195,6 +22576,11 @@ | |||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "split2": { | ||||
|       "version": "4.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", | ||||
|       "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" | ||||
|     }, | ||||
|     "sshpk": { | ||||
|       "version": "1.17.0", | ||||
|       "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
|       "oauth_secret": "NBWGhWDrD3QDB35xtVuxv4aExnmIt4FA_WgeLtwxasg", | ||||
|       "url": "https://www.openstreetmap.org" | ||||
|     }, | ||||
|     "mvt_layer_server": "http://127.0.0.1:7800/public.{layer}/{z}/{x}/{y}.pbf", | ||||
|     "disabled:oauth_credentials": { | ||||
|       "##": "DEV", | ||||
|       "#": "This client-id is registered by 'MapComplete' on https://master.apis.dev.openstreetmap.org/", | ||||
|  | @ -118,6 +119,7 @@ | |||
|     "@turf/length": "^6.5.0", | ||||
|     "@turf/turf": "^6.5.0", | ||||
|     "@types/dompurify": "^3.0.2", | ||||
|     "@types/pg": "^8.10.9", | ||||
|     "@types/qrcode-generator": "^1.0.6", | ||||
|     "@types/showdown": "^2.0.0", | ||||
|     "chart.js": "^3.8.0", | ||||
|  | @ -148,6 +150,8 @@ | |||
|     "osmtogeojson": "^3.0.0-beta.5", | ||||
|     "panzoom": "^9.4.3", | ||||
|     "papaparse": "^5.3.1", | ||||
|     "pbf": "^3.2.1", | ||||
|     "pg": "^8.11.3", | ||||
|     "pic4carto": "^2.1.15", | ||||
|     "prompt-sync": "^4.2.0", | ||||
|     "qrcode-generator": "^1.4.4", | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import { AllSharedLayers } from "../../src/Customizations/AllSharedLayers" | |||
| import fs from "fs" | ||||
| import { Or } from "../../src/Logic/Tags/Or" | ||||
| import { RegexTag } from "../../src/Logic/Tags/RegexTag" | ||||
| import { Utils } from "../../src/Utils" | ||||
| 
 | ||||
| class LuaSnippets{ | ||||
|     /** | ||||
|  | @ -40,18 +41,24 @@ class GenerateLayerLua { | |||
|     } | ||||
|     public functionName(){ | ||||
|         const l = this._layer | ||||
|         if(!l.source?.osmTags){ | ||||
|             return undefined | ||||
|         } | ||||
|         return `process_poi_${l.id}` | ||||
|     } | ||||
| 
 | ||||
|     public generateFunction(): string { | ||||
|         const l = this._layer | ||||
|         if(!l.source?.osmTags){ | ||||
|             return undefined | ||||
|         } | ||||
|         return [ | ||||
|             `local pois_${l.id} = osm2pgsql.define_table({`, | ||||
|             `  name = '${l.id}',`, | ||||
|             "  ids = { type = 'any', type_column = 'osm_type', id_column = 'osm_id' },", | ||||
|             "  columns = {", | ||||
|             "    { column = 'tags', type = 'jsonb' },", | ||||
|             "    { column = 'geom', type = 'point', not_null = true },", | ||||
|             "    { column = 'geom', type = 'point', projection = 4326, not_null = true },", | ||||
|             "  }" + | ||||
|             "})", | ||||
|             "", | ||||
|  | @ -117,14 +124,13 @@ class GenerateLayerFile extends Script { | |||
|     } | ||||
| 
 | ||||
|     async main(args: string[]) { | ||||
|         let dw = AllSharedLayers.sharedLayers.get("drinking_water") | ||||
|         let t = AllSharedLayers.sharedLayers.get("toilet") | ||||
|         const layerNames = Array.from(AllSharedLayers.sharedLayers.values()) | ||||
| 
 | ||||
|         const generators = [dw, t].map(l => new GenerateLayerLua(l)) | ||||
|         const generators = layerNames.map(l => new GenerateLayerLua(l)) | ||||
| 
 | ||||
|         const script = [ | ||||
|             ...generators.map(g => g.generateFunction()), | ||||
|             LuaSnippets.combine(generators.map(g => g.functionName())), | ||||
|             LuaSnippets.combine(Utils.NoNull(generators.map(g => g.functionName()))), | ||||
|             LuaSnippets.tail | ||||
|         ].join("\n\n\n") | ||||
|         const path = "build_db.lua" | ||||
|  |  | |||
							
								
								
									
										44
									
								
								scripts/osm2pgsql/tilecountServer.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								scripts/osm2pgsql/tilecountServer.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| import { BBox } from "../../src/Logic/BBox" | ||||
| import { Client } from "pg" | ||||
| 
 | ||||
| /** | ||||
|  * Connects with a Postgis database, gives back how much items there are within the given BBOX | ||||
|  */ | ||||
| export default class TilecountServer { | ||||
|     private readonly _client: Client | ||||
|     private isConnected = false | ||||
| 
 | ||||
|     constructor(connectionString: string) { | ||||
|         this._client = new Client(connectionString) | ||||
|     } | ||||
| 
 | ||||
|     async getCount(layer: string, bbox: BBox = undefined): Promise<number> { | ||||
|         if (!this.isConnected) { | ||||
|             await this._client.connect() | ||||
|             this.isConnected = true | ||||
|         } | ||||
| 
 | ||||
|         let query = "SELECT COUNT(*) FROM " + layer | ||||
| 
 | ||||
|         if(bbox){ | ||||
|             query += ` WHERE ST_MakeEnvelope (${bbox.minLon}, ${bbox.minLat}, ${bbox.maxLon}, ${bbox.maxLat}, 4326) ~ geom` | ||||
|         } | ||||
| console.log(query) | ||||
|         const result = await this._client.query(query) | ||||
|         return result.rows[0].count | ||||
|     } | ||||
| 
 | ||||
|     disconnect() { | ||||
|         this._client.end() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const tcs = new TilecountServer("postgresql://user:none@localhost:5444/osm-poi") | ||||
| console.log(">>>", await tcs.getCount("drinking_water", new BBox([ | ||||
|     [1.5052013991654007, | ||||
|         42.57480750272123, | ||||
|     ], [ | ||||
|         1.6663677350703097, | ||||
|         42.499856652770745, | ||||
|     ]]))) | ||||
| tcs.disconnect() | ||||
|  | @ -11,6 +11,9 @@ import DynamicGeoJsonTileSource from "../TiledFeatureSource/DynamicGeoJsonTileSo | |||
| import { BBox } from "../../BBox" | ||||
| import LocalStorageFeatureSource from "../TiledFeatureSource/LocalStorageFeatureSource" | ||||
| import FullNodeDatabaseSource from "../TiledFeatureSource/FullNodeDatabaseSource" | ||||
| import { Features } from "@rgossiaux/svelte-headlessui/types" | ||||
| import DynamicMvtileSource from "../TiledFeatureSource/DynamicMvtTileSource" | ||||
| import { layouts } from "chart.js" | ||||
| 
 | ||||
| /** | ||||
|  * This source will fetch the needed data from various sources for the given layout. | ||||
|  | @ -44,14 +47,18 @@ export default class LayoutSource extends FeatureSourceMerger { | |||
|                     maxAge: l.maxAgeOfCache, | ||||
|                 }) | ||||
|         ) | ||||
|         console.log(mapProperties) | ||||
|         const mvtSources: FeatureSource[] = osmLayers.map(l => LayoutSource.setupMvtSource(l, mapProperties, isDisplayed(l.id))) | ||||
| 
 | ||||
| 
 | ||||
|         /* | ||||
|         const overpassSource = LayoutSource.setupOverpass( | ||||
|             backend, | ||||
|             osmLayers, | ||||
|             bounds, | ||||
|             zoom, | ||||
|             featureSwitches | ||||
|         ) | ||||
|         )//*/
 | ||||
| 
 | ||||
|         const osmApiSource = LayoutSource.setupOsmApiSource( | ||||
|             osmLayers, | ||||
|  | @ -61,22 +68,27 @@ export default class LayoutSource extends FeatureSourceMerger { | |||
|             featureSwitches, | ||||
|             fullNodeDatabaseSource | ||||
|         ) | ||||
| 
 | ||||
|         const geojsonSources: FeatureSource[] = geojsonlayers.map((l) => | ||||
|             LayoutSource.setupGeojsonSource(l, mapProperties, isDisplayed(l.id)) | ||||
|         ) | ||||
| 
 | ||||
|         super(overpassSource, osmApiSource, ...geojsonSources, ...fromCache) | ||||
| 
 | ||||
|         super(osmApiSource, ...geojsonSources, ...fromCache, ...mvtSources) | ||||
| 
 | ||||
|         const self = this | ||||
|         function setIsLoading() { | ||||
|             const loading = overpassSource?.runningQuery?.data || osmApiSource?.isRunning?.data | ||||
|             self._isLoading.setData(loading) | ||||
|            // const loading = overpassSource?.runningQuery?.data || osmApiSource?.isRunning?.data
 | ||||
|            // self._isLoading.setData(loading)
 | ||||
|         } | ||||
| 
 | ||||
|         overpassSource?.runningQuery?.addCallbackAndRun((_) => setIsLoading()) | ||||
|         // overpassSource?.runningQuery?.addCallbackAndRun((_) => setIsLoading())
 | ||||
|         osmApiSource?.isRunning?.addCallbackAndRun((_) => setIsLoading()) | ||||
|     } | ||||
| 
 | ||||
|     private static setupMvtSource(layer: LayerConfig, mapProperties:  { zoom: Store<number>; bounds: Store<BBox> },    isActive?: Store<boolean>): FeatureSource{ | ||||
|         return new DynamicMvtileSource(layer, mapProperties, { isActive }) | ||||
|     } | ||||
|     private static setupGeojsonSource( | ||||
|         layer: LayerConfig, | ||||
|         mapProperties: { zoom: Store<number>; bounds: Store<BBox> }, | ||||
|  |  | |||
							
								
								
									
										378
									
								
								src/Logic/FeatureSource/Sources/MvtSource.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								src/Logic/FeatureSource/Sources/MvtSource.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,378 @@ | |||
| import { Feature, Geometry } from "geojson" | ||||
| import { Store, UIEventSource } from "../../UIEventSource" | ||||
| import { FeatureSource } from "../FeatureSource" | ||||
| import Pbf from "pbf" | ||||
| import * as pbfCompile from "pbf/compile" | ||||
| import * as PbfSchema from "protocol-buffers-schema" | ||||
| 
 | ||||
| type Coords = [number, number][] | ||||
| 
 | ||||
| class MvtFeatureBuilder { | ||||
|     private static readonly geom_types = ["Unknown", "Point", "LineString", "Polygon"] as const | ||||
|     private readonly _size: number | ||||
|     private readonly _x0: number | ||||
|     private readonly _y0: number | ||||
| 
 | ||||
|     constructor(extent: number, x: number, y: number, z: number) { | ||||
|         this._size = extent * Math.pow(2, z) | ||||
|         this._x0 = extent * x | ||||
|         this._y0 = extent * y | ||||
|     } | ||||
| 
 | ||||
|     public toGeoJson(geometry, typeIndex, properties): Feature { | ||||
|         let coords: [number, number] | Coords | Coords[] = this.encodeGeometry(geometry) | ||||
|         switch (typeIndex) { | ||||
|             case 1: | ||||
|                 const points = [] | ||||
|                 for (let i = 0; i < coords.length; i++) { | ||||
|                     points[i] = coords[i][0] | ||||
|                 } | ||||
|                 coords = points | ||||
|                 this.project(<any>coords) | ||||
|                 break | ||||
| 
 | ||||
|             case 2: | ||||
|                 for (let i = 0; i < coords.length; i++) { | ||||
|                     this.project(coords[i]) | ||||
|                 } | ||||
|                 break | ||||
| 
 | ||||
|             case 3: | ||||
|                 let classified = this.classifyRings(coords) | ||||
|                 for (let i = 0; i < coords.length; i++) { | ||||
|                     for (let j = 0; j < coords[i].length; j++) { | ||||
|                         this.project(classified[i][j]) | ||||
|                     } | ||||
|                 } | ||||
|                 break | ||||
|         } | ||||
| 
 | ||||
|         let type: string = MvtFeatureBuilder.geom_types[typeIndex] | ||||
|         if (coords.length === 1) { | ||||
|             coords = coords[0] | ||||
|         } else { | ||||
|             type = "Multi" + type | ||||
|         } | ||||
| 
 | ||||
|         return { | ||||
|             type: "Feature", | ||||
|             geometry: { | ||||
|                 type: <any>type, | ||||
|                 coordinates: <any>coords, | ||||
|             }, | ||||
|             properties, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private encodeGeometry(geometry: number[]) { | ||||
|         let cX = 0 | ||||
|         let cY = 0 | ||||
|         let coordss: Coords[] = [] | ||||
|         let currentRing: Coords = [] | ||||
|         for (let i = 0; i < geometry.length; i++) { | ||||
|             let commandInteger = geometry[i] | ||||
|             let commandId = commandInteger & 0x7 | ||||
|             let commandCount = commandInteger >> 3 | ||||
|             /* | ||||
|             Command 	Id 	Parameters 	Parameter Count | ||||
|                         MoveTo 	1 	dX, dY 	2 | ||||
|                         LineTo 	2 	dX, dY 	2 | ||||
|                         ClosePath 	7 	No parameters 	0 | ||||
|             */ | ||||
|             if (commandId === 1) { | ||||
|                 // MoveTo means: we start a new ring
 | ||||
|                 if (currentRing.length !== 0) { | ||||
|                     coordss.push(currentRing) | ||||
|                     currentRing = [] | ||||
|                 } | ||||
|             } | ||||
|             if (commandId === 1 || commandId === 2){ | ||||
|                 for (let j = 0; j < commandCount; j++) { | ||||
|                     const dx = geometry[i + j * 2 + 1] | ||||
|                     cX += ((dx >> 1) ^ (-(dx & 1))) | ||||
|                     const dy = geometry[i + j * 2 + 2] | ||||
|                     cY += ((dy >> 1) ^ (-(dy & 1))) | ||||
|                     currentRing.push([cX, cY]) | ||||
|                 } | ||||
|                 i = commandCount * 2 | ||||
|             } | ||||
|             if(commandId === 7){ | ||||
|                 currentRing.push([...currentRing[0]]) | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         if (currentRing.length > 0) { | ||||
|             coordss.push(currentRing) | ||||
|         } | ||||
|         return coordss | ||||
|     } | ||||
| 
 | ||||
|     private signedArea(ring: Coords): number { | ||||
|         let sum = 0 | ||||
|         const len = ring.length | ||||
|         // J is basically (i - 1) % len
 | ||||
|         let j = len - 1 | ||||
|         let p1 | ||||
|         let p2 | ||||
|         for (let i = 0; i < len; i++) { | ||||
|             p1 = ring[i] | ||||
|             p2 = ring[j] | ||||
|             sum += (p2.x - p1.x) * (p1.y + p2.y) | ||||
|             j = i | ||||
|         } | ||||
|         return sum | ||||
|     } | ||||
| 
 | ||||
|     private classifyRings(rings: Coords[]): Coords[][] { | ||||
|         const len = rings.length | ||||
| 
 | ||||
|         if (len <= 1) return [rings] | ||||
| 
 | ||||
|         const polygons = [] | ||||
|         let polygon | ||||
|         // CounterClockWise
 | ||||
|         let ccw: boolean | ||||
| 
 | ||||
|         for (let i = 0; i < len; i++) { | ||||
|             const area = this.signedArea(rings[i]) | ||||
|             if (area === 0) continue | ||||
| 
 | ||||
|             if (ccw === undefined) { | ||||
|                 ccw = area < 0 | ||||
|             } | ||||
|             if (ccw === (area < 0)) { | ||||
|                 if (polygon) { | ||||
|                     polygons.push(polygon) | ||||
|                 } | ||||
|                 polygon = [rings[i]] | ||||
| 
 | ||||
|             } else { | ||||
|                 polygon.push(rings[i]) | ||||
|             } | ||||
|         } | ||||
|         if (polygon) { | ||||
|             polygons.push(polygon) | ||||
|         } | ||||
| 
 | ||||
|         return polygons | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Inline replacement of the location by projecting | ||||
|      * @param line | ||||
|      * @private | ||||
|      */ | ||||
|     private project(line: [number, number][]) { | ||||
|         const y0 = this._y0 | ||||
|         const x0 = this._x0 | ||||
|         const size = this._size | ||||
|         for (let i = 0; i < line.length; i++) { | ||||
|             let p = line[i] | ||||
|             let y2 = 180 - (p[1] + y0) * 360 / size | ||||
|             line[i] = [ | ||||
|                 (p[0] + x0) * 360 / size - 180, | ||||
|                 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90, | ||||
|             ] | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export default class MvtSource implements FeatureSource { | ||||
| 
 | ||||
|     private static readonly schemaSpec = ` | ||||
|     package vector_tile; | ||||
| 
 | ||||
| option optimize_for = LITE_RUNTIME; | ||||
| 
 | ||||
| message Tile { | ||||
| 
 | ||||
|         // GeomType is described in section 4.3.4 of the specification
 | ||||
|         enum GeomType { | ||||
|              UNKNOWN = 0; | ||||
|              POINT = 1; | ||||
|              LINESTRING = 2; | ||||
|              POLYGON = 3; | ||||
|         } | ||||
| 
 | ||||
|         // Variant type encoding
 | ||||
|         // The use of values is described in section 4.1 of the specification
 | ||||
|         message Value { | ||||
|                 // Exactly one of these values must be present in a valid message
 | ||||
|                 optional string string_value = 1; | ||||
|                 optional float float_value = 2; | ||||
|                 optional double double_value = 3; | ||||
|                 optional int64 int_value = 4; | ||||
|                 optional uint64 uint_value = 5; | ||||
|                 optional sint64 sint_value = 6; | ||||
|                 optional bool bool_value = 7; | ||||
| 
 | ||||
|                 extensions 8 to max; | ||||
|         } | ||||
| 
 | ||||
|         // Features are described in section 4.2 of the specification
 | ||||
|         message Feature { | ||||
|                 optional uint64 id = 1 [ default = 0 ]; | ||||
| 
 | ||||
|                 // Tags of this feature are encoded as repeated pairs of
 | ||||
|                 // integers.
 | ||||
|                 // A detailed description of tags is located in sections
 | ||||
|                 // 4.2 and 4.4 of the specification
 | ||||
|                 repeated uint32 tags = 2 [ packed = true ]; | ||||
| 
 | ||||
|                 // The type of geometry stored in this feature.
 | ||||
|                 optional GeomType type = 3 [ default = UNKNOWN ]; | ||||
| 
 | ||||
|                 // Contains a stream of commands and parameters (vertices).
 | ||||
|                 // A detailed description on geometry encoding is located in
 | ||||
|                 // section 4.3 of the specification.
 | ||||
|                 repeated uint32 geometry = 4 [ packed = true ]; | ||||
|         } | ||||
| 
 | ||||
|         // Layers are described in section 4.1 of the specification
 | ||||
|         message Layer { | ||||
|                 // Any compliant implementation must first read the version
 | ||||
|                 // number encoded in this message and choose the correct
 | ||||
|                 // implementation for this version number before proceeding to
 | ||||
|                 // decode other parts of this message.
 | ||||
|                 required uint32 version = 15 [ default = 1 ]; | ||||
| 
 | ||||
|                 required string name = 1; | ||||
| 
 | ||||
|                 // The actual features in this tile.
 | ||||
|                 repeated Feature features = 2; | ||||
| 
 | ||||
|                 // Dictionary encoding for keys
 | ||||
|                 repeated string keys = 3; | ||||
| 
 | ||||
|                 // Dictionary encoding for values
 | ||||
|                 repeated Value values = 4; | ||||
| 
 | ||||
|                 // Although this is an "optional" field it is required by the specification.
 | ||||
|                 // See https://github.com/mapbox/vector-tile-spec/issues/47
 | ||||
|                 optional uint32 extent = 5 [ default = 4096 ]; | ||||
| 
 | ||||
|                 extensions 16 to max; | ||||
|         } | ||||
| 
 | ||||
|         repeated Layer layers = 3; | ||||
| 
 | ||||
|         extensions 16 to 8191; | ||||
| } | ||||
| ` | ||||
|     private static readonly tile_schema = pbfCompile(PbfSchema.parse(MvtSource.schemaSpec)).Tile | ||||
| 
 | ||||
| 
 | ||||
|     private readonly _url: string | ||||
|     private readonly _layerName: string | ||||
|     private readonly _features: UIEventSource<Feature<Geometry, { | ||||
|         [name: string]: any | ||||
|     }>[]> = new UIEventSource<Feature<Geometry, { [p: string]: any }>[]>([]) | ||||
|     public readonly features: Store<Feature<Geometry, { [name: string]: any }>[]> = this._features | ||||
|     private readonly x: number | ||||
|     private readonly y: number | ||||
|     private readonly z: number | ||||
| 
 | ||||
|     constructor(url: string, x: number, y: number, z: number, layerName?: string) { | ||||
|         this._url = url | ||||
|         this._layerName = layerName | ||||
|         this.x = x | ||||
|         this.y = y | ||||
|         this.z = z | ||||
|         this.downloadSync() | ||||
|     } | ||||
| 
 | ||||
|     private getValue(v: { | ||||
|         // Exactly one of these values must be present in a valid message
 | ||||
|         string_value?: string, | ||||
|         float_value?: number, | ||||
|         double_value?: number, | ||||
|         int_value?: number, | ||||
|         uint_value?: number, | ||||
|         sint_value?: number, | ||||
|         bool_value?: boolean | ||||
|     }): string | number | undefined | boolean { | ||||
|         if (v.string_value !== "") { | ||||
|             return v.string_value | ||||
|         } | ||||
|         if (v.double_value !== 0) { | ||||
|             return v.double_value | ||||
|         } | ||||
|         if (v.float_value !== 0) { | ||||
|             return v.float_value | ||||
|         } | ||||
|         if (v.int_value !== 0) { | ||||
|             return v.int_value | ||||
|         } | ||||
|         if (v.uint_value !== 0) { | ||||
|             return v.uint_value | ||||
|         } | ||||
|         if (v.sint_value !== 0) { | ||||
|             return v.sint_value | ||||
|         } | ||||
|         if (v.bool_value !== false) { | ||||
|             return v.bool_value | ||||
|         } | ||||
|         return undefined | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private downloadSync(){ | ||||
|         this.download().then(d => { | ||||
|             if(d.length === 0){ | ||||
|                 return | ||||
|             } | ||||
|             return this._features.setData(d) | ||||
|         }).catch(e => {console.error(e)}) | ||||
|     } | ||||
|     private async download(): Promise<Feature[]> { | ||||
|         const result = await fetch(this._url) | ||||
|         const buffer = await result.arrayBuffer() | ||||
|         const data = MvtSource.tile_schema.read(new Pbf(buffer)) | ||||
|         const layers = data.layers | ||||
|         let layer = data.layers[0] | ||||
|         if (layers.length > 1) { | ||||
|             if (!this._layerName) { | ||||
|                 throw "Multiple layers in the downloaded tile, but no layername is given to choose from" | ||||
|             } | ||||
|             layer = layers.find(l => l.name === this._layerName) | ||||
|         } | ||||
|         if(!layer){ | ||||
|             return [] | ||||
|         } | ||||
|         const builder = new MvtFeatureBuilder(layer.extent, this.x, this.y, this.z) | ||||
|         const features: Feature[] = [] | ||||
| 
 | ||||
|         for (const feature of layer.features) { | ||||
|             const properties = this.inflateProperties(feature.tags, layer.keys, layer.values) | ||||
|             features.push(builder.toGeoJson(feature.geometry, feature.type, properties)) | ||||
|         } | ||||
| 
 | ||||
|         return features | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private inflateProperties(tags: number[], keys: string[], values: { string_value: string }[]) { | ||||
|         const properties = {} | ||||
|         for (let i = 0; i < tags.length; i += 2) { | ||||
|             properties[keys[tags[i]]] = this.getValue(values[tags[i + 1]]) | ||||
|         } | ||||
|         let type: string | ||||
|         switch (properties["osm_type"]) { | ||||
|             case "N": | ||||
|                 type = "node" | ||||
|                 break | ||||
|             case "W": | ||||
|                 type = "way" | ||||
|                 break | ||||
|             case "R": | ||||
|                 type = "relation" | ||||
|                 break | ||||
|         } | ||||
|         properties["id"] = type + "/" + properties["osm_id"] | ||||
|         delete properties["osm_id"] | ||||
|         delete properties["osm_type"] | ||||
| 
 | ||||
|         return properties | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,39 @@ | |||
| import { Store } from "../../UIEventSource" | ||||
| import DynamicTileSource from "./DynamicTileSource" | ||||
| import { Utils } from "../../../Utils" | ||||
| import { BBox } from "../../BBox" | ||||
| import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" | ||||
| import MvtSource from "../Sources/MvtSource" | ||||
| import { Tiles } from "../../../Models/TileRange" | ||||
| import Constants from "../../../Models/Constants" | ||||
| 
 | ||||
| export default class DynamicMvtileSource extends DynamicTileSource { | ||||
| 
 | ||||
|     constructor( | ||||
|         layer: LayerConfig, | ||||
|         mapProperties: { | ||||
|             zoom: Store<number> | ||||
|             bounds: Store<BBox> | ||||
|         }, | ||||
|         options?: { | ||||
|             isActive?: Store<boolean> | ||||
|         }, | ||||
|     ) { | ||||
|         super( | ||||
|             mapProperties.zoom, | ||||
|             layer.minzoom, | ||||
|             (zxy) => { | ||||
|                 const [z, x, y] = Tiles.tile_from_index(zxy) | ||||
|                 const url = Utils.SubstituteKeys(Constants.VectorTileServer, | ||||
|                     { | ||||
|                         z, x, y, layer: layer.id, | ||||
|                     }) | ||||
|                 return new MvtSource(url, x, y, z) | ||||
|             }, | ||||
|             mapProperties, | ||||
|             { | ||||
|                 isActive: options?.isActive, | ||||
|             }, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | @ -10,9 +10,9 @@ import FeatureSourceMerger from "../Sources/FeatureSourceMerger" | |||
|  */ | ||||
| export default class DynamicTileSource extends FeatureSourceMerger { | ||||
|     constructor( | ||||
|         zoomlevel: number, | ||||
|         zoomlevel: Store<number>, | ||||
|         minzoom: number, | ||||
|         constructSource: (tileIndex) => FeatureSource, | ||||
|         constructSource: (tileIndex: number) => FeatureSource, | ||||
|         mapProperties: { | ||||
|             bounds: Store<BBox> | ||||
|             zoom: Store<number> | ||||
|  | @ -34,8 +34,9 @@ export default class DynamicTileSource extends FeatureSourceMerger { | |||
|                         if (mapProperties.zoom.data < minzoom) { | ||||
|                             return undefined | ||||
|                         } | ||||
|                         const z = Math.round(zoomlevel.data) | ||||
|                         const tileRange = Tiles.TileRangeBetween( | ||||
|                             zoomlevel, | ||||
|                             z, | ||||
|                             bounds.getNorth(), | ||||
|                             bounds.getEast(), | ||||
|                             bounds.getSouth(), | ||||
|  | @ -49,7 +50,7 @@ export default class DynamicTileSource extends FeatureSourceMerger { | |||
|                         } | ||||
| 
 | ||||
|                         const needed = Tiles.MapRange(tileRange, (x, y) => | ||||
|                             Tiles.tile_index(zoomlevel, x, y) | ||||
|                             Tiles.tile_index(z, x, y) | ||||
|                         ).filter((i) => !loadedTiles.has(i)) | ||||
|                         if (needed.length === 0) { | ||||
|                             return undefined | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import DynamicTileSource from "./DynamicTileSource" | ||||
| import { Store } from "../../UIEventSource" | ||||
| import { ImmutableStore, Store } from "../../UIEventSource" | ||||
| import { BBox } from "../../BBox" | ||||
| import TileLocalStorage from "../Actors/TileLocalStorage" | ||||
| import { Feature } from "geojson" | ||||
|  | @ -27,7 +27,7 @@ export default class LocalStorageFeatureSource extends DynamicTileSource { | |||
|             options?.maxAge ?? 24 * 60 * 60 | ||||
|         ) | ||||
|         super( | ||||
|             zoomlevel, | ||||
|            new ImmutableStore(zoomlevel), | ||||
|             layer.minzoom, | ||||
|             (tileIndex) => | ||||
|                 new StaticFeatureSource( | ||||
|  |  | |||
|  | @ -154,6 +154,11 @@ export default class Constants { | |||
| 
 | ||||
|     ] as const | ||||
|     public static readonly defaultPinIcons: string[] = <any>Constants._defaultPinIcons | ||||
|     /** | ||||
|      * The location that the MVT-layer is hosted. | ||||
|      * This is a MapLibre/MapBox vector tile server which hosts vector tiles for every (official) layer | ||||
|      */ | ||||
|     public static VectorTileServer: string | undefined = Constants.config.mvt_layer_server | ||||
| 
 | ||||
|     private static isRetina(): boolean { | ||||
|         if (Utils.runningFromConsole) { | ||||
|  |  | |||
|  | @ -178,7 +178,6 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> { | |||
|             if (v === undefined) { | ||||
|                 const msg = `Default layer ${layerName} not found. ${state.sharedLayers.size} layers are available` | ||||
|                 if (layerName === "favourite") { | ||||
|                     context.warn(msg) | ||||
|                     continue | ||||
|                 } | ||||
|                 context.err(msg) | ||||
|  |  | |||
|  | @ -303,6 +303,7 @@ class LineRenderingLayer { | |||
|                         type: "FeatureCollection", | ||||
|                         features, | ||||
|                     }, | ||||
|                     cluster: true, | ||||
|                     promoteId: "id", | ||||
|                 }) | ||||
|                 const linelayer = this._layername + "_line" | ||||
|  |  | |||
|  | @ -1,70 +1,122 @@ | |||
| <script lang="ts"> | ||||
|   // Testing grounds | ||||
|   import { UIEventSource } from "../Logic/UIEventSource" | ||||
|   import MaplibreMap from "./Map/MaplibreMap.svelte" | ||||
|   import { Map as MlMap } from "maplibre-gl" | ||||
|   import { MapLibreAdaptor } from "./Map/MapLibreAdaptor" | ||||
|     // Testing grounds | ||||
|     import { UIEventSource } from "../Logic/UIEventSource" | ||||
|     import MaplibreMap from "./Map/MaplibreMap.svelte" | ||||
|     import { Map as MlMap } from "maplibre-gl" | ||||
|     import { MapLibreAdaptor } from "./Map/MapLibreAdaptor" | ||||
|     import Constants from "../Models/Constants" | ||||
|     import toilet from "../assets/generated/layers/toilet.json" | ||||
|     import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||
|     import DynamicMvtileSource from "../Logic/FeatureSource/TiledFeatureSource/DynamicMvtTileSource" | ||||
|     import ShowDataLayer from "./Map/ShowDataLayer" | ||||
| 
 | ||||
|   let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined) | ||||
|   let adaptor = new MapLibreAdaptor(map) | ||||
|   adaptor.location.setData({ | ||||
|     lat: 42.5404, | ||||
|     lon:1.4832 | ||||
|      | ||||
|   }) | ||||
|   adaptor.zoom.setData(10) | ||||
|   map.addCallbackAndRunD(map => { | ||||
|     map.on("load", () => { | ||||
|     const tl = new LayerConfig(<any>toilet) | ||||
| 
 | ||||
|       map.addSource("drinking_water", { | ||||
|         "type": "vector", | ||||
|         "tiles": ["http://127.0.0.2:7800/public.drinking_water/{z}/{x}/{y}.pbf"] // http://127.0.0.2:7800/public.drinking_water.json", | ||||
|       }) | ||||
| 
 | ||||
|       map.addLayer( | ||||
|         { | ||||
|           "id": "drinking_water_layer", | ||||
|           "type": "circle", | ||||
|           "source": "drinking_water", | ||||
|           "source-layer": "public.drinking_water", | ||||
|           "paint": { | ||||
|             "circle-radius": 5, | ||||
|             "circle-color": "#ff00ff", | ||||
|             "circle-stroke-width": 2, | ||||
|             "circle-stroke-color": "#000000", | ||||
|           }, | ||||
|         }, | ||||
|       ) | ||||
|     let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined) | ||||
|     let adaptor = new MapLibreAdaptor(map) | ||||
| 
 | ||||
|         map.addSource("toilet", { | ||||
|             "type": "vector", | ||||
|             "tiles": ["http://127.0.0.2:7800/public.toilet/{z}/{x}/{y}.pbf"] // http://127.0.0.2:7800/public.drinking_water.json", | ||||
|         }) | ||||
| 
 | ||||
|         map.addLayer( | ||||
|             { | ||||
|                 "id": "toilet_layer", | ||||
|                 "type": "circle", | ||||
|                 "source": "toilet", | ||||
|                 "source-layer": "public.toilet", | ||||
|                 "paint": { | ||||
|                     "circle-radius": 5, | ||||
|                     "circle-color": "#0000ff", | ||||
|                     "circle-stroke-width": 2, | ||||
|                     "circle-stroke-color": "#000000", | ||||
|                 }, | ||||
|             }, | ||||
|         ) | ||||
| 
 | ||||
|       map.on('click', 'drinking_water_layer', (e) => { | ||||
| // Copy coordinates array. | ||||
|         console.log(e) | ||||
| console.warn(">>>", e.features[0]) | ||||
|       }) | ||||
|     const src = new DynamicMvtileSource(tl, adaptor) | ||||
|     src.features.addCallbackAndRun(f => console.log(">>> Features are", f)) | ||||
|     new ShowDataLayer(map, { | ||||
|         layer: tl, | ||||
|         features: src | ||||
|     }) | ||||
| 
 | ||||
|     adaptor.location.setData({ | ||||
|         lat: 51.2095, lon: 3.2260, | ||||
|     }) | ||||
|     adaptor.zoom.setData(13) | ||||
|     const loadedIcons = new Set<string>() | ||||
| 
 | ||||
|     async function loadImage(map: MlMap, url: string, name: string): Promise<void> { | ||||
|         return new Promise((resolve, reject) => { | ||||
|             if (loadedIcons.has(name)) { | ||||
|                 return new Promise<void>((resolve, reject) => resolve()) | ||||
|             } | ||||
|             loadedIcons.add(name) | ||||
|             if (Constants.defaultPinIcons.indexOf(url) >= 0) { | ||||
|                 url = "./assets/svg/" + url + ".svg" | ||||
|             } | ||||
|             map.loadImage( | ||||
|                 url, | ||||
|                 (error, image) => { | ||||
|                     if (error) { | ||||
|                         reject(error) | ||||
|                     } | ||||
|                     map.addImage(name, image) | ||||
|                     resolve() | ||||
|                 }) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     map.addCallbackAndRunD(map => { | ||||
|         map.on("load", async () => { | ||||
|             console.log("Onload") | ||||
|             await loadImage(map, "https://upload.wikimedia.org/wikipedia/commons/7/7c/201408_cat.png", "cat") | ||||
| 
 | ||||
|             /* | ||||
|             map.addSource("drinking_water", { | ||||
|                 "type": "vector", | ||||
|                 "tiles": ["http://127.0.0.2:7800/public.drinking_water/{z}/{x}/{y}.pbf"], // http://127.0.0.2:7800/public.drinking_water.json", | ||||
|             }) | ||||
| 
 | ||||
|             map.addLayer( | ||||
|                 { | ||||
|                     "id": "drinking_water_layer", | ||||
|                     "type": "circle", | ||||
|                     "source": "drinking_water", | ||||
|                     "source-layer": "public.drinking_water", | ||||
|                     "paint": { | ||||
|                         "circle-radius": 5, | ||||
|                         "circle-color": "#ff00ff", | ||||
|                         "circle-stroke-width": 2, | ||||
|                         "circle-stroke-color": "#000000", | ||||
|                     }, | ||||
|                 }, | ||||
|             )*/ | ||||
|             /* | ||||
|                         map.addSource("toilet", { | ||||
|                             "type": "vector", | ||||
|                             "tiles": ["http://127.0.0.2:7800/public.toilet/{z}/{x}/{y}.pbf"], // http://127.0.0.2:7800/public.drinking_water.json", | ||||
|                         }) | ||||
|              | ||||
|                         map.addLayer( | ||||
|                             { | ||||
|                                 "id": "toilet_layer", | ||||
|                                 "type": "circle", | ||||
|                                 "source": "toilet", | ||||
|                                 "source-layer": "public.toilet", | ||||
|                                 "paint": { | ||||
|                                     "circle-radius": 5, | ||||
|                                     "circle-color": "#0000ff", | ||||
|                                     "circle-stroke-width": 2, | ||||
|                                     "circle-stroke-color": "#000000", | ||||
|                                 }, | ||||
|                             }, | ||||
|                         ) | ||||
|                         map.addLayer({ | ||||
|                             "id": "points", | ||||
|                             "type": "symbol", | ||||
|                             "source": "toilet", | ||||
|                             "source-layer": "public.toilet", | ||||
|                             "layout": { | ||||
|                                 "icon-overlap": "always", | ||||
|                                 "icon-image": "cat", | ||||
|                                 "icon-size": 0.05, | ||||
|                             }, | ||||
|                         })*/ | ||||
| 
 | ||||
| 
 | ||||
|             map.on("click", "drinking_water_layer", (e) => { | ||||
| // Copy coordinates array. | ||||
|                 console.log(e) | ||||
|                 console.warn(">>>", e.features[0]) | ||||
|             }) | ||||
|         }) | ||||
|     }) | ||||
|   }) | ||||
| </script> | ||||
| 
 | ||||
| <div class="h-screen w-screen"> | ||||
| <MaplibreMap {map} /> | ||||
|   <MaplibreMap {map} /> | ||||
| </div> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue