Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
dc8515b
feat(farm_base): add farm.mixin AbstractModel for color/notes/mail.th…
dnplkndll May 17, 2026
eb65779
feat(farm_crop): catalog of farm crops with 18 seeded entries
dnplkndll May 17, 2026
09c237b
feat(farm_field): fields/plots with multi-company isolation
dnplkndll May 17, 2026
7c39af9
feat(farm_planting): planting events with state machine + multi-company
dnplkndll May 17, 2026
3f30321
feat(farm_harvest): harvest events with auto-fill from planting
dnplkndll May 17, 2026
cf00cfa
fix: kanban templates for Odoo 19 Card system
dnplkndll May 17, 2026
9781760
feat(farm_field_geo): PostGIS polygon + auto-computed acreage
dnplkndll May 17, 2026
1371106
refactor(farm_field_geo): self-review fixes
dnplkndll May 17, 2026
89d8d14
ci(farm_field_geo): pin OCA/geospatial unreleased deps via test-requi…
dnplkndll May 18, 2026
2d05567
fix(farm_quickbooks_io): drop deprecated target=inline on settings ac…
dnplkndll May 18, 2026
ed64be1
ci: trigger workflow on feat/farm-field-geo after retarget to 19.0
dnplkndll May 18, 2026
c0ed0f2
Merge remote-tracking branch 'origin/19.0' into feat/farm-field-geo
dnplkndll May 18, 2026
75dc514
fix(farm_field_geo): development_status=Alpha matches farm_field
dnplkndll May 18, 2026
0480572
ci: swap postgres:13 → postgis/postgis:13-3.4-alpine in tests service
dnplkndll May 18, 2026
fb5d9d8
fix(farm_field_geo): drop bogus code field from test crop fixture
dnplkndll May 18, 2026
7e17b0f
fix(farm_field_geo): use shapely.Polygon instead of EWKT strings in t…
dnplkndll May 18, 2026
a7485fd
fix(farm_field_geo): use pyproj+shapely.ops.transform for reprojection
dnplkndll May 18, 2026
3232f87
fix(farm_field_geo): declare pyproj + shapely as external deps
dnplkndll May 18, 2026
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
7 changes: 6 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ jobs:
name: test with OCB
services:
postgres:
image: postgres:13
# postgis/postgis carries the same Postgres major version as the
# upstream OCA template (13) but bundles PostGIS so base_geoengine
# (and any farm.field geometry module) can `CREATE EXTENSION postgis`.
# Drop back to `postgres:13` when farm-pack no longer ships geo
# modules that need PostGIS.
image: postgis/postgis:13-3.4-alpine
env:
POSTGRES_USER: odoo
POSTGRES_PASSWORD: odoo
Expand Down
125 changes: 125 additions & 0 deletions farm_field_geo/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

=======================
Farm Field — Geospatial
=======================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:156d6ff91db3f7522e9501d6a413cbc2a2e29881632c0f19543ed5de002262fc
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-ledoent%2Ffarm--pack-lightgray.png?logo=github
:target: https://github.com/ledoent/farm-pack/tree/19.0/farm_field_geo
:alt: ledoent/farm-pack

|badge1| |badge2| |badge3|

Adds PostGIS polygon boundaries to ``farm.field`` and auto-computes
acreage from the geometry.

Without this module, ``farm.field.acres`` is a manual decimal entry.
With it, set a WGS84 polygon as the field's boundary and the acreage
updates from the polygon area, reprojected to EPSG:5070 (Conus Albers
Equal Area) — the projection NRCS and NASS use for lower-48 area
calculations, so the number matches what soil-survey and yield tooling
would report for the same parcel.

``acres`` remains editable as a fallback: fields without a digitized
boundary keep working with a manual estimate, and the compute only fires
once a polygon is set.

**Scope.** This module ships a single ``GeoPolygon`` per field —
non-contiguous plots (two disconnected polygons that are
administratively one "field") should be recorded as separate
``farm.field`` records for now. A future ``farm_field_multi_polygon``
extension can promote the column to ``GeoMultiPolygon`` if the demand
materializes.

**Install requirement.** The Postgres instance must have PostGIS
enabled. ``base_geoengine`` declares the extension; install will fail
cleanly if the extension is missing. The base ``farm_field`` module
stays usable without PostGIS — install this extension only when geometry
support is wanted.

.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_

**Table of contents**

.. contents::
:local:

Usage
=====

1. Open Farm → Fields → Fields and pick a field.
2. The **Boundary** notebook tab renders a Leaflet map of the field's
polygon.
3. Set the polygon via the geoengine map view (vertex drag works there)
or by importing a WKT string. In-form vertex editing through the
boundary tab arrives once ``web_leaflet_draw_lib`` is migrated to
Odoo 19.
4. Save. The **Acres** field auto-recomputes from the polygon area,
reprojected to EPSG:5070 Albers Equal Area.
5. Switch the action's view to **Map** (geoengine) to see every field at
once.

The ``acres`` field stays editable as a fallback: fields without a
digitized boundary can carry a manual estimate. Once a polygon is set,
the compute overrides the manual value.

Multi-plot fields (a "north 40" that is two non-contiguous polygons)
need ``GeoMultiPolygon`` rather than ``GeoPolygon`` — out of scope for
the MVP; record each plot as its own ``farm.field`` for now.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/ledoent/farm-pack/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/ledoent/farm-pack/issues/new?body=module:%20farm_field_geo%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Ledo Enterprises

Contributors
------------

- Daniel Kendall <dkendall@ledoweb.com>

Maintainers
-----------

