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",
|
||||
|
|
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"
|
||||
|
|
|
@ -4,21 +4,61 @@
|
|||
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"
|
||||
|
||||
const tl = new LayerConfig(<any>toilet)
|
||||
|
||||
|
||||
let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
|
||||
let adaptor = new MapLibreAdaptor(map)
|
||||
adaptor.location.setData({
|
||||
lat: 42.5404,
|
||||
lon:1.4832
|
||||
|
||||
const src = new DynamicMvtileSource(tl, adaptor)
|
||||
src.features.addCallbackAndRun(f => console.log(">>> Features are", f))
|
||||
new ShowDataLayer(map, {
|
||||
layer: tl,
|
||||
features: src
|
||||
})
|
||||
adaptor.zoom.setData(10)
|
||||
map.addCallbackAndRunD(map => {
|
||||
map.on("load", () => {
|
||||
|
||||
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",
|
||||
"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(
|
||||
|
@ -34,11 +74,11 @@
|
|||
"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",
|
||||
"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(
|
||||
|
@ -55,16 +95,28 @@
|
|||
},
|
||||
},
|
||||
)
|
||||
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) => {
|
||||
|
||||
map.on("click", "drinking_water_layer", (e) => {
|
||||
// Copy coordinates array.
|
||||
console.log(e)
|
||||
console.warn(">>>", e.features[0])
|
||||
console.warn(">>>", e.features[0])
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="h-screen w-screen">
|
||||
<MaplibreMap {map} />
|
||||
<MaplibreMap {map} />
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue