Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New (breaking) Traits based release #33

Merged
merged 46 commits into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0ce3aaa
Moved from GeoInterfaceRFC.jl
evetion Aug 24, 2020
7f7fa43
Renamed all RFC occurences.
evetion Aug 26, 2020
668dc52
Small update.
evetion Mar 25, 2022
f7fc274
Merge branch 'master' into v1-traits
evetion Mar 25, 2022
d47dd88
Fix review comments.
evetion Mar 25, 2022
5ce639b
Use `isgeometry` as a first check.
evetion Mar 25, 2022
47281c4
Updated integrations.
evetion Mar 25, 2022
27dc9fe
Improved documentation.
evetion Apr 1, 2022
b727c07
Updated docs. Fixed bugs.
evetion Apr 4, 2022
4637713
Update src/defaults.jl
evetion Apr 8, 2022
45dd59a
Update docs/src/guides/developer.md
evetion Apr 8, 2022
2ae9581
Changes based on ArchGDAL implementation.
evetion Apr 10, 2022
b252cd1
Fixed tests.
evetion Apr 10, 2022
33df32f
Reverted Type traits. Suffixed Trait to all types.
evetion Apr 15, 2022
49a4d11
Add Julia Computing as contributor. Fix `isempty` and add `convert` m…
evetion Apr 23, 2022
7af21e4
Add `asbinary` and `astext` (wkb and wkt) methods.
evetion Apr 25, 2022
1ba01f5
Added iterators. Added feature interface. Updated documentation based…
evetion May 3, 2022
e6feb6b
Removed unused import.
evetion May 3, 2022
0df59c9
add getring function to the interface
rafaqz May 1, 2022
497abd3
add getpoint iterators
rafaqz May 3, 2022
40c4bcc
bugfix defaults
rafaqz May 3, 2022
b15d130
fix functions and standardise iterator docstrings
rafaqz May 3, 2022
c537100
bugfix
rafaqz May 3, 2022
49644d7
Merge pull request #47 from JuliaGeo/rs/getrings
evetion May 3, 2022
9ab7e2c
Fixed doc reference. Removed duplicate getring function.
evetion May 8, 2022
7ee9ace
Added doctests. Expanded the documentation.
evetion May 8, 2022
1805746
Fixed bug in coordinates.
evetion May 8, 2022
567683b
Fix bug in x, y, z, m lookup in getcoord.
evetion May 9, 2022
ddee198
Improved performance of coordinate name lookup.
evetion May 9, 2022
6303188
standardise trait arg passing
rafaqz May 9, 2022
66b68e4
Merge pull request #48 from JuliaGeo/rs/standardise_traitargs
evetion May 9, 2022
3305aef
Completed subtrait method.
evetion May 10, 2022
19946d7
Fixed doctest.
evetion May 11, 2022
fa4a26d
Fixed bugs in nested iterator. Expanded tests and enabled code coverage.
evetion May 12, 2022
4630f13
rename defaults.jl to fallbacks.jl
visr May 15, 2022
2c00af7
editing
visr May 15, 2022
69b38a7
replace argument _ with geom
visr May 15, 2022
7bef237
drop module name for exported functions
visr May 15, 2022
d1d9589
fix tests
visr May 15, 2022
f98a139
export the traits
visr May 15, 2022
cfadd5e
Merge pull request #50 from JuliaGeo/export-traits
evetion May 15, 2022
ccd276a
Update .github/workflows/CI.yml
evetion May 15, 2022
8468fa9
Merge pull request #49 from JuliaGeo/check
evetion May 15, 2022
2cc0e07
Expanded tests.
evetion May 15, 2022
a22db09
Documented conversion interface.
evetion May 16, 2022
b5f5675
Expanded test coverage more.
evetion May 16, 2022
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
26 changes: 19 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
# Documentation: http://docs.travis-ci.com/user/languages/julia
language: julia
os:
- linux
- osx
notifications:
email: false
julia:
- 1.0
- 1
- 1.5
evetion marked this conversation as resolved.
Show resolved Hide resolved
- nightly
matrix:
os:
- linux
- osx
- windows
arch:
- x64
- x86
cache:
directories:
- ~/.julia/artifacts
jobs:
fast_finish: true
allow_failures:
- julia: nightly
notifications:
email: false
exclude:
- arch: x86
os: osx
4 changes: 1 addition & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
name = "GeoInterface"
uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
license = "MIT"
version = "0.5.4"
version = "1.0"

