diff --git a/package-lock.json b/package-lock.json index 20ed4d5d7..950d3fec5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,7 +65,7 @@ "opening_hours": "^3.6.0", "osm-auth": "^2.5.0", "osmtogeojson": "^3.0.0-beta.5", - "panoramax-js": "^0.3.9", + "panoramax-js": "^0.3.10", "panzoom": "^9.4.3", "papaparse": "^5.3.1", "pg": "^8.11.3", @@ -4727,6 +4727,44 @@ "node-fetch": "^2.6.12" } }, + "node_modules/@jeswr/prefixcc/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@jeswr/prefixcc/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@jeswr/prefixcc/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@jeswr/prefixcc/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "dev": true, @@ -8707,6 +8745,44 @@ "node-fetch": "^2.6.12" } }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/cross-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/cross-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/cross-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, @@ -10335,6 +10411,44 @@ "node-fetch": "^2.6.12" } }, + "node_modules/fetch-sparql-endpoint/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/fetch-sparql-endpoint/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/fetch-sparql-endpoint/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/fetch-sparql-endpoint/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/fflate": { "version": "0.4.8", "license": "MIT" @@ -12030,6 +12144,44 @@ "node-fetch": "^2.6.12" } }, + "node_modules/jsonld-context-parser/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/jsonld-context-parser/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/jsonld-context-parser/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/jsonld-context-parser/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/jsonld-request": { "version": "2.0.1", "license": "BSD-3-Clause", @@ -12256,22 +12408,6 @@ } } }, - "node_modules/ky-universal/node_modules/node-fetch": { - "version": "3.3.2", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, "node_modules/latlon2country": { "version": "1.2.6", "license": "GPL-3.0-or-later", @@ -13300,37 +13436,20 @@ } }, "node_modules/node-fetch": { - "version": "2.7.0", - "license": "MIT", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dependencies": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": "4.x || >=6.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/node-html-parser": { @@ -16009,9 +16128,9 @@ "license": "MIT" }, "node_modules/panoramax-js": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.9.tgz", - "integrity": "sha512-CCXSmqwJxZYTxkru1LBAm2O+O/nnm3lwPX/8kLxYCIAZKQSFJsl1DoTkDqm/MLoo/dJOp3LmIVuK7nTM2NRgwA==", + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.10.tgz", + "integrity": "sha512-ZI9gH98FB3RFWYy69Evsv6vWA+crwhlsdiY8KiZgXAdVYnW7C1YzuQg/Mls546ZHh8/WHj1GMwfe8w5UU6OcFg==", "dependencies": { "@ogcapi-js/features": "^1.1.1", "@ogcapi-js/shared": "^1.1.1", @@ -24630,6 +24749,33 @@ "requires": { "node-fetch": "^2.6.12" } + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } } }, @@ -27338,6 +27484,35 @@ "version": "4.0.0", "requires": { "node-fetch": "^2.6.12" + }, + "dependencies": { + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } } }, "cross-spawn": { @@ -28401,6 +28576,33 @@ "requires": { "node-fetch": "^2.6.12" } + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } } }, @@ -29513,6 +29715,33 @@ "requires": { "node-fetch": "^2.6.12" } + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } } }, @@ -29659,16 +29888,6 @@ "requires": { "abort-controller": "^3.0.0", "node-fetch": "^3.2.10" - }, - "dependencies": { - "node-fetch": { - "version": "3.3.2", - "requires": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - } - } } }, "latlon2country": { @@ -30370,24 +30589,13 @@ "version": "1.0.0" }, "node-fetch": { - "version": "2.7.0", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "requires": { - "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3" - }, - "webidl-conversions": { - "version": "3.0.1" - }, - "whatwg-url": { - "version": "5.0.0", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" } }, "node-html-parser": { @@ -32104,9 +32312,9 @@ "version": "1.0.0" }, "panoramax-js": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.9.tgz", - "integrity": "sha512-CCXSmqwJxZYTxkru1LBAm2O+O/nnm3lwPX/8kLxYCIAZKQSFJsl1DoTkDqm/MLoo/dJOp3LmIVuK7nTM2NRgwA==", + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.10.tgz", + "integrity": "sha512-ZI9gH98FB3RFWYy69Evsv6vWA+crwhlsdiY8KiZgXAdVYnW7C1YzuQg/Mls546ZHh8/WHj1GMwfe8w5UU6OcFg==", "requires": { "@ogcapi-js/features": "^1.1.1", "@ogcapi-js/shared": "^1.1.1", diff --git a/package.json b/package.json index b5e0fc883..ed7f7fe84 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,8 @@ "generate:buildDbScript": "vite-node scripts/osm2pgsql/generateBuildDbScript.ts", "generate:summaryCache": "vite-node scripts/generateSummaryTileCache.ts", "create:database": "vite-node scripts/osm2pgsql/createNewDatabase.ts", - "delete:database:old": "vite-node scripts/osm2pgsql/deleteOldDbs.ts" + "delete:database:old": "vite-node scripts/osm2pgsql/deleteOldDbs.ts", + "upload:panoramax": "vite-node scripts/ImgurToPanoramax.ts" }, "keywords": [ "OpenStreetMap", @@ -209,7 +210,7 @@ "opening_hours": "^3.6.0", "osm-auth": "^2.5.0", "osmtogeojson": "^3.0.0-beta.5", - "panoramax-js": "^0.3.9", + "panoramax-js": "^0.3.10", "panzoom": "^9.4.3", "papaparse": "^5.3.1", "pg": "^8.11.3", diff --git a/scripts/ImgurToPanoramax.ts b/scripts/ImgurToPanoramax.ts new file mode 100644 index 000000000..019fc7931 --- /dev/null +++ b/scripts/ImgurToPanoramax.ts @@ -0,0 +1,156 @@ +import Script from "./Script" +import { Overpass } from "../src/Logic/Osm/Overpass" +import { RegexTag } from "../src/Logic/Tags/RegexTag" +import Constants from "../src/Models/Constants" +import { BBox } from "../src/Logic/BBox" +import { existsSync, readFileSync, writeFileSync } from "fs" +import { PanoramaxUploader } from "../src/Logic/ImageProviders/Panoramax" +import { Feature } from "geojson" +import { LicenseInfo } from "../src/Logic/ImageProviders/LicenseInfo" +import { GeoOperations } from "../src/Logic/GeoOperations" +import { Tag } from "../src/Logic/Tags/Tag" +import { Utils } from "../src/Utils" +import ChangeTagAction from "../src/Logic/Osm/Actions/ChangeTagAction" +import { And } from "../src/Logic/Tags/And" +import { Changes } from "../src/Logic/Osm/Changes" +import { ChangeDescription } from "../src/Logic/Osm/Actions/ChangeDescription" +import OsmObjectDownloader from "../src/Logic/Osm/OsmObjectDownloader" +import { OsmObject } from "../src/Logic/Osm/OsmObject" +import { createReadStream } from "node:fs" +import { File } from 'buffer'; +import { open } from 'node:fs/promises'; +import { UploadableTag } from "../src/Logic/Tags/TagTypes" + + +export class ImgurToPanoramax extends Script { + + private readonly panoramax = new PanoramaxUploader(Constants.panoramax.url, Constants.panoramax.token) + + private _imageDirectory: string + private _licenseDirectory: string + + private readonly sequenceIds = { + test: "7f34cf53-27ff-46c9-ac22-78511fa8457a", + cc0: "f0d6f78a-ff95-4db1-8494-6eb44a17bb37", + ccby: "288a8052-b475-422c-811a-4f6f1a00015e", + ccbysa: "f3d02893-b4c1-4cd6-8b27-e27ab57eb59a", + } as const + + + constructor() { + super( + "Queries OSM for 'imgur'-images, uploads them to Panoramax and creates a changeset to update OSM", + ) + } + + async uploadImage(key: string, feat: Feature, sequences: ({ + id: string; + "stats:items": { count: number } + })[]): Promise { + const v = feat.properties[key] + if (!v) { + return undefined + } + const imageHash = v.split("/").at(-1).split(".").at(0) + let path: string = undefined + if (existsSync(this._imageDirectory + "/" + imageHash + ".jpg")) { + path = this._imageDirectory + "/" + imageHash + ".jpg" + } else if (existsSync(this._imageDirectory + "/" + imageHash + ".jpeg")) { + path = this._imageDirectory + "/" + imageHash + ".jpeg" + } + if (!path) { + return undefined + } + const licensePath = this._licenseDirectory + "/" + v.replaceAll(/[^a-zA-Z0-9]/g, "_") + ".json" + if (!existsSync(licensePath)) { + return undefined + } + const licenseText: LicenseInfo = JSON.parse(readFileSync(licensePath, "utf8")) + if (!licenseText.licenseShortName) { + console.log("No license found for", path, licenseText) + return undefined + } + const license = licenseText.licenseShortName.toLowerCase().split(" ")[0].replace(/-/g, "") + const sequence = this.sequenceIds[license] + const author = licenseText.artist + + + const handle = await open(path); + + const stat = await handle.stat(); + + class MyFile extends File { + // we should set correct size + // otherwise we will encounter UND_ERR_REQ_CONTENT_LENGTH_MISMATCH + size = stat.size; + stream = undefined + } + + const file = new MyFile([], path) + + file.stream = function() { + return handle.readableWebStream(); + }; + + console.log("Uploading", imageHash, sequence) + const result = await this.panoramax.uploadImage( file, GeoOperations.centerpointCoordinates(feat), author, true, sequence) + await handle.close() + return new And([new Tag(key.replace("image", result.key), result.value), + new Tag(key,"")]) + } + + async main(args: string[]): Promise { + this._imageDirectory = args[0] ?? "/home/pietervdvn/data/imgur-image-backup" + this._licenseDirectory = args[1] ?? "/home/pietervdvn/git/MapComplete-data/ImageLicenseInfo" + + const bounds = new BBox([[3.6984301050112833, 51.06715570450848], [3.7434328399847914, 51.039379568816145]]) + const maxcount = 100 + const filter = new RegexTag("image", /^https:\/\/i.imgur.com\/.*/) + const overpass = new Overpass(filter, [], Constants.defaultOverpassUrls[0]) + const features = (await overpass.queryGeoJson(bounds))[0].features + + let converted = 0 + + const pano = this.panoramax.panoramax + const sequences = await pano.mySequences() + const changes: ChangeDescription[] = [] + do { + const f = features.shift() + if (!f) { + break + } + + const changedTags: (UploadableTag | undefined)[] = [] + for (const k of ["image", "image:menu", "image:streetsign"]) { + changedTags.push(await this.uploadImage(k, f, sequences)) + for (let i = 0; i < 20; i++) { + changedTags.push( + await this.uploadImage(k + ":" + i, f, sequences), + ) + } + } + const action = new ChangeTagAction(f.properties.id, new And(Utils.NoNull(changedTags)), + f.properties, { + theme: "image-mover", + changeType: "link-image", + }, + ) + changes.push(...await action.CreateChangeDescriptions()) + converted++ + } while (converted < maxcount) + + const modif: string[] = Utils.Dedup(changes.map(ch => ch.type + "/" + ch.id)) + const modifiedObjectsFresh = + (await Promise.all(modif.map(id => new OsmObjectDownloader().DownloadObjectAsync(id)))) + .filter(m => m !== "deleted") + const modifiedObjects = Changes.createChangesetObjectsStatic( + changes, + modifiedObjectsFresh,false, []) + const cs = Changes.buildChangesetXML("0", modifiedObjects) + writeFileSync("imgur_to_panoramax.osc", cs, "utf8") + + } + +} + +new ImgurToPanoramax().run() diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index 6cd676096..f5acdbc99 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -45,9 +45,8 @@ export default class PanoramaxImageProvider extends ImageProvider { * @private */ private async getInfoFromMapComplete(id: string): Promise<{ data: ImageData, url: string }> { - const sequence = "6e702976-580b-419c-8fb3-cf7bd364e6f8" // We always reuse this sequence const url = `https://panoramax.mapcomplete.org/` - const data = await PanoramaxImageProvider.defaultPanoramax.imageInfo(id, sequence) + const data = await PanoramaxImageProvider.defaultPanoramax.imageInfo(id) return { url, data } } @@ -163,27 +162,24 @@ export default class PanoramaxImageProvider extends ImageProvider { } export class PanoramaxUploader implements ImageUploader { - private readonly _panoramax: AuthorizedPanoramax + public readonly panoramax: AuthorizedPanoramax maxFileSizeInMegabytes = 100 * 1000 * 1000 // 100MB constructor(url: string, token: string) { - this._panoramax = new AuthorizedPanoramax(url, token) + this.panoramax = new AuthorizedPanoramax(url, token) } - async uploadImage(blob: File, currentGps: [number, number], author: string, noblur: boolean = false): Promise<{ + async uploadImage(blob: File, currentGps: [number, number], author: string, noblur: boolean = false, sequenceId?: string ): Promise<{ key: string; value: string; absoluteUrl: string }> { // https://panoramax.openstreetmap.fr/api/docs/swagger#/ - let hasDate = false - let hasGPS = false let [lon, lat] = currentGps let datetime = new Date().toISOString() try { const tags = await ExifReader.load(blob) - hasDate = tags?.DateTime !== undefined const [[latD], [latM], [latS, latSDenom]] =<[[number,number],[number,number],[number,number]]> tags?.GPSLatitude.value const [[lonD], [lonM], [lonS, lonSDenom]] =<[[number,number],[number,number],[number,number]]> tags?.GPSLongitude.value lat = latD + latM / 60 + latS / (3600 * latSDenom) @@ -198,12 +194,12 @@ export class PanoramaxUploader implements ImageUploader { } - const p = this._panoramax - const defaultSequence = (await p.mySequences())[0] - console.log("Upload options are", lon, lat, datetime) + const p = this.panoramax + const defaultSequence: {id: string, "stats:items":{count:number}} = (await p.mySequences()).find(s => s.id === (sequenceId ?? "6e702976-580b-419c-8fb3-cf7bd364e6f8")) + console.log("Upload options are", lon, lat, datetime, blob) const img = await p.addImage(blob, defaultSequence, { - lon: Utils.Round7(lon), - lat: Utils.Round7(lat), + lon, + lat, datetime, isBlurred: noblur, exifOverride: { diff --git a/src/Logic/Osm/Changes.ts b/src/Logic/Osm/Changes.ts index 113fc27a4..82e80c3ae 100644 --- a/src/Logic/Osm/Changes.ts +++ b/src/Logic/Osm/Changes.ts @@ -297,6 +297,18 @@ export class Changes { newObjects: OsmObject[] modifiedObjects: OsmObject[] deletedObjects: OsmObject[] + }{ + return Changes.createChangesetObjectsStatic(changes, downloadedOsmObjects, ignoreNoCreate, this.previouslyCreated) + } + public static createChangesetObjectsStatic( + changes: ChangeDescription[], + downloadedOsmObjects: OsmObject[], + ignoreNoCreate: boolean = false, + previouslyCreated : OsmObject[] + ): { + newObjects: OsmObject[] + modifiedObjects: OsmObject[] + deletedObjects: OsmObject[] } { /** * This is a rather complicated method which does a lot of stuff. @@ -322,7 +334,7 @@ export class Changes { states.set(o.type + "/" + o.id, "unchanged") } - for (const o of this.previouslyCreated) { + for (const o of previouslyCreated) { objects.set(o.type + "/" + o.id, o) states.set(o.type + "/" + o.id, "unchanged") } @@ -372,7 +384,7 @@ export class Changes { throw "Hmm? This is a bug" } objects.set(id, osmObj) - this.previouslyCreated.push(osmObj) + previouslyCreated.push(osmObj) } const state = states.get(id)