From ff4ce9050c98d8f77a8582fd248e7e34c588c0bd Mon Sep 17 00:00:00 2001 From: Alec Zoeller Date: Wed, 5 Nov 2025 13:10:54 +1300 Subject: [PATCH 1/4] Added examples/java-spark-sql. Java implementation of Sedona and GeoParquet (with test class and JUnit tests) provided a running localhost Spark cluster. --- docs-overrides/package-lock.json | 91 +++-------- examples/README.md | 3 + examples/java-spark-sql/.gitignore | 20 +++ examples/java-spark-sql/pom.xml | 149 ++++++++++++++++++ .../main/java/spark/GeoParquetAccessor.java | 90 +++++++++++ .../main/java/spark/SedonaGeoParquetMain.java | 61 +++++++ .../main/java/spark/SedonaSparkSession.java | 56 +++++++ .../src/main/resources/application.properties | 18 +++ .../test/java/spark/SedonaParquetTest.java | 105 ++++++++++++ .../src/test/resources/TestPoints.parquet | Bin 0 -> 79834 bytes .../src/test/resources/application.properties | 18 +++ 11 files changed, 540 insertions(+), 71 deletions(-) create mode 100644 examples/java-spark-sql/.gitignore create mode 100644 examples/java-spark-sql/pom.xml create mode 100644 examples/java-spark-sql/src/main/java/spark/GeoParquetAccessor.java create mode 100644 examples/java-spark-sql/src/main/java/spark/SedonaGeoParquetMain.java create mode 100644 examples/java-spark-sql/src/main/java/spark/SedonaSparkSession.java create mode 100644 examples/java-spark-sql/src/main/resources/application.properties create mode 100644 examples/java-spark-sql/src/test/java/spark/SedonaParquetTest.java create mode 100644 examples/java-spark-sql/src/test/resources/TestPoints.parquet create mode 100644 examples/java-spark-sql/src/test/resources/application.properties diff --git a/docs-overrides/package-lock.json b/docs-overrides/package-lock.json index 2fa3076660b..48e1b88f684 100644 --- a/docs-overrides/package-lock.json +++ b/docs-overrides/package-lock.json @@ -1950,7 +1950,6 @@ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1975,7 +1974,6 @@ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1987,7 +1985,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2001,7 +1998,6 @@ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -2135,7 +2131,6 @@ "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -2151,7 +2146,6 @@ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2163,7 +2157,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2177,7 +2170,6 @@ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=12.22" }, @@ -2192,8 +2184,7 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", @@ -2212,7 +2203,6 @@ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -2360,6 +2350,7 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -2500,6 +2491,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -2700,8 +2692,7 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", @@ -3057,6 +3048,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3083,7 +3075,6 @@ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -3094,7 +3085,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3262,8 +3252,7 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "Python-2.0", - "peer": true + "license": "Python-2.0" }, "node_modules/aria-query": { "version": "5.3.2", @@ -4003,6 +3992,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -4464,7 +4454,6 @@ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4617,8 +4606,7 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/define-data-property": { "version": "1.1.4", @@ -4728,7 +4716,6 @@ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -5310,6 +5297,7 @@ "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -5884,7 +5872,6 @@ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5896,7 +5883,6 @@ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -5914,7 +5900,6 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -5928,7 +5913,6 @@ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -5942,7 +5926,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5972,7 +5955,6 @@ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -5991,7 +5973,6 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -6193,16 +6174,14 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-uri": { "version": "3.1.0", @@ -6247,7 +6226,6 @@ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -6310,7 +6288,6 @@ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -6371,7 +6348,6 @@ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -6386,8 +6362,7 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/follow-redirects": { "version": "1.15.11", @@ -7839,7 +7814,6 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.8.19" } @@ -8280,7 +8254,6 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -8608,7 +8581,6 @@ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -8644,8 +8616,7 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -8659,16 +8630,14 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", @@ -8715,7 +8684,6 @@ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -8776,7 +8744,6 @@ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -8850,7 +8817,6 @@ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -8902,8 +8868,7 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.some": { "version": "4.6.0", @@ -9144,7 +9109,6 @@ } ], "license": "MIT", - "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -9468,7 +9432,6 @@ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -9505,7 +9468,6 @@ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -9522,7 +9484,6 @@ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -9616,7 +9577,6 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -9637,7 +9597,6 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -9855,7 +9814,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8.0" } @@ -9921,7 +9879,6 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -10396,7 +10353,6 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -10564,8 +10520,7 @@ "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/schema-utils": { "version": "4.3.3", @@ -10593,6 +10548,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -10995,7 +10951,6 @@ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -11009,7 +10964,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -11596,7 +11550,6 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" }, @@ -11793,8 +11746,7 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/textextensions": { "version": "3.3.0", @@ -11976,7 +11928,6 @@ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -12327,7 +12278,6 @@ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "punycode": "^2.1.0" } @@ -12536,6 +12486,7 @@ "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -12839,7 +12790,6 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -12972,7 +12922,6 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, diff --git a/examples/README.md b/examples/README.md index cde819ba3f0..e414c3f17be 100644 --- a/examples/README.md +++ b/examples/README.md @@ -29,6 +29,7 @@ The folder structure of this repository is as follows. * spark-sql: a Scala template shows how to use Sedona RDD, DataFrame and SQL API * flink-sql: a Java template show how to use Sedona SQL via Flink Table APIs +* java-spark-sql: a pure Java template implementation and use case for Sedona RDD, DataFrame and SQL API ## Compile and package @@ -59,6 +60,8 @@ To run the jar in this way, you need to: We highly suggest you use IDEs to run template projects on your local machine. For Scala, we recommend IntelliJ IDEA with Scala plug-in. For Java, we recommend IntelliJ IDEA and Eclipse. With the help of IDEs, **you don't have to prepare anything** (even don't need to download and set up Spark!). As long as you have Scala and Java, everything works properly! +* In the case of the java-spark-sql template, execute `mvn test` to run JUnit tests using sample GeoParquet file. + ### Scala Import the Scala template project as SBT project. Then run the Main file in this project. diff --git a/examples/java-spark-sql/.gitignore b/examples/java-spark-sql/.gitignore new file mode 100644 index 00000000000..b14e5ffe4de --- /dev/null +++ b/examples/java-spark-sql/.gitignore @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +/.idea/ +/target/ +dependency-reduced-pom.xml diff --git a/examples/java-spark-sql/pom.xml b/examples/java-spark-sql/pom.xml new file mode 100644 index 00000000000..6e8cd149818 --- /dev/null +++ b/examples/java-spark-sql/pom.xml @@ -0,0 +1,149 @@ + + + + 4.0.0 + + org.apache.sedona + sedona-java-spark-example + 1.6.1 + Sedona : Examples : Java Spark SQL + Example project for Apache Sedona with Java and Spark. + + + + provided + test + + 1.6.1 + 1.8.0-33.1 + 3.5.7 + 4.0.1 + + + + + org.datasyslab + geotools-wrapper + ${geotools.version} + + + org.apache.sedona + sedona-spark-shaded-3.5_2.13 + ${sedona.version} + + + org.apache.spark + spark-core_2.13 + ${spark.version} + + + org.apache.spark + spark-sql_2.13 + ${spark.version} + ${spark.scope} + + + javax.servlet + javax.servlet-api + ${javax.servlet.version} + ${javax.scope} + + + org.junit.jupiter + junit-jupiter-engine + 5.2.0-M1 + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + --add-opens=java.base/sun.nio.ch=ALL-UNNAMED + --add-opens=java.base/java.nio=ALL-UNNAMED + --add-opens=java.base/java.lang=ALL-UNNAMED + --add-opens=java.base/java.util=ALL-UNNAMED + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.1 + + + package + + shade + + + + + + + reference.conf + + + + Java Advanced Imaging Image I/O Tools + 1.1 + Sun Microsystems, Inc. + com.sun.media.imageio + 1.1 + Sun Microsystems, Inc. + com.sun.media.imageio + + + + spark.SedonaGeoParquetMain + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + --add-opens=java.base/sun.nio.ch=ALL-UNNAMED + --add-opens=java.base/java.nio=ALL-UNNAMED + --add-opens=java.base/java.lang=ALL-UNNAMED + --add-opens=java.base/java.util=ALL-UNNAMED + + + + + + + + diff --git a/examples/java-spark-sql/src/main/java/spark/GeoParquetAccessor.java b/examples/java-spark-sql/src/main/java/spark/GeoParquetAccessor.java new file mode 100644 index 00000000000..197f52c963a --- /dev/null +++ b/examples/java-spark-sql/src/main/java/spark/GeoParquetAccessor.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package spark; + +import org.apache.sedona.core.spatialRDD.SpatialRDD; +import org.apache.sedona.sql.utils.Adapter; +import org.apache.spark.sql.Dataset; +import org.apache.spark.sql.Row; +import org.apache.spark.sql.SparkSession; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.apache.sedona.core.spatialOperator.SpatialPredicate; +import org.apache.sedona.core.spatialOperator.RangeQuery; + +import java.util.List; + + +public class GeoParquetAccessor { + + private final SparkSession session; + private String parquetPath; + + public GeoParquetAccessor() { + this.session = new SedonaSparkSession().getSession(); + this.parquetPath = ""; + } + + //Overload with constructor that has Spark session provided + //Use to avoid error - can't have two SparkContext objects on one JVM + public GeoParquetAccessor(SparkSession session, String parquetPath) { + this.session = session; + this.parquetPath = parquetPath; + } + + public List selectFeaturesByPolygon(double xmin, double ymax, + double xmax, double ymin, + String geometryColumn) { + + //Read the GeoParquet file into a DataFrame + Dataset insarDF = session.read().format("geoparquet").load(parquetPath); + + //Convert the DataFrame to a SpatialRDD + //The second argument to toSpatialRdd is the name of the geometry column. + SpatialRDD insarRDD = Adapter.toSpatialRdd(insarDF, geometryColumn); + + // Define the polygon for the spatial query + GeometryFactory geometryFactory = new GeometryFactory(); + Coordinate[] coordinates = new Coordinate[] { + new Coordinate(xmin, ymin), + new Coordinate(xmax, ymin), + new Coordinate(xmax, ymax), + new Coordinate(xmin, ymax), + new Coordinate(xmin, ymin) // A closed polygon has the same start and end coordinate + }; + Polygon queryPolygon = geometryFactory.createPolygon(coordinates); + + // Perform the spatial range query + // This will return all geometries that intersect with the query polygon. + // Alternatives are SpatialPredicate.CONTAINS or SpatialPredicate.WITHIN + SpatialRDD resultRDD = new SpatialRDD<>(); + try { + resultRDD.rawSpatialRDD = RangeQuery.SpatialRangeQuery(insarRDD, queryPolygon, SpatialPredicate.INTERSECTS, false); + } catch (Exception e) { + e.printStackTrace(); + } + + // Collect the results back to the driver + return resultRDD.getRawSpatialRDD().collect(); + } + +} diff --git a/examples/java-spark-sql/src/main/java/spark/SedonaGeoParquetMain.java b/examples/java-spark-sql/src/main/java/spark/SedonaGeoParquetMain.java new file mode 100644 index 00000000000..77edc1f8811 --- /dev/null +++ b/examples/java-spark-sql/src/main/java/spark/SedonaGeoParquetMain.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package spark; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Properties; + +public class SedonaGeoParquetMain { + + protected static Properties properties; + protected static String parquetPath; + protected static SedonaSparkSession session; + + public static void main(String args[]) { + + session = new SedonaSparkSession(); + //Get parquetPath and any other application.properties + try { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Properties properties = new Properties(); + InputStream is = loader.getResourceAsStream("application.properties"); + properties.load(is); + parquetPath = properties.getProperty("parquet.path"); + } catch (IOException e) { + e.printStackTrace(); + parquetPath = ""; + } + GeoParquetAccessor accessor = new GeoParquetAccessor(session.session, parquetPath); + //Test parquet happens to be in New Zealand Transverse Mercator (EPSG:2193) (meters) + List geoms = accessor.selectFeaturesByPolygon(1155850, 4819840, 1252000, 4748100, "geometry"); + System.out.println("Coordinates of convex hull of points in boundary:"); + for (Geometry geom : geoms) { + Coordinate[] convexHullCoordinates = geom.convexHull().getCoordinates(); + for (Coordinate coord : convexHullCoordinates) { + System.out.println(String.format("\t%s", coord.toString())); + } + } + } +} diff --git a/examples/java-spark-sql/src/main/java/spark/SedonaSparkSession.java b/examples/java-spark-sql/src/main/java/spark/SedonaSparkSession.java new file mode 100644 index 00000000000..a99d7c06420 --- /dev/null +++ b/examples/java-spark-sql/src/main/java/spark/SedonaSparkSession.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package spark; + +import org.apache.spark.sql.SparkSession; +import org.apache.sedona.sql.utils.SedonaSQLRegistrator; +import org.apache.spark.SparkConf; +import org.apache.spark.sql.RuntimeConfig; + + +public class SedonaSparkSession { + + private SparkConf conf; + private RuntimeConfig config; + public SparkSession session; + + public SedonaSparkSession() { + + //Set configuration for localhost spark cluster. Intended ot be run from IDE or similar. + this.conf = new SparkConf().setAppName(this.getClass().getSimpleName()) + .setMaster("local[*]") + .set("spark.ui.enabled", "false") + .set("spark.driver.extraJavaOptions", + "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED"); + this.session = SparkSession.builder() + .config(this.conf) + .getOrCreate(); + //Configure Saprk session to execute with Sedona + SedonaSQLRegistrator.registerAll(this.session); + this.config = this.session.conf(); + } + + public SparkSession getSession() { + // Access SparkSession object + return this.session; + } + +} + diff --git a/examples/java-spark-sql/src/main/resources/application.properties b/examples/java-spark-sql/src/main/resources/application.properties new file mode 100644 index 00000000000..d4ed45762fa --- /dev/null +++ b/examples/java-spark-sql/src/main/resources/application.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +parquet.path=src/test/resources/TestPoints.parquet \ No newline at end of file diff --git a/examples/java-spark-sql/src/test/java/spark/SedonaParquetTest.java b/examples/java-spark-sql/src/test/java/spark/SedonaParquetTest.java new file mode 100644 index 00000000000..e9965266872 --- /dev/null +++ b/examples/java-spark-sql/src/test/java/spark/SedonaParquetTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package spark; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Properties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import org.apache.spark.sql.Dataset; +import org.apache.spark.sql.Row; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; + + +public class SedonaParquetTest { + + + protected static Properties properties; + protected static String parquetPath; + protected static SedonaSparkSession session; + + public SedonaParquetTest() { + } + + @BeforeAll + public static void setUpClass() throws IOException { + + session = new SedonaSparkSession(); + //Get parquetPath and any other application.properties + try { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Properties properties = new Properties(); + InputStream is = loader.getResourceAsStream("application.properties"); + properties.load(is); + parquetPath = properties.getProperty("parquet.path"); + } catch (IOException e) { + e.printStackTrace(); + parquetPath = ""; + } + + } + + @AfterAll + public static void tearDownClass() { + } + + @BeforeEach + public void setUp() { + } + + @AfterEach + public void tearDown() { + } + + @Test + public void connects() { + assertNotNull(session, "SparkSedonaSession not initialized correctly."); + assertNotNull(session.session, "Spark session not initialized inside SparkSedonaSession."); + } + + @Test + public void parquetAccessible() { + File file = new File(parquetPath); + assertTrue(file.exists(), "Parquet file does not exist."); + assertTrue(file.canRead(), "Can't read geoparquet file on record."); + } + + @Test + public void canLoadRDD() { + assertNotNull(session, "Session is null."); + Dataset insarDF = session.session.read() + .format("geoparquet") + .load(parquetPath); + assertNotNull(insarDF, "Dataset was not created."); + assertTrue(insarDF.count() > 0, "Dataset is empty."); + } + +} + + \ No newline at end of file diff --git a/examples/java-spark-sql/src/test/resources/TestPoints.parquet b/examples/java-spark-sql/src/test/resources/TestPoints.parquet new file mode 100644 index 0000000000000000000000000000000000000000..8038b545c743e51786a6fca5ed20b2c6422ec27e GIT binary patch literal 79834 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 literal 0 HcmV?d00001 diff --git a/examples/java-spark-sql/src/test/resources/application.properties b/examples/java-spark-sql/src/test/resources/application.properties new file mode 100644 index 00000000000..d4ed45762fa --- /dev/null +++ b/examples/java-spark-sql/src/test/resources/application.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +parquet.path=src/test/resources/TestPoints.parquet \ No newline at end of file From 4d688f98fd1808f30673e11527b12069de1180d7 Mon Sep 17 00:00:00 2001 From: Alec Zoeller Date: Wed, 5 Nov 2025 14:09:46 +1300 Subject: [PATCH 2/4] Added mvn spotless:apply step --- examples/java-spark-sql/pom.xml | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/examples/java-spark-sql/pom.xml b/examples/java-spark-sql/pom.xml index 6e8cd149818..240ec4b4865 100644 --- a/examples/java-spark-sql/pom.xml +++ b/examples/java-spark-sql/pom.xml @@ -36,6 +36,7 @@ 1.8.0-33.1 3.5.7 4.0.1 + 3.0.0 @@ -144,6 +145,39 @@ + + com.diffplug.spotless + spotless-maven-plugin + ${spotless.version} + + + + + + + .gitattributes + .gitignore + + + + + + true + 4 + + + + + + + 1.10 + + true + false + + + + From b59f17e9d5d0aa980b156e8107d8ba70e4a4de8c Mon Sep 17 00:00:00 2001 From: Jia Yu Date: Sat, 15 Nov 2025 21:09:54 -0800 Subject: [PATCH 3/4] Revert "Added examples/java-spark-sql. Java implementation of Sedona and GeoParquet (with test class and JUnit tests) provided a running localhost Spark cluster." This reverts commit ff4ce9050c98d8f77a8582fd248e7e34c588c0bd. --- docs-overrides/package-lock.json | 91 +++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 20 deletions(-) diff --git a/docs-overrides/package-lock.json b/docs-overrides/package-lock.json index 48e1b88f684..2fa3076660b 100644 --- a/docs-overrides/package-lock.json +++ b/docs-overrides/package-lock.json @@ -1950,6 +1950,7 @@ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1974,6 +1975,7 @@ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1985,6 +1987,7 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1998,6 +2001,7 @@ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -2131,6 +2135,7 @@ "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -2146,6 +2151,7 @@ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2157,6 +2163,7 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2170,6 +2177,7 @@ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=12.22" }, @@ -2184,7 +2192,8 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", @@ -2203,6 +2212,7 @@ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -2350,7 +2360,6 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -2491,7 +2500,6 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -2692,7 +2700,8 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", @@ -3048,7 +3057,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3075,6 +3083,7 @@ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -3085,6 +3094,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3252,7 +3262,8 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "Python-2.0" + "license": "Python-2.0", + "peer": true }, "node_modules/aria-query": { "version": "5.3.2", @@ -3992,7 +4003,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -4454,6 +4464,7 @@ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4606,7 +4617,8 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/define-data-property": { "version": "1.1.4", @@ -4716,6 +4728,7 @@ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -5297,7 +5310,6 @@ "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -5872,6 +5884,7 @@ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5883,6 +5896,7 @@ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -5900,6 +5914,7 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -5913,6 +5928,7 @@ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -5926,6 +5942,7 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5955,6 +5972,7 @@ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -5973,6 +5991,7 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -6174,14 +6193,16 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fast-uri": { "version": "3.1.0", @@ -6226,6 +6247,7 @@ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -6288,6 +6310,7 @@ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -6348,6 +6371,7 @@ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -6362,7 +6386,8 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/follow-redirects": { "version": "1.15.11", @@ -7814,6 +7839,7 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.8.19" } @@ -8254,6 +8280,7 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -8581,6 +8608,7 @@ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -8616,7 +8644,8 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -8630,14 +8659,16 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/json5": { "version": "2.2.3", @@ -8684,6 +8715,7 @@ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -8744,6 +8776,7 @@ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -8817,6 +8850,7 @@ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -8868,7 +8902,8 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.some": { "version": "4.6.0", @@ -9109,6 +9144,7 @@ } ], "license": "MIT", + "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -9432,6 +9468,7 @@ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -9468,6 +9505,7 @@ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -9484,6 +9522,7 @@ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -9577,6 +9616,7 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -9597,6 +9637,7 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -9814,6 +9855,7 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -9879,6 +9921,7 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -10353,6 +10396,7 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -10520,7 +10564,8 @@ "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/schema-utils": { "version": "4.3.3", @@ -10548,7 +10593,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -10951,6 +10995,7 @@ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -10964,6 +11009,7 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -11550,6 +11596,7 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" }, @@ -11746,7 +11793,8 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/textextensions": { "version": "3.3.0", @@ -11928,6 +11976,7 @@ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -12278,6 +12327,7 @@ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "punycode": "^2.1.0" } @@ -12486,7 +12536,6 @@ "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -12790,6 +12839,7 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -12922,6 +12972,7 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, From 57d5ce836369aa170199dd464d4da45551a1be4b Mon Sep 17 00:00:00 2001 From: Jia Yu Date: Sat, 15 Nov 2025 21:26:49 -0800 Subject: [PATCH 4/4] Refactor --- examples/README.md | 2 +- examples/java-spark-sql/pom.xml | 1 + .../main/java/spark/GeoParquetAccessor.java | 12 ++--- .../main/java/spark/SedonaGeoParquetMain.java | 8 ++-- .../main/java/spark/SedonaSparkSession.java | 37 +++++++-------- .../src/main/resources/application.properties | 2 +- .../test/java/spark/SedonaParquetTest.java | 45 +++++++++---------- .../src/test/resources/application.properties | 2 +- 8 files changed, 51 insertions(+), 58 deletions(-) diff --git a/examples/README.md b/examples/README.md index e414c3f17be..85bdd35bd17 100644 --- a/examples/README.md +++ b/examples/README.md @@ -60,7 +60,7 @@ To run the jar in this way, you need to: We highly suggest you use IDEs to run template projects on your local machine. For Scala, we recommend IntelliJ IDEA with Scala plug-in. For Java, we recommend IntelliJ IDEA and Eclipse. With the help of IDEs, **you don't have to prepare anything** (even don't need to download and set up Spark!). As long as you have Scala and Java, everything works properly! -* In the case of the java-spark-sql template, execute `mvn test` to run JUnit tests using sample GeoParquet file. +* In the case of the java-spark-sql template, execute `mvn test` to run JUnit tests using sample GeoParquet file. ### Scala diff --git a/examples/java-spark-sql/pom.xml b/examples/java-spark-sql/pom.xml index 240ec4b4865..640cf400f25 100644 --- a/examples/java-spark-sql/pom.xml +++ b/examples/java-spark-sql/pom.xml @@ -85,6 +85,7 @@ --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED + --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED diff --git a/examples/java-spark-sql/src/main/java/spark/GeoParquetAccessor.java b/examples/java-spark-sql/src/main/java/spark/GeoParquetAccessor.java index 197f52c963a..ba8e6a1f659 100644 --- a/examples/java-spark-sql/src/main/java/spark/GeoParquetAccessor.java +++ b/examples/java-spark-sql/src/main/java/spark/GeoParquetAccessor.java @@ -19,6 +19,8 @@ package spark; +import org.apache.sedona.core.spatialOperator.RangeQuery; +import org.apache.sedona.core.spatialOperator.SpatialPredicate; import org.apache.sedona.core.spatialRDD.SpatialRDD; import org.apache.sedona.sql.utils.Adapter; import org.apache.spark.sql.Dataset; @@ -28,14 +30,12 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Polygon; -import org.apache.sedona.core.spatialOperator.SpatialPredicate; -import org.apache.sedona.core.spatialOperator.RangeQuery; import java.util.List; public class GeoParquetAccessor { - + private final SparkSession session; private String parquetPath; @@ -43,7 +43,7 @@ public GeoParquetAccessor() { this.session = new SedonaSparkSession().getSession(); this.parquetPath = ""; } - + //Overload with constructor that has Spark session provided //Use to avoid error - can't have two SparkContext objects on one JVM public GeoParquetAccessor(SparkSession session, String parquetPath) { @@ -53,7 +53,7 @@ public GeoParquetAccessor(SparkSession session, String parquetPath) { public List selectFeaturesByPolygon(double xmin, double ymax, double xmax, double ymin, - String geometryColumn) { + String geometryColumn) { //Read the GeoParquet file into a DataFrame Dataset insarDF = session.read().format("geoparquet").load(parquetPath); @@ -86,5 +86,5 @@ public List selectFeaturesByPolygon(double xmin, double ymax, // Collect the results back to the driver return resultRDD.getRawSpatialRDD().collect(); } - + } diff --git a/examples/java-spark-sql/src/main/java/spark/SedonaGeoParquetMain.java b/examples/java-spark-sql/src/main/java/spark/SedonaGeoParquetMain.java index 77edc1f8811..4a114372830 100644 --- a/examples/java-spark-sql/src/main/java/spark/SedonaGeoParquetMain.java +++ b/examples/java-spark-sql/src/main/java/spark/SedonaGeoParquetMain.java @@ -21,7 +21,7 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; -import java.io.File; + import java.io.IOException; import java.io.InputStream; import java.util.List; @@ -30,18 +30,18 @@ public class SedonaGeoParquetMain { protected static Properties properties; - protected static String parquetPath; + protected static String parquetPath; protected static SedonaSparkSession session; public static void main(String args[]) { session = new SedonaSparkSession(); //Get parquetPath and any other application.properties - try { + try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Properties properties = new Properties(); InputStream is = loader.getResourceAsStream("application.properties"); - properties.load(is); + properties.load(is); parquetPath = properties.getProperty("parquet.path"); } catch (IOException e) { e.printStackTrace(); diff --git a/examples/java-spark-sql/src/main/java/spark/SedonaSparkSession.java b/examples/java-spark-sql/src/main/java/spark/SedonaSparkSession.java index a99d7c06420..6be6c995855 100644 --- a/examples/java-spark-sql/src/main/java/spark/SedonaSparkSession.java +++ b/examples/java-spark-sql/src/main/java/spark/SedonaSparkSession.java @@ -19,38 +19,33 @@ package spark; +import org.apache.sedona.spark.SedonaContext; import org.apache.spark.sql.SparkSession; -import org.apache.sedona.sql.utils.SedonaSQLRegistrator; -import org.apache.spark.SparkConf; -import org.apache.spark.sql.RuntimeConfig; public class SedonaSparkSession { - - private SparkConf conf; - private RuntimeConfig config; + public SparkSession session; public SedonaSparkSession() { - //Set configuration for localhost spark cluster. Intended ot be run from IDE or similar. - this.conf = new SparkConf().setAppName(this.getClass().getSimpleName()) - .setMaster("local[*]") - .set("spark.ui.enabled", "false") - .set("spark.driver.extraJavaOptions", - "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED"); - this.session = SparkSession.builder() - .config(this.conf) - .getOrCreate(); - //Configure Saprk session to execute with Sedona - SedonaSQLRegistrator.registerAll(this.session); - this.config = this.session.conf(); + //Set configuration for localhost spark cluster. Intended to be run from IDE or similar. + //Use SedonaContext builder to create SparkSession with Sedona extensions + SparkSession config = SedonaContext.builder() + .appName(this.getClass().getSimpleName()) + .master("local[*]") + .config("spark.ui.enabled", "false") + .config("spark.driver.extraJavaOptions", + "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED") + .getOrCreate(); + + //Create Sedona-enabled SparkSession + this.session = SedonaContext.create(config); } public SparkSession getSession() { - // Access SparkSession object + // Access SparkSession object return this.session; } - -} +} diff --git a/examples/java-spark-sql/src/main/resources/application.properties b/examples/java-spark-sql/src/main/resources/application.properties index d4ed45762fa..2c96c734a6e 100644 --- a/examples/java-spark-sql/src/main/resources/application.properties +++ b/examples/java-spark-sql/src/main/resources/application.properties @@ -15,4 +15,4 @@ # specific language governing permissions and limitations # under the License. -parquet.path=src/test/resources/TestPoints.parquet \ No newline at end of file +parquet.path=src/test/resources/TestPoints.parquet diff --git a/examples/java-spark-sql/src/test/java/spark/SedonaParquetTest.java b/examples/java-spark-sql/src/test/java/spark/SedonaParquetTest.java index e9965266872..036cdda9567 100644 --- a/examples/java-spark-sql/src/test/java/spark/SedonaParquetTest.java +++ b/examples/java-spark-sql/src/test/java/spark/SedonaParquetTest.java @@ -19,56 +19,55 @@ package spark; +import org.apache.spark.sql.Dataset; +import org.apache.spark.sql.Row; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.List; import java.util.Properties; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; -import org.apache.spark.sql.Dataset; -import org.apache.spark.sql.Row; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SedonaParquetTest { - - + + protected static Properties properties; - protected static String parquetPath; + protected static String parquetPath; protected static SedonaSparkSession session; - + public SedonaParquetTest() { } - + @BeforeAll public static void setUpClass() throws IOException { session = new SedonaSparkSession(); //Get parquetPath and any other application.properties - try { + try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Properties properties = new Properties(); InputStream is = loader.getResourceAsStream("application.properties"); - properties.load(is); + properties.load(is); parquetPath = properties.getProperty("parquet.path"); } catch (IOException e) { e.printStackTrace(); parquetPath = ""; } - + } - + @AfterAll public static void tearDownClass() { } - + @BeforeEach public void setUp() { } @@ -76,7 +75,7 @@ public void setUp() { @AfterEach public void tearDown() { } - + @Test public void connects() { assertNotNull(session, "SparkSedonaSession not initialized correctly."); @@ -101,5 +100,3 @@ public void canLoadRDD() { } } - - \ No newline at end of file diff --git a/examples/java-spark-sql/src/test/resources/application.properties b/examples/java-spark-sql/src/test/resources/application.properties index d4ed45762fa..2c96c734a6e 100644 --- a/examples/java-spark-sql/src/test/resources/application.properties +++ b/examples/java-spark-sql/src/test/resources/application.properties @@ -15,4 +15,4 @@ # specific language governing permissions and limitations # under the License. -parquet.path=src/test/resources/TestPoints.parquet \ No newline at end of file +parquet.path=src/test/resources/TestPoints.parquet