.. |maintainer-dnplkndll| image:: https://github.com/dnplkndll.png?size=40px
:target: https://github.com/dnplkndll
:alt: dnplkndll

Current maintainer:

|maintainer-dnplkndll|

This module is part of the `ledoent/farm-pack <https://github.com/ledoent/farm-pack/tree/19.0/farm_field_geo>`_ project on GitHub.

You are welcome to contribute.
1 change: 1 addition & 0 deletions farm_field_geo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
28 changes: 28 additions & 0 deletions farm_field_geo/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "Farm Field — Geospatial",
"version": "19.0.1.0.0",
"summary": "PostGIS polygons + auto-computed acreage for farm.field",
"author": "Ledo Enterprises, Odoo Community Association (OCA)",
"maintainers": ["dnplkndll"],
"website": "https://github.com/ledoent/farm-pack",
"license": "AGPL-3",
"category": "Vertical/Agriculture",
# Alpha matches farm_field; OCA's check-dev-status job rejects higher
# dev-status modules depending on lower-status ones.
"development_status": "Alpha",
"depends": [
"farm_field",
"base_geoengine",
],
"external_dependencies": {
# pyproj: reproject WGS84 polygon → EPSG:5070 Albers for acreage.
# shapely: base_geoengine already requires it; listed here to make
# the dep graph explicit (CI's oca_install_addons uses this list).
"python": ["pyproj", "shapely"],
},
"data": [
"views/farm_field_views.xml",
],
"installable": True,
"application": False,
}
1 change: 1 addition & 0 deletions farm_field_geo/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import farm_field
52 changes: 52 additions & 0 deletions farm_field_geo/models/farm_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from functools import lru_cache

from pyproj import Transformer
from shapely.ops import transform as shapely_transform

from odoo import api, fields, models

# Square meters in one US survey acre.
M2_PER_ACRE = 4046.8564224


@lru_cache(maxsize=1)
def _wgs84_to_albers_conus():
"""Build the EPSG:4326 → EPSG:5070 transformer once and reuse it.

EPSG:5070 (Conus Albers Equal Area) is what NRCS and NASS use for
lower-48 acreage. Reprojecting the WGS84 polygon there before measuring
area gives a number that matches soil-survey + yield tooling for the
same parcel.
"""
return Transformer.from_crs("EPSG:4326", "EPSG:5070", always_xy=True).transform


class FarmField(models.Model):
_inherit = "farm.field"

geom = fields.GeoPolygon(
string="Boundary",
srid=4326,
help="Field boundary as a WGS84 polygon. Drives the computed acreage; "
"stored as PostGIS geometry.",
)
acres = fields.Float(
compute="_compute_acres_from_geom",
store=True,
readonly=False,
digits=(8, 2),
help="Auto-computed from the polygon area when a boundary is drawn. "
"Editable as a fallback for fields without a digitized boundary yet.",
)

@api.depends("geom")
def _compute_acres_from_geom(self):
# base_geoengine returns the field as a shapely geometry on read.
# shapely has no `.transform()` method — reproject explicitly via
# pyproj + shapely.ops.transform before measuring area.
transformer = _wgs84_to_albers_conus()
for rec in self:
if not rec.geom:
continue
projected = shapely_transform(transformer, rec.geom)
rec.acres = projected.area / M2_PER_ACRE
3 changes: 3 additions & 0 deletions farm_field_geo/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
1 change: 1 addition & 0 deletions farm_field_geo/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Daniel Kendall &lt;dkendall@ledoweb.com&gt;
23 changes: 23 additions & 0 deletions farm_field_geo/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Adds PostGIS polygon boundaries to `farm.field` and auto-computes acreage
from the geometry.

Without this module, `farm.field.acres` is a manual decimal entry. With it,
set a WGS84 polygon as the field's boundary and the acreage updates from the
polygon area, reprojected to EPSG:5070 (Conus Albers Equal Area) — the
projection NRCS and NASS use for lower-48 area calculations, so the number
matches what soil-survey and yield tooling would report for the same parcel.

`acres` remains editable as a fallback: fields without a digitized boundary
keep working with a manual estimate, and the compute only fires once a
polygon is set.

**Scope.** This module ships a single `GeoPolygon` per field — non-contiguous
plots (two disconnected polygons that are administratively one "field")
should be recorded as separate `farm.field` records for now. A future
`farm_field_multi_polygon` extension can promote the column to
`GeoMultiPolygon` if the demand materializes.

**Install requirement.** The Postgres instance must have PostGIS enabled.
`base_geoengine` declares the extension; install will fail cleanly if the
extension is missing. The base `farm_field` module stays usable without
PostGIS — install this extension only when geometry support is wanted.
16 changes: 16 additions & 0 deletions farm_field_geo/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
1. Open Farm → Fields → Fields and pick a field.
2. The **Boundary** notebook tab renders a Leaflet map of the field's polygon.
3. Set the polygon via the geoengine map view (vertex drag works there) or by
importing a WKT string. In-form vertex editing through the boundary tab
arrives once `web_leaflet_draw_lib` is migrated to Odoo 19.
4. Save. The **Acres** field auto-recomputes from the polygon area, reprojected
to EPSG:5070 Albers Equal Area.
5. Switch the action's view to **Map** (geoengine) to see every field at once.

The `acres` field stays editable as a fallback: fields without a digitized
boundary can carry a manual estimate. Once a polygon is set, the compute
overrides the manual value.

Multi-plot fields (a "north 40" that is two non-contiguous polygons) need
`GeoMultiPolygon` rather than `GeoPolygon` — out of scope for the MVP;
record each plot as its own `farm.field` for now.
Empty file.
Loading
Loading