Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ docs/src/*.png
*.sha256
.DS_Store
Manifest.toml
Manifest-v*.toml
*.jl.cov
*.jl.*.cov
*.jl.mem
docs/final_site
node_modules/
test/data
12 changes: 10 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ Extents = "411431e0-e8b7-467b-b5e0-f676ba4f2910"
GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f"
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
GeometryOps = "3251bfac-6a57-4b6d-aa61-ac1fef2975ab"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Proj = "c94c279d-25a6-4763-9509-64d165bea63e"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
Expand All @@ -31,9 +30,12 @@ GeoDataFramesGeoParquetExt = "GeoParquet"
GeoDataFramesShapefileExt = "Shapefile"

[compat]
Aqua = "0.8"
ArchGDAL = "0.10"
DataAPI = "1.13"
DataFrames = "1.4"
Dates = "1"
ExplicitImports = "1.15"
Extents = "0.1"
FlatGeobuf = "0.2"
GeoArrow = "0.2"
Expand All @@ -42,18 +44,24 @@ GeoInterface = "1.0.1"
GeoJSON = "0.8.1"
GeoParquet = "0.2.1, 0.3"
GeometryOps = "0.1.23"
LibGEOS = "0.9"
Pkg = "1"
Proj = "1.8.1"
Reexport = "1.2"
Shapefile = "0.13.1"
Tables = "1"
Test = "1"
TestItemRunner = "1.1.0"
julia = "1.10"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
LibGEOS = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a"

[targets]
test = ["Test", "Dates", "Shapefile", "GeoArrow", "GeoJSON", "GeoParquet", "FlatGeobuf", "LibGEOS", "TestItemRunner"]
test = ["Aqua", "ExplicitImports", "Pkg", "Test", "Dates", "Shapefile", "GeoArrow", "GeoJSON", "GeoParquet", "FlatGeobuf", "LibGEOS", "TestItemRunner"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://evetion.github.io/GeoDataFrames.jl/dev)
[![CI](https://github.com/evetion/GeoDataFrames.jl/actions/workflows/ci.yml/badge.svg)](https://github.com/evetion/GeoDataFrames.jl/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/evetion/GeoDataFrames.jl/branch/master/graph/badge.svg?token=38QJAX7H9I)](https://codecov.io/gh/evetion/GeoDataFrames.jl)
[![Aqua QA](https://juliatesting.github.io/Aqua.jl/dev/assets/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)


GeoDataFrames provides a simple and efficient way to work with geospatial vector data in Julia. By combining the power of DataFrames with [ArchGDAL](https://github.com/yeesian/ArchGDAL.jl/) and native Julia packages such as [GeometryOps](https://juliageo.org/GeometryOps.jl/stable/), it offers a familiar interface for handling geographical data while maintaining Julia's performance advantages. The package supports reading and writing various geospatial formats, making it easy to integrate into your data analysis workflows. GeoDataFrames takes its inspiration from Python's [GeoPandas](https://geopandas.org/en/stable/).
Expand Down
13 changes: 6 additions & 7 deletions docs/Manifest.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ DocumenterVitepress = "4710194d-e776-4893-9690-8d956a29c365"
FlatGeobuf = "d985ece1-97de-4d33-914c-38fb84042e15"
GeoArrow = "5bc3a8d9-1bfb-4624-ba94-a391279174d6"
GeoDataFrames = "62cb38b5-d8d2-4862-a48e-6a340996859f"
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
GeoJSON = "61d90e0f-e114-555e-ac52-39dfb47a3ef9"
GeoParquet = "e99870d8-ce00-4fdd-aeee-e09192881159"
LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589"
Expand Down
34 changes: 30 additions & 4 deletions src/GeoDataFrames.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,39 @@
"""
GeoDataFrames

Work with geospatial vector data (points, lines, polygons and their attributes)
using the familiar `DataFrame` from [DataFrames.jl](https://dataframes.juliadata.org/).

A GeoDataFrame is just a regular `DataFrame` with one or more columns of
[GeoInterface.jl](https://juliageo.org/GeoInterface.jl/) compatible geometries;
there is no dedicated type. The package provides:

- [`read`](@ref) and [`write`](@ref) for the most common vector file formats
(GeoPackage, Shapefile, GeoJSON, FlatGeobuf, GeoParquet, Arrow, ...).
- Coordinate reference system helpers such as [`setcrs!`](@ref),
[`reproject`](@ref) and [`reproject!`](@ref).
- Re-exports of `DataFrames`, `GeoInterface`, `GeometryOps` and `Extents` so that
`using GeoDataFrames` is usually all you need.

```julia
using GeoDataFrames
df = DataFrame(geometry = GeoInterface.Point.(rand(10), rand(10)), name = "test")
GeoDataFrames.write("points.gpkg", df)
```
"""
Comment on lines +1 to +23

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this just be the readme? Harder to maintain docstrings in two places

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you mean, copy that content, or do you want to do it automagically?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC Julia already does it automatically, try ?GeometryOps

module GeoDataFrames

import ArchGDAL as AG
using DataFrames
using Tables
using DataFrames: DataFrames, DataFrame, DataFrameRow, metadata, metadata!, rename!
using Tables: Tables
import GeoFormatTypes as GFT
import GeoInterface as GI
using DataAPI
using Reexport
using GeoInterface: GeoInterface
using Extents: Extents
using DataAPI: DataAPI
using Reexport: Reexport, @reexport
import GeometryOps as GO
using GeometryOps: GeometryOps
import Proj # For GO reproject

include("vector.jl")
Expand Down
30 changes: 24 additions & 6 deletions src/drivers.jl
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
abstract type AbstractDriver end

"""
GeoJSON driver
GeoJSONDriver()

Driver for reading and writing GeoJSON files, backed by the `GeoJSON` package.
Load `GeoJSON` to enable it.
"""
struct GeoJSONDriver <: AbstractDriver end
"""
Shapefile driver
ShapefileDriver()

Driver for reading and writing ESRI Shapefiles, backed by the `Shapefile` package.
Load `Shapefile` to enable it.
"""
struct ShapefileDriver <: AbstractDriver end
"""
GeoParquet driver
GeoParquetDriver()

Driver for reading and writing (Geo)Parquet files, backed by the `GeoParquet` package.
Load `GeoParquet` to enable it.
"""
struct GeoParquetDriver <: AbstractDriver end
"""
FlatGeobuf driver
FlatGeobufDriver()

Driver for reading and writing FlatGeobuf files, backed by the `FlatGeobuf` package.
Load `FlatGeobuf` to enable it.
"""
struct FlatGeobufDriver <: AbstractDriver end
"""
ArchGDAL driver (default)
ArchGDALDriver()

Default driver, backed by the `ArchGDAL` package. Supports a wide range of formats and is
always available, requiring no extra package to be loaded.
"""
struct ArchGDALDriver <: AbstractDriver end
"""
GeoArrow driver
GeoArrowDriver()

Driver for reading and writing (Geo)Arrow files, backed by the `GeoArrow` package.
Load `GeoArrow` to enable it.
"""
struct GeoArrowDriver <: AbstractDriver end

Expand Down
5 changes: 5 additions & 0 deletions src/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
@reexport using DataFrames
@reexport using GeometryOps

# `flatten` and `union` are exported by both GeometryOps and DataFrames/Base.
# Resolve the conflict explicitly in favor of GeometryOps' geometry operations.
using GeometryOps: flatten, union
export flatten, union

using GeoFormatTypes:
AbstractWellKnownText,
CoordSys,
Expand Down
45 changes: 34 additions & 11 deletions src/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,24 @@ const lookup_type = Dict{Tuple{DataType, Int}, AG.OGRwkbGeometryType}(
"""
read(fn::AbstractString; kwargs...)

Read a file into a DataFrame. Any kwargs are passed to the driver, by default set to [`ArchGDALDriver`](@ref).
Read a file into a `DataFrame`. Any kwargs are passed to the driver, by default set to [`ArchGDALDriver`](@ref).

Returns a `DataFrame` whose geometry column(s) hold GeoInterface.jl compatible geometries, with
the coordinate reference system and geometry column names stored as table metadata.

# Example
```jldoctest
julia> df = DataFrame(geometry = GeoInterface.Point.([(1.0, 2.0), (3.0, 4.0)]), name = ["a", "b"]);

julia> path = GeoDataFrames.write(joinpath(tempdir(), "example.gpkg"), df);

julia> df2 = GeoDataFrames.read(path);

julia> names(df2)
2-element Vector{String}:
"geometry"
"name"
```
"""
function read(fn; kwargs...)
gfn = _gdal_path(fn)
Expand All @@ -60,16 +77,10 @@ function read(fn; kwargs...)
df
end

@deprecate read(fn::AbstractString, layer::Union{AbstractString, Integer}; kwargs...) read(
fn;
layer,
kwargs...,
)

"""
read(driver::AbstractDriver, fn::AbstractString; kwargs...)

Read a file into a DataFrame using the specified `driver`. Any kwargs are passed to the driver, by default set to [`ArchGDALDriver`](@ref).
Read a file into a DataFrame using the specified `driver`. Any kwargs are passed to the driver, by default set to [`ArchGDALDriver`](@ref). Returns a `DataFrame`.
"""
function read(driver::AbstractDriver, fn::AbstractString; kwargs...)
@debug "Using GDAL for reading, import $(package(driver)) for a native driver."
Expand All @@ -82,7 +93,7 @@ end
Read a file into a DataFrame using the ArchGDAL driver.
By default you only get the first layer, unless you specify either the index (0 based) or name (string) of the layer.
Other supported kwargs are passed to the [ArchGDAL read](https://yeesian.com/ArchGDAL.jl/stable/reference/#ArchGDAL.read-Tuple{AbstractString}) method.
The `options` keyword argument can be used to pass GDAL open options.
The `options` keyword argument can be used to pass GDAL open options. Returns a `DataFrame`.
"""
function read(driver::ArchGDALDriver, fn::AbstractString; layer = nothing, kwargs...)
fn = _gdal_path(fn)
Expand Down Expand Up @@ -146,6 +157,18 @@ end
write(fn::AbstractString, table; kwargs...)

Write the provided `table` to `fn`. A driver is selected based on the extension of `fn`.

Returns the path `fn` that was written.

# Example
```jldoctest
julia> df = DataFrame(geometry = GeoInterface.Point.([(1.0, 2.0), (3.0, 4.0)]), name = ["a", "b"]);

julia> path = GeoDataFrames.write(joinpath(tempdir(), "example.gpkg"), df);

julia> isfile(path)
true
```
"""
function write(fn::AbstractString, table; kwargs...)
ext = last(splitext(fn))
Expand All @@ -155,7 +178,7 @@ end
"""
write(driver::AbstractDriver, fn::AbstractString, table; kwargs...)

Write the provided `table` to `fn` using the specified driver. Any kwargs are passed to the driver, by default set to [`ArchGDALDriver`](@ref).
Write the provided `table` to `fn` using the specified driver. Any kwargs are passed to the driver, by default set to [`ArchGDALDriver`](@ref). Returns the path `fn` that was written.
"""
function write(driver::AbstractDriver, fn::AbstractString, table; kwargs...)
@debug "Using GDAL for writing, import $(package(driver)) for a native driver."
Expand All @@ -165,7 +188,7 @@ end
"""
write(driver::ArchGDALDriver, fn::AbstractString, table; layer_name="data", crs::Union{GFT.GeoFormat,Nothing}=getcrs(table), driver::Union{Nothing,AbstractString}=nothing, options::Dict{String,String}=Dict(), geom_columns::Tuple{Symbol}=getgeometrycolumns(table), kwargs...)

Write the provided `table` to `fn` using the ArchGDAL driver.
Write the provided `table` to `fn` using the ArchGDAL driver. Returns the path `fn` that was written.
"""
function write(
::ArchGDALDriver,
Expand Down
8 changes: 4 additions & 4 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ setcrs!(df::DataFrame, crs) = metadata!(df, "GEOINTERFACE:crs", crs; style = :no
Reproject the geometries in a DataFrame `df` to a new Coordinate Reference System `target_crs`, from the current CRS.
See also [`reproject(df, source_crs, target_crs)`](@ref) and the in place version [`reproject!(df, target_crs)`](@ref).
`always_xy` (`true` by default) can override the default axis mapping strategy of the CRS. If true, input is
assumed to be in the traditional GIS order (longitude, latitude).
assumed to be in the traditional GIS order (longitude, latitude). Returns a new `DataFrame`; the input `df` is left unchanged.
"""
function GO.reproject(
df::DataFrame,
Expand All @@ -177,7 +177,7 @@ end
reproject(df::DataFrame, source_crs, target_crs; [always_xy=true])

Reproject the geometries in a DataFrame `df` from the crs `source_crs` to a new crs `target_crs`.
This overrides any current CRS of the Dataframe.
This overrides any current CRS of the Dataframe. Returns a new `DataFrame`; the input `df` is left unchanged.
"""
function GO.reproject(
df::DataFrame,
Expand All @@ -199,7 +199,7 @@ end
"""
reproject!(df::DataFrame, target_crs; [always_xy=true])

Reproject the geometries in a DataFrame `df` to a new Coordinate Reference System `target_crs`, from the current CRS, in place.
Reproject the geometries in a DataFrame `df` to a new Coordinate Reference System `target_crs`, from the current CRS, in place. Returns the modified `df`.
"""
function reproject!(df::DataFrame, target_crs; always_xy = true, kwargs...)
reproject!(df, getcrs(df), target_crs; always_xy, kwargs...)
Expand All @@ -209,7 +209,7 @@ end
reproject!(df::DataFrame, source_crs, target_crs; [always_xy=true])

Reproject the geometries in a DataFrame `df` from the crs `source_crs` to a new crs `target_crs` in place.
This overrides any current CRS of the Dataframe.
This overrides any current CRS of the Dataframe. Returns the modified `df`.
"""
function reproject!(df::DataFrame, source_crs, target_crs; always_xy = true, kwargs...)
columns = Tables.columns(df)
Expand Down
15 changes: 13 additions & 2 deletions src/vector.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
"""
GeometryVector(A)
GeometryVector(A::Vector)

A vector of geometries, as used in GeoDataFrames.
A thin wrapper around a `Vector` of geometries, used as the geometry column type in
GeoDataFrames. It behaves like a regular `AbstractVector` (indexing, mutation, `similar`),
and exists as a distinct type so geometry-specific behaviour (such as a future spatial index)
can be attached. Geometry columns returned by [`read`](@ref) are wrapped in a `GeometryVector`.

# Example
```jldoctest
julia> gv = GeoDataFrames.GeometryVector([GeoInterface.Point(1.0, 2.0), GeoInterface.Point(3.0, 4.0)]);

julia> length(gv)
2
```
"""
struct GeometryVector{T} <: AbstractArray{T, 1}
A::Vector{T}
Expand Down
Loading
Loading