-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #909 from Kotlin/geo
GeoDataFrame init
- Loading branch information
Showing
25 changed files
with
880 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/execute.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package org.jetbrains.kotlinx.dataframe.jupyter | ||
|
||
import org.jetbrains.kotlinx.dataframe.codeGen.CodeWithConverter | ||
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelHost | ||
import org.jetbrains.kotlinx.jupyter.api.VariableName | ||
import kotlin.reflect.KProperty | ||
import kotlin.reflect.KType | ||
|
||
internal fun KotlinKernelHost.execute(codeWithConverter: CodeWithConverter, argument: String): VariableName? { | ||
val code = codeWithConverter.with(argument) | ||
return if (code.isNotBlank()) { | ||
val result = execute(code) | ||
if (codeWithConverter.hasConverter) { | ||
result.name | ||
} else { | ||
null | ||
} | ||
} else { | ||
null | ||
} | ||
} | ||
|
||
internal fun KotlinKernelHost.execute( | ||
codeWithConverter: CodeWithConverter, | ||
property: KProperty<*>, | ||
type: KType, | ||
): VariableName? { | ||
val variableName = "(${property.name}${if (property.returnType.isMarkedNullable) "!!" else ""} as $type)" | ||
return execute(codeWithConverter, variableName) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
## :dataframe-geo | ||
|
||
This module, published as `dataframe-geo`, contains all logic and tests for DataFrame to be able to work | ||
with geographical data. | ||
|
||
Experimental. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import org.jetbrains.kotlin.gradle.tasks.BaseKotlinCompile | ||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile | ||
|
||
plugins { | ||
with(libs.plugins) { | ||
alias(kotlin.jvm) | ||
alias(publisher) | ||
alias(jupyter.api) | ||
alias(ktlint) | ||
alias(dataframe) | ||
alias(ksp) | ||
} | ||
} | ||
|
||
group = "org.jetbrains.kotlinx" | ||
|
||
repositories { | ||
// geo repository should come before Maven Central | ||
maven("https://repo.osgeo.org/repository/release") | ||
mavenCentral() | ||
} | ||
|
||
// https://stackoverflow.com/questions/26993105/i-get-an-error-downloading-javax-media-jai-core1-1-3-from-maven-central | ||
// jai core dependency should be excluded from geotools dependencies and added separately | ||
fun ExternalModuleDependency.excludeJaiCore() = exclude("javax.media", "jai_core") | ||
|
||
dependencies { | ||
api(project(":core")) | ||
|
||
implementation(libs.geotools.main) { excludeJaiCore() } | ||
implementation(libs.geotools.shapefile) { excludeJaiCore() } | ||
implementation(libs.geotools.geojson) { excludeJaiCore() } | ||
implementation(libs.geotools.referencing) { excludeJaiCore() } | ||
implementation(libs.geotools.epsg.hsql) { excludeJaiCore() } | ||
|
||
implementation(libs.jai.core) | ||
|
||
implementation(libs.jts.core) | ||
implementation(libs.jts.io.common) | ||
|
||
implementation(libs.ktor.client.core) | ||
implementation(libs.ktor.client.cio) | ||
implementation(libs.ktor.client.content.negotiation) | ||
implementation(libs.ktor.serialization.kotlinx.json) | ||
|
||
testImplementation(kotlin("test")) | ||
} | ||
|
||
tasks.withType<KotlinCompile>().configureEach { | ||
val friendModule = project(":core") | ||
val jarTask = friendModule.tasks.getByName("jar") as Jar | ||
val jarPath = jarTask.archiveFile.get().asFile.absolutePath | ||
(this as BaseKotlinCompile).friendPaths.from(jarPath) | ||
} | ||
|
||
kotlinPublications { | ||
publication { | ||
publicationName = "dataframeGeo" | ||
artifactId = "dataframe-geo" | ||
description = "GeoDataFrame API" | ||
packageName = artifactId | ||
} | ||
} | ||
|
||
tasks.processJupyterApiResources { | ||
libraryProducers = listOf("org.jetbrains.kotlinx.dataframe.jupyter.IntegrationGeo") | ||
} | ||
|
||
tasks.test { | ||
useJUnitPlatform() | ||
} |
76 changes: 76 additions & 0 deletions
76
dataframe-geo/src/main/kotlin/org/jetbrains/kotlinx/dataframe/geo/GeoDataFrame.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package org.jetbrains.kotlinx.dataframe.geo | ||
|
||
import org.geotools.api.referencing.crs.CoordinateReferenceSystem | ||
import org.geotools.geometry.jts.JTS | ||
import org.geotools.referencing.CRS | ||
import org.jetbrains.kotlinx.dataframe.DataFrame | ||
import org.jetbrains.kotlinx.dataframe.api.update | ||
import org.jetbrains.kotlinx.dataframe.api.with | ||
|
||
/** | ||
* A data structure representing a geographical DataFrame, combining spatial data with | ||
* an optional Coordinate Reference System (CRS). | ||
* | ||
* @param T The type parameter extending `WithGeometry`, indicating the presence of a geometry column. | ||
* @property df The underlying `DataFrame` containing geometries. | ||
* @property crs The coordinate reference system associated with the data, if any. | ||
*/ | ||
class GeoDataFrame<T : WithGeometry>(val df: DataFrame<T>, val crs: CoordinateReferenceSystem?) { | ||
/** | ||
* Creates a new `GeoDataFrame` with the modified underlying DataFrame. | ||
* | ||
* @param block The block defining the transformations to be applied to the DataFrame. | ||
* @return A new `GeoDataFrame` instance with updated dataframe and the same CRS. | ||
*/ | ||
inline fun modify(block: DataFrame<T>.() -> DataFrame<T>): GeoDataFrame<T> = GeoDataFrame(df.block(), crs) | ||
|
||
/** | ||
* Transforms the geometries to a specified Coordinate Reference System (CRS). | ||
* | ||
* This function reprojects the geometry data from the current CRS to a target CRS. | ||
* If no target CRS is specified and the `GeoDataFrame` has no CRS, WGS 84 is used by default. | ||
* | ||
* @param targetCrs The target CRS for transformation. | ||
* @return A new `GeoDataFrame` with reprojected geometries and the specified CRS. | ||
*/ | ||
fun applyCrs(targetCrs: CoordinateReferenceSystem): GeoDataFrame<T> { | ||
if (crs == null) { | ||
return GeoDataFrame(df, targetCrs) | ||
} | ||
if (targetCrs == this.crs) return this | ||
// Use WGS 84 by default TODO | ||
val sourceCRS: CoordinateReferenceSystem = this.crs | ||
val transform = CRS.findMathTransform(sourceCRS, targetCrs, true) | ||
return GeoDataFrame( | ||
df.update { geometry }.with { JTS.transform(it, transform) }, | ||
targetCrs, | ||
) | ||
} | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (javaClass != other?.javaClass) return false | ||
|
||
other as GeoDataFrame<*> | ||
|
||
if (df != other.df) return false | ||
|
||
return when { | ||
crs == null && other.crs == null -> true | ||
crs == null || other.crs == null -> false | ||
else -> CRS.equalsIgnoreMetadata(crs, other.crs) | ||
} | ||
} | ||
|
||
override fun hashCode(): Int { | ||
var result = df.hashCode() | ||
result = 31 * result + (crs?.hashCode() ?: 0) | ||
return result | ||
} | ||
|
||
override fun toString(): String = "GeoDataFrame(df=$df, crs=$crs)" | ||
|
||
companion object { | ||
val DEFAULT_CRS = CRS.decode("EPSG:4326", true) | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
dataframe-geo/src/main/kotlin/org/jetbrains/kotlinx/dataframe/geo/WithGeometry.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package org.jetbrains.kotlinx.dataframe.geo | ||
|
||
import org.jetbrains.kotlinx.dataframe.annotations.DataSchema | ||
import org.locationtech.jts.geom.Geometry | ||
import org.locationtech.jts.geom.LineString | ||
import org.locationtech.jts.geom.MultiLineString | ||
import org.locationtech.jts.geom.MultiPoint | ||
import org.locationtech.jts.geom.MultiPolygon | ||
import org.locationtech.jts.geom.Point | ||
import org.locationtech.jts.geom.Polygon | ||
|
||
@DataSchema | ||
interface WithGeometry { | ||
val geometry: Geometry | ||
} | ||
|
||
@DataSchema | ||
interface WithPolygonGeometry : WithGeometry { | ||
override val geometry: Polygon | ||
} | ||
|
||
@DataSchema | ||
interface WithMultiPolygonGeometry : WithGeometry { | ||
override val geometry: MultiPolygon | ||
} | ||
|
||
@DataSchema | ||
interface WithPointGeometry : WithGeometry { | ||
override val geometry: Point | ||
} | ||
|
||
@DataSchema | ||
interface WithMultiPointGeometry : WithGeometry { | ||
override val geometry: MultiPoint | ||
} | ||
|
||
@DataSchema | ||
interface WithLineStringGeometry : WithGeometry { | ||
override val geometry: LineString | ||
} | ||
|
||
@DataSchema | ||
interface WithMultiLineStringGeometry : WithGeometry { | ||
override val geometry: MultiLineString | ||
} |
15 changes: 15 additions & 0 deletions
15
dataframe-geo/src/main/kotlin/org/jetbrains/kotlinx/dataframe/geo/bounds.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package org.jetbrains.kotlinx.dataframe.geo | ||
|
||
import org.geotools.geometry.jts.ReferencedEnvelope | ||
import org.jetbrains.kotlinx.dataframe.api.asIterable | ||
import org.jetbrains.kotlinx.dataframe.geo.jts.computeBounds | ||
|
||
/** | ||
* Computes the bounding envelope for all geometries in a `GeoDataFrame`, | ||
* considering the specified coordinate reference system (CRS). | ||
* | ||
* @receiver The `GeoDataFrame` containing the geometries for which to compute bounds. | ||
* @return The bounding envelope that includes all geometries, | ||
* associated with the CRS of the `GeoDataFrame`. | ||
*/ | ||
fun GeoDataFrame<*>.bounds(): ReferencedEnvelope = ReferencedEnvelope(df.geometry.asIterable().computeBounds(), crs) |
Oops, something went wrong.