[deps]
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"

[compat]
RecipesBase = "0.6, 0.7, 0.8, 1.0"
julia = "1"

[extras]
Expand Down
278 changes: 202 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,202 @@
# GeoInterface.jl

A Julia Protocol for Geospatial Data

## Motivation
To support operations or visualization of multiple (but similar) implementations of vector data (across `GeoJSON.jl`, `LibGEOS.jl`, etc). As a starting point, it will follow the [GEO interface](https://gist.github.com/sgillies/2217756) [1] in Python (which in turn borrows its design from the [GeoJSON specification](http://geojson.org/) [2]).

## GEO Interface

### AbstractPosition
A position can be thought of as a tuple of numbers. There must be at least two elements, and may be more. The order of elements must follow `x`, `y`, `z` order (e.g. easting, northing, altitude for coordinates in a projected coordinate reference system, or longitude, latitude, altitude for coordinates in a geographic coordinate reference system). It requires the following methods:

- `xcoord(::AbstractPosition)::Float64`
- `ycoord(::AbstractPosition)::Float64`
- `zcoord(::AbstractPosition)::Float64`
- `hasz(::AbstractPosition)::Bool` (`false` by default)

Remark: Although the specification allows the representation of up to 3 dimensions, not all algorithms support require all 3 dimensions. Also, if you are working with an arbitrary `obj::AbstractPosition`, you should call `hasz(obj)` before calling `zcoord(obj)`.

### AbstractGeometry
Represents vector geometry, and encompasses the following abstract types: `AbstractPoint, AbstractMultiPoint, AbstractLineString, AbstractMultiLineString, AbstractMultiPolygon, AbstractPolygon`. It requires the `coordinates` method, where

- `coordinates(::AbstractPoint)` returns a single position.
- `coordinates(::AbstractMultiPoint)` returns a vector of positions.
- `coordinates(::AbstractLineString)` returns a vector of positions.
- `coordinates(::AbstractMultiLineString)` returns a vector of linestrings.
- `coordinates(::AbstractPolygon)` returns a vector of linestrings.
- `coordinates(::AbstractMultiPolygon)` returns a vector of polygons.

### AbstractGeometryCollection
Represents a collection of geometries, and requires the `geometries` method, which returns a vector of geometries. Is also a subtype of `AbstractGeometry`.

### AbstractFeature
Represents a geometry with additional attributes, and requires the following methods

- `geometry(::AbstractFeature)::AbstractGeometry` returns the corresponding geometry
- `properties(::AbstractFeature)::Dict{AbstractString,Any}` returns a dictionary of the properties

Optionally, you can also provide the following methods

- `bbox(::AbstractFeature)::AbstractGeometry` returns the bounding box for that feature
- `crs(::AbstractFeature)::Dict{AbstractString,Any}` returns the coordinate reference system

## Geospatial Geometries
If you don't need to provide your own user types, GeoInterface also provides a set of geometries (below), which implements the GEO Interface:

- `CRS`
- `Position`
- `Geometry <: AbstractGeometry`
- `Point <: AbstractPoint <: AbstractGeometry`
- `MultiPoint <: AbstractMultiPoint <: AbstractGeometry`
- `LineString <: AbstractLineString <: AbstractGeometry`
- `MultiLineString <: AbstractMultiLineString <: AbstractGeometry`
- `Polygon <: AbstractPolygon <: AbstractGeometry`
- `MultiPolygon <: AbstractMultiPolygon <: AbstractGeometry`
- `GeometryCollection <: AbstractGeometryCollection <: AbstractGeometry`
- `Feature <: AbstractFeature`
- `FeatureCollection <: AbstractFeatureCollection`

## Remarks

Conceptually,

- an `::AbstractGeometryCollection` maps to a `DataArray{::AbstractGeometry}`, and
- an `::AbstractFeatureCollection` maps to a `DataFrame`, where each row is an `AbstractFeature`

The design of the types in GeoInterface differs from the GeoJSON specification in the following ways:

- Julia Geometries do not provide a `bbox` and `crs` method. If you wish to provide a `bbox` or `crs` attribute, wrap the geometry into a `Feature` or `FeatureCollection`.
- Features do not have special fields for `id`, `bbox`, and `crs`. These are to be provided (or found) in the `properties` field, under the keys `featureid`, `bbox`, and `crs` respectively (if they exist).

## References

[1]: A Python Protocol for Geospatial Data ([gist](https://gist.github.com/sgillies/2217756))

[2]: GeoJSON Specification ([website](http://geojson.org/))
# GeoInterface
An interface for geospatial vector data in Julia

This Package describe a set of traits based on the [Simple Features standard (SF)](https://www.opengeospatial.org/standards/sfa)
for geospatial vector data, including the SQL/MM extension with support for circular geometry.

Packages which support the GeoInterfaceRFC.jl interface can be found in [INTEGRATIONS.md](INTEGRATIONS.md).

## Changes with respect to SF
While we try to adhere to SF, there are changes and extensions to make it more Julian.

### Function names
All function names are without the `ST_` prefix and are lowercased. In some cases the names have changed as well, to be inline with common Julia functions. `NumX` becomes `nx` and `Xn` becomes `getX`:
```julia
GeometryType -> geomtype
NumGeometries -> ngeom
GeometryN -> getgeom
NumPatches -> npatch
# etc
```

We also simplified the dimension functions. From the three original (`dimension`, `coordinateDimension`, `spatialDimension`) there's now only the coordinate dimension, so not to overlap with the Julia `ndims`.
```julia
coordinateDimension -> ncoords
```

We've generalized the some functions:
```julia
SRID -> crs
envelope -> extent
```

And added a helper method to clarify the naming of coordinates.
```julia
coordnames = (:X, :Y, :Z, :M)
```

### Coverage
Not all SF functions are implemented, either as a possibly slower fallback or empty descriptor or not at all. The following SF functions are not (yet) available.

```julia
dimension
spatialDimension
asText
asBinary
is3D
isMeasured
boundary

locateAlong
locateBetween

distance
buffer
convexHull

```
While the following functions have no implementation:
```julia
equals
disjoint
touches
within
overlaps
crosses
intersects
contains
relate

intersection
union
difference
symdifference
```


## Implementation
GeoInterface provides a traits interface, not unlike Tables.jl, by

(a) a set of functions:
```julia
geomtype(geom)
ncoord(geom)
ngeom(geom)
getgeom(geom::geomtype, i)
...
```
(b) a set of types for dispatching on said functions.
The types tells GeoInterface how to interpret the input object inside a GeoInterface function.

```julia
abstract Geometry
Point <: AbstractPoint <: AbstractGeometry
MultiPoint <: AbstractMultiPointGeometry <:AbstractGeometryCollection <: AbstractGeometry
...
```

### For developers looking to implement the interface
GeoInterface requires five functions to be defined for a given geom:

```julia
GeoInterface.geomtype(geom::geomtype)::DataType = GeoInterface.X()
GeoInterface.ncoord(geomtype(geom), geom::geomtype)::Integer
GeoInterface.getcoord(geomtype(geom), geom::geomtype, i)::Real # only for Points
GeoInterface.ngeom(geomtype(geom), geom::geomtype)::Integer
GeoInterface.getgeom(geomtype(geom), geom::geomtype, i) # geomtype -> GeoInterface.Y
```
Where the `getgeom` could be an iterator (without the i) as well. It will return a new geom with the correct `geomtype`. The `ngeom` and `getgeom` are aliases for their geom specific counterparts, such as `npoints` and `getpoint` for LineStrings.

There are also optional generic methods that could help or speed up operations:
```julia
GeoInterface.crs(geom)::Union{Missing, GeoFormatTypes.CoordinateReferenceSystemFormat}
GeoInterface.extent(geom) # geomtype -> GeoInterface.Rectangle
Copy link
Member

@rafaqz rafaqz Oct 15, 2021

Choose a reason for hiding this comment

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

extent needs to be implemented.

Could it hold/be a NamedTuple ? Like extent(obj) = Extent((X=(xmin, xmax), Y=(ymin, ymax))) etc for how many dimensions there are, using the :X, :Y, :Z, :M names from the interface.

I find this organisation is easier to use than the xmin, ymin, xmax, ymax style bbox usually uses.

(but somehow I missed that its meant to return a Rectangle. I guess that makes sense in a different way.)

```

And lastly, there are many other optional functions for each specific geometry. GeoInterface provides fallback implementations based on the generic functions above, but these are not optimized. These are detailed in the next chapter.

### Examples

A `geom::geomtype` with "Point"-like traits implements
```julia
GeoInterface.geomtype(geom::geomtype)::DataType = GeoInterface.Point()
GeoInterface.ncoord(::GeoInterface.Point, geom::geomtype)::Integer
GeoInterface.getcoord(::GeoInterface.Point, geom::geomtype, i)::Real

# Defaults
GeoInterface.ngeom(::GeoInterface.Point, geom)::Integer = 0
GeoInterface.getgeom(::GeoInterface.Point, geom::geomtype, i) = nothing
```

A `geom::geomtype` with "LineString"-like traits implements the following methods:
```julia
GeoInterface.geomtype(geom::geomtype)::DataType = GeoInterface.LineString()
GeoInterface.ncoord(::GeoInterface.LineString, geom::geomtype)::Integer

# These alias for npoint and getpoint
GeoInterface.ngeom(::GeoInterface.LineString, geom::geomtype)::Integer
GeoInterface.getgeom(::GeoInterface.LineString, geom::geomtype, i) # of geomtype Point

# Optional
GeoInterface.isclosed(::GeoInterface.LineString, geom::geomtype)::Bool
GeoInterface.issimple(::GeoInterface.LineString, geom::geomtype)::Bool
GeoInterface.length(::GeoInterface.LineString, geom::geomtype)::Real
```
A `geom::geomtype` with "Polygon"-like traits can implement the following methods:
```julia
GeoInterface.geomtype(geom::geomtype)::DataType = GeoInterface.Polygon()
GeoInterface.ncoord(::GeoInterface.Polygon, geom::geomtype)::Integer

# These alias for nring and getring
GeoInterface.ngeom(::GeoInterface.Polygon, geom::geomtype)::Integer
GeoInterface.getgeom(::GeoInterface.Polygon, geom::geomtype, i)::"LineString"

# Optional
GeoInterface.area(::GeoInterface.Polygon, geom::geomtype)::Real
GeoInterface.centroid(::GeoInterface.Polygon, geom::geomtype)::"Point"
GeoInterface.pointonsurface(::GeoInterface.Polygon, geom::geomtype)::"Point"
GeoInterface.boundary(::GeoInterface.Polygon, geom::geomtype)::"LineString"

```
A `geom::geomtype` with "GeometryCollection"-like traits has to implement the following methods:
```julia
GeoInterface.geomtype(geom::geomtype) = GeoInterface.GeometryCollection()
GeoInterface.ncoord(::GeoInterface.GeometryCollection, geom::geomtype)::Integer
GeoInterface.ngeom(::GeoInterface.GeometryCollection, geom::geomtype)::Integer
GeoInterface.getgeom(::GeoInterface.GeometryCollection,geom::geomtypem, i)::"Geometry"
```
A `geom::geomtype` with "MultiPoint"-like traits has to implement the following methods:
```julia
GeoInterface.geomtype(geom::geomtype) = GeoInterface.MultiPoint()
GeoInterface.ncoord(::GeoInterface.MultiPoint, geom::geomtype)::Integer

# These alias for npoint and getpoint
GeoInterface.ngeom(::GeoInterface.MultiPoint, geom::geomtype)::Integer
GeoInterface.getgeom(::GeoInterface.MultiPoint, geom::geomtype, i)::"Point"
```
A `geom::geomtype` with "MultiLineString"-like traits has to implement the following methods:
```julia
GeoInterface.geomtype(geom::geomtype) = GeoInterface.MultiLineString()
GeoInterface.ncoord(::GeoInterface.MultiLineString, geom::geomtype)::Integer

# These alias for nlinestring and getlinestring
GeoInterface.ngeom(::GeoInterface.MultiLineString, geom::geomtype)::Integer
GeoInterface.getgeom(::GeoInterface.MultiLineString,geom::geomtypem, i)::"LineString"
```
A `geom::geomtype` with "MultiPolygon"-like traits has to implement the following methods:
```julia
GeoInterface.geomtype(geom::geomtype) = GeoInterface.MultiPolygon()
GeoInterface.ncoord(::GeoInterface.MultiPolygon, geom::geomtype)::Integer

# These alias for npolygon and getpolygon
GeoInterface.ngeom(::GeoInterface.MultiPolygon, geom::geomtype)::Integer
GeoInterface.getgeom(::GeoInterface.MultiPolygon, geom::geomtype, i)::"Polygon"
```


### Testing the interface
GeoInterface provides a Testsuite for a geom type to check whether all functions that have been implemented also work as expected.

```julia
GeoInterface.test_interface_for_geom(geom)
```
Loading