Skip to content

Commit d03fa69

Browse files
authored
add an AGENTS.md for the codebase (#358)
2 parents fc8e36d + 88c6d41 commit d03fa69

File tree

2 files changed

+314
-0
lines changed

2 files changed

+314
-0
lines changed

AGENTS.md

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Development Commands
6+
7+
### Testing
8+
Run all tests:
9+
```bash
10+
julia --project=. -e 'using Pkg; Pkg.test()'
11+
```
12+
13+
Note: There is currently no way to run a single test file in isolation.
14+
15+
### Git Commit Style
16+
Commit messages in this repository follow a simple, descriptive style:
17+
18+
- **Use imperative mood**: "Fix bug" not "Fixed bug" or "Fixes bug"
19+
- **Start with a capital letter**: "Add feature" not "add feature"
20+
- **Be concise but descriptive**: Explain what changed, not why (unless non-obvious)
21+
- **No trailing periods**: Commit messages don't end with a period
22+
- **Use backticks for code**: Reference functions/types with backticks like `smooth` or `TraitTarget`
23+
- **No conventional commit prefixes**: Don't use "feat:", "fix:", "docs:", etc.
24+
25+
Examples from the project:
26+
```
27+
Fix type constraint in _smooth function
28+
Add a `smooth` function
29+
Refactor tests to be a bit easier to parse
30+
Tree based acceleration for polygon clipping / boolean ops
31+
Bump version from 0.1.30 to 0.1.31
32+
```
33+
34+
## High-Level Architecture
35+
36+
### Monorepo Structure
37+
GeometryOps uses a monorepo structure with GeometryOpsCore as a subpackage:
38+
- **GeometryOpsCore/**: Core abstractions, types, and primitive functions (`apply`, `applyreduce`, `flatten`, etc.)
39+
- **src/**: Main package implementation
40+
- **ext/**: Package extensions for optional dependencies (LibGEOS, Proj, TGGeometry, DataFrames, FlexiJoins)
41+
42+
### Core Abstractions
43+
44+
**GeoInterface Integration**: All functions work with any GeoInterface-compatible geometry. Dispatch is based on GeoInterface traits (PointTrait, LineStringTrait, PolygonTrait, etc.), not concrete types.
45+
46+
**Manifold System**: Operations can be performed on different manifolds:
47+
- `Planar()`: Euclidean/Cartesian coordinates (default)
48+
- `Spherical()`: Spherical coordinates on a unit sphere
49+
- `Geodesic()`: Geodesic calculations on Earth (requires Proj extension)
50+
- `AutoManifold()`: Automatically select appropriate manifold
51+
52+
Functions typically accept a manifold as the first argument:
53+
```julia
54+
area(Planar(), polygon)
55+
area(Spherical(), polygon)
56+
```
57+
58+
**Apply Framework**: The `apply` and `applyreduce` functions from GeometryOpsCore are the workhorses for geometry operations:
59+
- `apply`: Applies a function to geometries matching a target trait, then reconstructs the geometry
60+
- `applyreduce`: Applies a function and reduces the results (e.g., sum, min, max)
61+
- `TraitTarget`: Specifies which geometry traits to target (e.g., `TraitTarget{GI.PointTrait}()`)
62+
63+
Example pattern:
64+
```julia
65+
applyreduce(WithTrait((trait, g) -> _area(T, trait, g)), +, _AREA_TARGETS, geom; threaded, init=zero(T))
66+
```
67+
68+
### Code Organization Principles
69+
70+
1. **Literate Programming**: Source files use literate programming with documentation and examples at the top, followed by implementation. Examples should include visual plots using Makie/CairoMakie when appropriate.
71+
72+
2. **One File, One Job**: Each file should handle one semantic concept (e.g., `area.jl`, `distance.jl`, `centroid.jl`). Common utilities can be extracted to separate files.
73+
74+
3. **Public vs Internal**:
75+
- Public functions: Exported, documented, promise API stability
76+
- Internal functions: Prefixed with `_`, not exported, may have comments instead of docstrings
77+
78+
4. **File Structure Pattern** (see `src/methods/area.jl` or `src/methods/distance.jl`):
79+
```julia
80+
# # Title
81+
export function_name
82+
83+
#=
84+
## What is [concept]?
85+
[Explanation with examples, plots]
86+
87+
## Implementation
88+
[Implementation notes]
89+
=#
90+
91+
# Public API with docstring
92+
function_name(geom, args...) = ...
93+
94+
# Internal implementation functions
95+
_function_name(...) = ...
96+
```
97+
98+
### Directory Structure
99+
100+
- **src/methods/**: Geometric operations and predicates
101+
- Basic: `area.jl`, `centroid.jl`, `distance.jl`, `perimeter.jl`, `angles.jl`
102+
- Spatial relations: `geom_relations/` (contains, intersects, within, etc.)
103+
- Clipping: `clipping/` (intersection, union, difference, cut, coverage)
104+
- Other: `barycentric.jl`, `buffer.jl`, `convex_hull.jl`, `orientation.jl`, `polygonize.jl`
105+
106+
- **src/transformations/**: Geometry transformations
107+
- `simplify.jl`, `segmentize.jl`, `smooth.jl`, `flip.jl`, `transform.jl`
108+
- `reproject.jl`: Coordinate reference system transformations
109+
- `correction/`: Geometry correction utilities
110+
111+
- **src/utils/**: Utility modules
112+
- `LoopStateMachine/`: State machine for polygon processing
113+
- `SpatialTreeInterface/`: Spatial indexing (STRtree)
114+
- `UnitSpherical/`: Spherical geometry utilities
115+
- `NaturalIndexing.jl`: Natural indexing utilities
116+
117+
- **test/**: Mirror structure of src/ with corresponding test files
118+
119+
## Writing a New Algorithm
120+
121+
### Step-by-Step Pattern
122+
123+
1. **Choose the right location**:
124+
- Methods (area, distance, etc.) go in `src/methods/`
125+
- Transformations (simplify, flip, etc.) go in `src/transformations/`
126+
127+
2. **Create the file with literate documentation**:
128+
```julia
129+
# # Algorithm Name
130+
export algorithm_name
131+
132+
#=
133+
## What is [algorithm]?
134+
[Clear explanation with visual examples using Makie]
135+
136+
```@example demo
137+
import GeometryOps as GO
138+
import GeoInterface as GI
139+
using CairoMakie
140+
141+
# Create example geometry and demonstrate usage
142+
polygon = GI.Polygon([[(0,0), (1,0), (1,1), (0,1), (0,0)]])
143+
result = GO.algorithm_name(polygon)
144+
```
145+
146+
## Implementation
147+
[Notes about the approach, algorithm complexity, special cases]
148+
=#
149+
```
150+
151+
3. **Define target traits** (if using apply/applyreduce):
152+
```julia
153+
const _ALGORITHM_TARGETS = TraitTarget{Union{GI.PolygonTrait,GI.MultiPolygonTrait}}()
154+
```
155+
156+
4. **Implement public API** with manifold support:
157+
```julia
158+
"""
159+
algorithm_name(geom, [T = Float64]; kwargs...)
160+
161+
[Docstring with description, parameters, return value]
162+
"""
163+
function algorithm_name(geom, ::Type{T} = Float64; threaded=false, kwargs...) where T
164+
algorithm_name(Planar(), geom, T; threaded, kwargs...)
165+
end
166+
167+
# Manifold-specific implementations
168+
function algorithm_name(m::Planar, geom, ::Type{T} = Float64; threaded=false, kwargs...) where T
169+
# Use apply or applyreduce pattern
170+
applyreduce(WithTrait((trait, g) -> _algorithm(T, trait, g)), +, _ALGORITHM_TARGETS, geom; threaded, init=zero(T), kwargs...)
171+
end
172+
173+
function algorithm_name(m::Spherical, geom, ::Type{T} = Float64; threaded=false, kwargs...) where T
174+
# Spherical implementation if applicable
175+
end
176+
```
177+
178+
5. **Implement internal functions** (trait-dispatched):
179+
```julia
180+
# Dispatch on different geometry traits
181+
_algorithm(::Type{T}, ::GI.PointTrait, point) where T = # Point implementation
182+
_algorithm(::Type{T}, ::GI.LineStringTrait, linestring) where T = # LineString implementation
183+
_algorithm(::Type{T}, ::GI.PolygonTrait, polygon) where T = # Polygon implementation
184+
```
185+
186+
6. **Add to main module**: Include the file in `src/GeometryOps.jl`:
187+
```julia
188+
include("methods/algorithm_name.jl")
189+
```
190+
191+
7. **Write tests**: Create `test/methods/algorithm_name.jl` mirroring the source structure:
192+
```julia
193+
using Test
194+
using GeometryOps
195+
import GeoInterface as GI
196+
197+
@testset "Algorithm Name" begin
198+
@testset "Point" begin
199+
# Test with points
200+
end
201+
202+
@testset "Polygon" begin
203+
# Test with polygons
204+
end
205+
206+
@testset "Edge cases" begin
207+
# Empty geometries, degenerate cases, etc.
208+
end
209+
end
210+
```
211+
212+
8. **Register test**: Add to `test/runtests.jl`:
213+
```julia
214+
@safetestset "Algorithm Name" begin include("methods/algorithm_name.jl") end
215+
```
216+
217+
## Interfacing with Input
218+
219+
### GeoInterface Pattern
220+
All functions accept any GeoInterface-compatible geometry. Always use GeoInterface accessors:
221+
```julia
222+
GI.trait(geom) # Get geometry trait
223+
GI.npoint(geom) # Number of points
224+
GI.getpoint(geom, i) # Get point at index i
225+
GI.getpoint(geom) # Iterator over points
226+
GI.x(point) # X coordinate
227+
GI.y(point) # Y coordinate
228+
GI.z(point) # Z coordinate (if exists)
229+
GI.getexterior(poly) # Exterior ring
230+
GI.gethole(poly) # Iterator over holes
231+
GI.isempty(geom) # Check if empty
232+
```
233+
234+
### Type Flexibility
235+
Functions should accept a type parameter `T` (defaulting to `Float64`) for numeric calculations:
236+
```julia
237+
function my_function(geom, ::Type{T} = Float64) where T <: AbstractFloat
238+
result = zero(T)
239+
# ... computation using type T
240+
return result
241+
end
242+
```
243+
244+
### Threading Support
245+
Most operations support threading via `threaded=false` keyword. Threading happens at the highest level (over arrays, feature collections, or multi-geometries):
246+
```julia
247+
applyreduce(f, op, targets, geom; threaded=true) # Enable threading
248+
```
249+
250+
### Extension-Based Optional Features
251+
Optional dependencies are loaded via extensions:
252+
- **GEOS**: `GEOS()` algorithm for calling LibGEOS functions
253+
- **Proj**: `PROJ()` algorithm for reprojection and geodesic operations
254+
- **TGGeometry**: `TG()` algorithm for fast C-based predicates
255+
- **DataFrames**: `apply` works on DataFrame columns
256+
- **FlexiJoins**: Spatial join operations
257+
258+
Use algorithm types to dispatch to these implementations. **The algorithm is always the first argument:**
259+
```julia
260+
# Use GEOS implementation (algorithm comes first)
261+
buffer(GEOS(), geom, 10.0)
262+
263+
# Use native Julia implementation (default)
264+
buffer(geom, 10.0)
265+
```
266+
267+
The `Algorithm` type and its subtypes are defined in `GeometryOpsCore/src/types/algorithm.jl`.
268+
269+
### Working with Tables
270+
`apply` and `applyreduce` work with any Tables.jl-compatible table (DataFrames, etc.):
271+
```julia
272+
# Apply operation to geometry column in a table
273+
result = apply(PointTrait(), df.geometry) do point
274+
(GI.y(point), GI.x(point)) # Flip coordinates
275+
end
276+
```
277+
278+
## Common Patterns
279+
280+
### Error Handling for Missing Extensions
281+
Functions requiring extensions should provide helpful error hints:
282+
```julia
283+
function __init__()
284+
Base.Experimental.register_error_hint(_reproject_error_hinter, MethodError)
285+
Base.Experimental.register_error_hint(_geodesic_segments_error_hinter, MethodError)
286+
Base.Experimental.register_error_hint(_buffer_error_hinter, MethodError)
287+
end
288+
```
289+
290+
### TraitTarget Usage
291+
Use `TraitTarget` to specify which geometry types your function operates on:
292+
```julia
293+
# Single trait
294+
const _TARGETS = TraitTarget{GI.PointTrait}()
295+
296+
# Union of traits
297+
const _TARGETS = TraitTarget{Union{GI.PolygonTrait,GI.MultiPolygonTrait}}()
298+
299+
# More complex unions
300+
const _TARGETS = TraitTarget{Union{GI.PointTrait,GI.LineStringTrait,GI.LinearRingTrait}}()
301+
```
302+
303+
### WithTrait Pattern
304+
Use `WithTrait` wrapper when you need both the trait and geometry in your function:
305+
```julia
306+
applyreduce(WithTrait((trait, geom) -> _my_function(trait, geom)), +, targets, input)
307+
```
308+
309+
### Handling Empty Geometries
310+
Always check for empty geometries when appropriate:
311+
```julia
312+
GI.isempty(geom) && return zero(T) # Or appropriate default value
313+
```

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md

0 commit comments

Comments
 (0)