Decide Polygon Overlap Handling Strategy

Status: Implemented

Assignee: Alfons Mueller

Problem

We must decide whether PermaplanT should allow or prohibit overlapping polygons in brushing layers (shade, hydrology, soil texture, zones) before the 1.0 release.

Currently, overlapping polygons are possible in all area layers because no validation prevents overlaps during area creation in the backend.
Furthermore, the semantic of what the overlapping means is not clearly defined. This leads to ambiguous, contradictory, and semantically invalid spatial data.

Example:

  • User brushes “permanent shade” in shade layer → later adds “full sun” overlapping it
  • Now the system contains contradictory information for the same location
  • Current implementation: permanent shade wins, but full sun is seen by the user.
  • So heatmap logic calculation differs from what would expect by looking at the frontend canvas.

Other layers (hydrology, zones, soil texture) behave similarly

This ambiguity leads to unpredictable behavior for users and inconsistent results for computations such as heatmap generation.

Migration Feasibility Analysis

Can we write a safe migration?

Technical feasibility: YES - PostGIS provides functions:

  • ST_Overlaps(geom1, geom2): Detect overlaps
  • ST_Difference(geom1, geom2): Subtract one polygon from another
  • ST_Union(geom1, geom2): Merge polygons
  • ST_Intersection(geom1, geom2): Get overlapping area

Data loss risk: MEDIUM

  • If we apply "last created wins", older polygons lose area
  • If we apply "first created wins", newer user intent is lost
  • Without user intervention, any automated resolution may violate user intent
  • Temporal ordering (created_at) is sufficient to decide priority.

Example migration approach (last created wins):

-- For each overlapping pair, subtract older polygon from newer
UPDATE shadings s1
SET geometry = ST_Difference(s1.geometry, s2.geometry)
FROM shadings s2
WHERE s1.layer_id = s2.layer_id
  AND s1.id != s2.id
  AND ST_Overlaps(s1.geometry, s2.geometry)
  AND s1.created_at < s2.created_at;

Migration complexity: ~8h for implementation and testing across all area tables

Why are overlapping areas correctly arranged (last drawn always on top)?

We don't have ordering logic in the frontend, elements are simply arranged based on the rendering order.
For example:
In the shading layer, we are mapping over an array of shades received from backend.
In terms of rendering order, this means that element with index=0 is rendered first, then element with index=1, then with index=3 and so on.
When adding two shading objects: first - no shade, then light shade.
The objects are returned from the backend exactly in this order and therefore drawn "correctly" over each other.
Thus persisting the shadings preserves the order they where drawn in.

Constraints

  1. Existing data must remain valid and ideally not lose user intent.
  2. Decision must be consistent with previous architecture decisions, e.g.,
    • polygon_calculations.md:52: intention to compute “areas over areas” in backend.
  3. Backend and frontend must remain performant even with 15 overlapping areas.
  4. Real-time collaborative editing should not create race conditions.
  5. Migration feasibility: If overlaps are allowed now, it must be possible to resolve them later without unacceptable data loss.
  6. UNDO functionality must work with solution.

Assumptions

  1. Users generally expect mutually exclusive semantics for shading, hydrology, soil texture, and zones.
  2. Overlaps, if allowed temporarily, will grow in amount over time.
  3. Temporal ordering (created_at) should reflect intent, but might not be the desired solution for all users.
  4. Users use add/removal date correctly, they don't place all their areas with the same start/end time if they intended it to have different start/end time.

Solutions

Alternative A: Prohibit Overlaps Before 1.0 (Proactive)

Implement backend validation for new polygons:

  • Detect overlaps when creating/updating polygons
  • Apply one of:
    • A1: New polygon overrides old (subtract old area)
    • A2: Disallow creation if overlap occurs
    • A3: Let user choose via UI dialog
    • A4: Merge/union polygons of same type

Pros

  • Prevents corrupted data from entering the system
  • Guarantees clear semantics and consistent heatmap results
  • No destructive migration later
  • Aligned with future backend polygon-calculation architecture

Cons

  • Higher implementation cost pre-1.0
  • Requires UI and backend work (~16-24h)
  • Might require redesign of brush tool UX

Alternative B: Allow Overlaps Now, Resolve with Migration Later (Reactive)

Allow overlaps today, migrate after release. Only add created_at fields to areas right now.

A migration might use:

  • “Last created wins”
  • “First created wins”
  • PostGIS tools (ST_Difference, ST_Union, ST_Overlaps)

Pros

  • Faster path to 1.0
  • Might be able to choose migration strategy based on user data

Cons

  • Risk of significant data loss during migration if users have created extensive overlapping areas
  • Ambiguous heatmap results in production
  • May confuse users about application behavior
  • Harder to decide resolution strategy without user input

Option C: Document as Known Limitation

Explicitly document that overlaps are undefined behavior:

  1. Add warning in UI/docs
  2. Leave resolution to user discretion
  3. Handle overlaps in heatmap calculation (e.g., "last polygon in query order wins")

Pros:

  • Minimal implementation effort
  • Users aware of limitation

Cons:

  • Unprofessional for 1.0 release
  • Data quality issues
  • Undefined behavior in heatmap

Option D: Communicate Last Placed Area Winning Over Older Areas, Base Heatmap on this Ordering

Explicitly document how last placed area wins over the ones below it:

  1. Add created_at to areas
  2. Handle overlaps in heatmap calculation (e.g., "last polygon in query order wins")

A prototype for the updated heatmap calculation has already been created by Christoph Schreiner in an MR

Pros:

  • Minimal implementation effort
  • Users aware of area ordering
  • Introducing audit trail fields makes it easier to migrate to Option A later on

Cons:

  • Data quality issues

Decision

Option D: Communicate Last Placed Area Winning Over Older Areas, Base Heatmap on this Ordering

Rationale

Choosing Option D gives us a decent solution to implement before 1.0 release. It also leaves open the possibility of migrating to a different (polygon cutting) solution down the line.

Implications

Explicitly document how last placed area wins over the ones below it:

  1. Add information in UI (e.g., "If 2 or more types of shade are placed on top of each other, only the one on the very top is considered.")
  2. Add created_at to areas
  3. Handle overlaps in heatmap calculation (e.g., "last polygon in query order wins")
  • polygon_calculations.md prior decision to handle polygons and overlaps in backend.

Notes

  • Discussions: meetings on 2024-10-14, 2024-10-21, 2024-10-28, 2024-11-04, 2024-11-11 regarding overlap issues.
  • Backend DB context: PostGIS polygons created in migration 2024-05-31-120000_heatmap_and_shadings/up.sql.
  • Related code:
    backend/src/service/areas.rs,
    backend/src/model/dto/areas.rs,
    backend/src/schema.rs.