A Spark DataSource v2 implementation for accessing geospatial data from Geodesic with seamless Apache Sedona integration.
- Spark DataSource v2: Native Spark SQL integration with optimized data loading
- Apache Sedona Integration: Built-in support for spatial operations and geometry types
- Authentication: Secure access using Geodesic API keys or token-based authentication
- Flexible Configuration: Support for custom datasets, projects, and collections
Add the following dependency to your pom.xml:
<dependency>
<groupId>ai.seer</groupId>
<artifactId>geodesic-spark-datasource-sedona_2.12</artifactId>
<version>0.1.7</version>
</dependency>Add the following to your build.sbt:
libraryDependencies += "ai.seer" %% "geodesic-spark-datasource-sedona" % "0.1.7"Add the following to your build.gradle:
implementation 'ai.seer:geodesic-spark-datasource-sedona_2.12:0.1.7'import org.apache.spark.sql.SparkSession
import org.apache.sedona.spark.SedonaContext
// Create Spark session
val spark = SparkSession
.builder()
.master("local[*]")
.appName("GeodesicDemo")
.getOrCreate()
// Create Sedona context for spatial operations
val sedona = SedonaContext.create(spark)
// Load data from Geodesic
val df = sedona.read
.format("ai.seer.geodesic.sources.boson")
.option("datasetId", "ukr-adm3-boundaries")
.option("projectId", "global")
.load()
// Display the data
df.show()
// Perform spatial operations
df.createOrReplaceTempView("boundaries")
sedona.sql("SELECT *, ST_Area(geometry) as area FROM boundaries").show()from pyspark.sql import SparkSession
from sedona.spark import SedonaContext
# Create Spark session
spark = SparkSession.builder \
.appName("GeodesicDemo") \
.master("local[*]") \
.getOrCreate()
# Create Sedona context
sedona = SedonaContext.create(spark)
# Load data from Geodesic
df = sedona.read \
.format("ai.seer.geodesic.sources.boson") \
.option("datasetId", "ukr-adm3-boundaries") \
.option("projectId", "global") \
.load()
# Display the data
df.show()
# Perform spatial operations
df.createOrReplaceTempView("boundaries")
sedona.sql("SELECT *, ST_Area(geometry) as area FROM boundaries").show()The DataSource supports multiple authentication methods:
export GEODESIC_API_KEY="your-api-key-here"
export GEODESIC_HOST="https://api.geodesic.seerai.space" # Optional, defaults to this URLCreate a configuration file at:
import geodesic
geodesic.authenticate()or
$ geodesic authenticateexport GEODESIC_CONFIG_PATH="/path/to/your/config.json"| Option | Description | Default | Required |
|---|---|---|---|
datasetId |
The ID of the dataset to load | - | Yes |
projectId |
The project containing the dataset | - | Yes |
collectionId |
The collection within the dataset | Same as datasetId |
No |
pageSize |
Number of features to fetch per page | Dataset Default or 2000 |
No |
val df = sedona.read
.format("ai.seer.geodesic.sources.boson")
.option("datasetId", "my-dataset")
.option("projectId", "my-project")
.option("collectionId", "my-collection")
.option("pageSize", "5000")
.load()Once loaded, you can use all Apache Sedona spatial functions:
// Register as a temporary view
df.createOrReplaceTempView("geodata")
// Spatial queries
sedona.sql("""
SELECT
*,
ST_Area(geometry) as area,
ST_Centroid(geometry) as centroid,
ST_Buffer(geometry, 0.01) as buffered_geom
FROM geodata
WHERE ST_Area(geometry) > 1000
""").show()
// Spatial joins
val otherData = sedona.read.format("...").load()
otherData.createOrReplaceTempView("other")
sedona.sql("""
SELECT a.*, b.*
FROM geodata a
JOIN other b ON ST_Intersects(a.geometry, b.geometry)
""").show()The DataSource automatically infers the schema from the Geodesic dataset metadata. The schema includes:
- All dataset fields with their appropriate Spark SQL types (
string,integer,double,boolean) - A
geometrycolumn with Sedona'sGeometryUDTtype for spatial operations
- Pagination: Adjust
pageSizebased on your memory constraints and network conditions - Caching: Cache frequently accessed datasets using
df.cache() - Partitioning: Consider repartitioning large datasets for better parallelism
The DataSource includes robust error handling for:
- Network connectivity issues
- Authentication failures
- Invalid dataset or project IDs
- Malformed geometry data
Errors are logged with detailed messages to help with troubleshooting.
- Apache Spark 3.3.0+
- Scala 2.12
- Apache Sedona 1.7.1+
- Java 8+
We welcome contributions! Please follow these guidelines:
For automatic semantic versioning, use these patterns in your commit messages:
(MAJOR)- Breaking changes that require a major version bump(MINOR)- New features that are backward compatible- No suffix - Bug fixes, documentation, or other patch-level changes
Example: "Add support for custom CRS transformations (MINOR)"
- Clone the repository
- Set up your Geodesic authentication (see Configuration section)
- Run tests:
sbt test - Build:
sbt compile
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
For questions, issues, or feature requests:
- GitHub Issues: https://github.com/seerai/geodesic-spark-datasource/issues
- Email: contact@seerai.space
- Documentation: https://docs.geodesic.seerai.space
- NEW: Automatic Spatial Filter Pushdown - 17-43x performance improvement for spatial queries
- NEW: Multiple Spatial Predicates Support:
ST_Intersects- geometric intersectionST_Contains- geometric containmentST_Within- geometric within relationshipST_Overlaps- geometric overlapST_Touches- geometric touchingST_Crosses- geometric crossing
- NEW: GeodesicSparkExtension - Configuration-driven spatial optimization
- NEW: Zero-Code Spatial Optimization - Enable with:
.config("spark.sql.extensions", "ai.seer.geodesic.GeodesicSparkExtension") - NEW: Enhanced Python Examples - Spatial + metadata filtering examples in
geodesic_pyspark_examples.py
// Scala - Automatic spatial filter pushdown
val spark = SparkSession.builder()
.config("spark.sql.extensions", "ai.seer.geodesic.GeodesicSparkExtension")
.getOrCreate()
val sedona = SedonaContext.create(spark)
// This query now uses server-side spatial filtering (17-43x faster!)
val result = sedona.sql("""
SELECT name, admin_level
FROM boundaries
WHERE ST_Intersects(geometry, ST_GeomFromWKT('POLYGON((30 50, 31 50, 31 51, 30 51, 30 50))'))
AND admin_level = 3
""")# Python - Same automatic spatial filter pushdown
config = (
SedonaContext.builder()
.config("spark.sql.extensions", "ai.seer.geodesic.GeodesicSparkExtension")
.getOrCreate()
)
sedona = SedonaContext.create(config)
# This query now uses server-side spatial filtering (17-43x faster!)
result = sedona.sql("""
SELECT name, admin_level
FROM boundaries
WHERE ST_Contains(geometry, ST_GeomFromWKT('POINT(30.5 50.5)'))
AND name LIKE '%Kyiv%'
""")- Fixed: JSON parsing error when API response has missing or null "links" field
- Improved: Made
linksfield optional inFeatureCollectionto handle various API response formats - Enhanced: More robust pagination handling for datasets without pagination links
- Fixed: ClassCastException when displaying DataFrame data due to schema-value order mismatch
- Improved: Data row creation now ensures values match schema field order exactly
- Fixed: Infinite loop issue in DataSourceExample when reaching end of dataset
- Fixed: Proper pagination termination when
nextLinkbecomesNone - Fixed: README badges (Maven Central, Build Status, GitHub Release)
- Fixed: Dependency examples for SBT and Gradle
- Improved: Cleaned up debug logging and simplified pagination logic
- Added: GitHub Release badge to README
- Spark DataSource v2 implementation
- Apache Sedona integration
- Pagination support
- Authentication via API key and config file
- Comprehensive error handling