Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
91e77b8
[UPD] README.rst
OCA-git-bot Feb 1, 2023
052221d
[IMP] base_geoengine: black, isort, prettier
kouffsamuel Feb 13, 2023
d6056dc
[MIG] base_geoengine : migration from odoo 14 to odoo 16
kouffsamuel Feb 14, 2023
1c6fe8d
[ADD] base_geoengine: display field geo_edit_map with multipolygon an…
kouffsamuel Feb 15, 2023
d3fa114
[FIX] base_geoengine: fix zip creation and refractor geo_edit_map
kouffsamuel Feb 15, 2023
ba35530
[FIX] base_geoengine: fix retails machine and start geoengine view fix
kouffsamuel Feb 16, 2023
7d5a986
[FIX] base_geoengine: fix geoengine view renderer
kouffsamuel Feb 18, 2023
b35ef10
[ADD] base_geoengine: add left-side menu for layers
kouffsamuel Feb 19, 2023
db03436
[FIX] base_geoengine: display raster layers for zip and retail machin…
kouffsamuel Feb 21, 2023
b31db8b
[FIX] base_geoengine: display vector layers for zip and retail machines
kouffsamuel Feb 23, 2023
faf596e
[FIX] remove ref to submodule
kouffsamuel Feb 24, 2023
d9a0064
[FIX] base_geoengine: display info box
kouffsamuel Feb 28, 2023
9089d14
[FIX] base_geoengine: button to edit record on info box + fix display…
kouffsamuel Feb 28, 2023
065cf20
[FIX] base_geogines: fix bugs and use opacity of the layer management
kouffsamuel Mar 1, 2023
233933e
[FIX] base_geoengine: code cleaning
kouffsamuel Mar 2, 2023
ec21895
[FIX] base_geoengine: fix pre-commit
kouffsamuel Mar 2, 2023
4804091
[IMP][FIX] base_geoengine: start model tests and fix retail machines …
kouffsamuel Mar 6, 2023
b550b42
[IMP] base_geoengine: end model tests
kouffsamuel Mar 6, 2023
42221bd
changing the default SRID (correction of the previous one)
lmarion-source May 27, 2020
88a59f4
[IMP] base_geoengine: add tests from_lat_lonand to_lat_lon
kouffsamuel Mar 6, 2023
638b3bd
[IMP] base_geoengine: refactor geo_search
kouffsamuel Mar 7, 2023
6d6515a
[FIX] base_geoengine: current_field __leaf_to_sql
kouffsamuel Mar 7, 2023
58ffa2c
[FIX] base_geoengine: Display Labels on Polygon
kouffsamuel Mar 8, 2023
539ec39
[FIX] base_geoengine: fix srid
kouffsamuel Mar 8, 2023
ce6476d
[FIX] base_geoengine: fix column definition at install
kouffsamuel Mar 9, 2023
3f11eaf
[IMP] base_geoengine: geo_search is deprecated, normal search method …
kouffsamuel Mar 9, 2023
dcea2fb
[FIX] base_geoengine: fix __leaf_to_sql by adding our own where_calc …
kouffsamuel Mar 10, 2023
e6ab1cd
[FIX] base_geoengine: fix fields SRID
kouffsamuel Mar 13, 2023
9a988f7
[IMP] base_geoengine : Access rules
kouffsamuel Mar 14, 2023
56c6022
[IMP] base_geoengine: Allows to define a model on the vector layer to…
kouffsamuel Mar 15, 2023
18fd28f
[FIX] base_geoengine: fix priority and add domain on geo_field_id
kouffsamuel Mar 15, 2023
1d8390c
[IMP] base_geoengine: this code seems not to be used
kouffsamuel Mar 15, 2023
d6f67ff
[ADD] base_geoengine : add copyright
kouffsamuel Mar 16, 2023
6f2d0dd
[FIX] base_geoengine: Use different types of source for rasters layers
kouffsamuel Mar 16, 2023
16ecf3d
[ADD] base_geoengine: add the possibility to specify a color and an o…
kouffsamuel Mar 16, 2023
70330e9
[IMP] base_geoengine: readme, refactor and documentation
kouffsamuel Mar 20, 2023
2b55e23
[FIX] base_geoengine: fix readme
kouffsamuel Mar 20, 2023
19fbe65
[IMP] base_geoengine: moving unnecessary files from the models folder
kouffsamuel Mar 20, 2023
1ff9c3c
[ADD] base_geoengine: order the vector layer in relation to the sequence
kouffsamuel Mar 20, 2023
7aae222
[FIX] base_geoengine: fix tests
kouffsamuel Mar 20, 2023
49f51ef
[IMP] base_geoengine: setting up and extending the domain widget on t…
kouffsamuel Mar 28, 2023
91a1e60
[ADD] base_geoengine: add editing functionnality of domain of a vecto…
kouffsamuel Mar 30, 2023
16fe91d
[ADD] base_geoengine: Added layer editing functionality and add a sor…
kouffsamuel Apr 4, 2023
4d49ece
[ADD] base_geoengine: add access rights in the layer panel
kouffsamuel Apr 5, 2023
f65832c
[IMP] base_geoengine: readme, documentation
kouffsamuel Apr 6, 2023
0e32114
[ADD] base_geoengine: adding a button to fold or unfold layers panel
kouffsamuel Apr 11, 2023
5aad609
[ADD] base_geoengine: adding records panel
kouffsamuel Apr 11, 2023
301354e
[IMP] base_geoengine: add some interactions to records panel
kouffsamuel Apr 11, 2023
0749237
[ADD] base_geoengine: module translation
kouffsamuel Apr 11, 2023
d0e6f32
[FIX] base_geoengine: fix import
kouffsamuel Apr 11, 2023
0d5d2b9
[IMP] base_geoengine: documentation, readme
kouffsamuel Apr 12, 2023
598ab3a
[IMP] base_geoengine: edit shape instead of redraw (geo_edit_map)
kouffsamuel Apr 25, 2023
0cb4210
[ADD] base_geoengine : modification of records from geoengine view
kouffsamuel Apr 28, 2023
bfc2a64
[ADD] base_geoengine: add zoom in localstorage
kouffsamuel Apr 28, 2023
8d16f28
[ADD] base_geoengine: create records on geoengine view
kouffsamuel May 2, 2023
0098fb0
[FIX] base_geoengine: fix load related model
kouffsamuel May 8, 2023
9f76c93
[FIX] base_geoengine: fix domain widget
kouffsamuel May 8, 2023
594f0cb
[ADD] base_geoengine: add geo_lesser operator
kouffsamuel May 8, 2023
6faad8d
[FIX] base_geoengine: add some validation constraints
kouffsamuel May 12, 2023
3a08e8f
[FIX] base_geoengine: fix in domain widget
kouffsamuel May 12, 2023
8c579fc
[FIX] base_geoengine: fixing the Geoengine Data tab display
kouffsamuel May 12, 2023
fbb094e
[IMP] base_geoengine: changing the colour of a control when clicked
kouffsamuel May 15, 2023
c8f449a
[FIX] base_geoengine : fix domain widget with active_ids and add legend
kouffsamuel May 18, 2023
99c5779
[IMP] base_geoengine: update import
kouffsamuel May 23, 2023
3e45638
[IMP] base_geoengine: remove super.setup() from
kouffsamuel May 23, 2023
ffbae23
[IMP] base_geoengine: replace loadJs and loadCss by loadBundle
kouffsamuel May 23, 2023
08f9b9c
[IMP] base_geoengine: use force when is necessary to add in
kouffsamuel May 23, 2023
21514ad
[IMP] base_geoengine: Encapsulate calls in Promise.all
kouffsamuel May 23, 2023
220a70a
[FIX] base_geoengine: Fix documentation format
lmignon Jun 5, 2023
9280d25
[UPD] Update base_geoengine.pot
Jun 6, 2023
aa90533
[UPD] README.rst
OCA-git-bot Jun 6, 2023
cd5a42f
Update translation files
weblate Jun 6, 2023
676882a
Added translation using Weblate (Spanish)
Ivorra78 Aug 8, 2023
442b312
Translated using Weblate (Spanish)
Ivorra78 Aug 8, 2023
8a8f2bc
[UPD] README.rst
OCA-git-bot Sep 3, 2023
f95392f
Update translation files
weblate Oct 9, 2023
a4383e4
Added translation using Weblate (Swedish)
jakobkrabbe Nov 2, 2023
72bc031
Translated using Weblate (Swedish)
jakobkrabbe Nov 2, 2023
8fdcc2e
Added translation using Weblate (Italian)
mymage Jan 25, 2024
c6f88da
Translated using Weblate (Italian)
mymage Jan 26, 2024
65ae774
[IMP] base_geoengine: pre-commit auto fixes
Apr 25, 2024
ff0bf48
[MIG] base_geoengine: Migration to 17.0
Apr 26, 2024
05c2204
[UPD] Update base_geoengine.pot
Sep 27, 2024
f9dd896
[BOT] post-merge updates
OCA-git-bot Sep 27, 2024
0f3c248
Update translation files
weblate Sep 27, 2024
c90294f
Translated using Weblate (Italian)
mymage Sep 27, 2024
0a8d11d
[UPDATE] Add rtl ignore for persian and arabic language
Sanazzzmi Nov 18, 2024
76fdbf1
Added translation using Weblate (Chinese (Simplified) (zh_CN))
xtanuiha Nov 25, 2024
fcd6052
[BOT] post-merge updates
OCA-git-bot Nov 25, 2024
e9ec494
Translated using Weblate (Chinese (Simplified) (zh_CN))
xtanuiha Nov 25, 2024
5261c31
[IMP] base_geoengine: pre-commit auto fixes
cormaza May 26, 2025
734f962
[18.0][MIG] base_geoengine: Migration to 18.0
cormaza May 26, 2025
9b9bfc3
[18.0][UPD] upgrade version of libs used in render
cormaza Jun 11, 2025
54dd285
[UPD] Update base_geoengine.pot
Jul 17, 2025
654f697
[BOT] post-merge updates
OCA-git-bot Jul 17, 2025
a460a1b
[IMP] base_geoengine: pre-commit auto fixes
weinni2000 Oct 4, 2025
07b65c6
[MIG] base_geoengine: Migration to 19.0
weinni2000 Oct 4, 2025
1614afd
Merge branch '19.0' into 19.0-mig-base_geoengine
weinni2000 Jan 28, 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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
makepot: "true"
services:
postgres:
image: postgres:13
image: postgis/postgis:13-3.4
env:
POSTGRES_USER: odoo
POSTGRES_PASSWORD: odoo
Expand Down
10 changes: 5 additions & 5 deletions base_geoengine/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ Geospatial support for Odoo
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fgeospatial-lightgray.png?logo=github
:target: https://github.com/OCA/geospatial/tree/18.0/base_geoengine
:target: https://github.com/OCA/geospatial/tree/19.0/base_geoengine
:alt: OCA/geospatial
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/geospatial-18-0/geospatial-18-0-base_geoengine
:target: https://translation.odoo-community.org/projects/geospatial-19-0/geospatial-19-0-base_geoengine
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/geospatial&target_branch=18.0
:target: https://runboat.odoo-community.org/builds?repo=OCA/geospatial&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|
Expand Down Expand Up @@ -306,7 +306,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/geospatial/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/OCA/geospatial/issues/new?body=module:%20base_geoengine%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
`feedback <https://github.com/OCA/geospatial/issues/new?body=module:%20base_geoengine%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.

Expand Down Expand Up @@ -363,6 +363,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/geospatial <https://github.com/OCA/geospatial/tree/18.0/base_geoengine>`_ project on GitHub.
This module is part of the `OCA/geospatial <https://github.com/OCA/geospatial/tree/19.0/base_geoengine>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions base_geoengine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
from . import geo_convertion_helper
from . import geo_operators
from .geo_db import init_postgis
from . import domains
2 changes: 1 addition & 1 deletion base_geoengine/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Geospatial support for Odoo",
"version": "18.0.1.0.1",
"version": "19.0.1.0.1",
"category": "GeoBI",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"license": "AGPL-3",
Expand Down
149 changes: 149 additions & 0 deletions base_geoengine/domains.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import contextlib
import logging
import warnings

from odoo.fields import Domain
from odoo.models import BaseModel
from odoo.orm import domains
from odoo.orm.domains import (
CONDITION_OPERATORS,
NEGATIVE_CONDITION_OPERATORS,
SQL,
DomainCondition,
OptimizationLevel,
Query,
)
from odoo.orm.identifiers import NewId

_logger = logging.getLogger(__name__)

GEO_OPERATORS = frozenset(
[
"geo_greater",
"geo_lesser",
"geo_equal",
"geo_touch",
"geo_within",
"geo_contains",
"geo_intersect",
]
)


def checked(self) -> DomainCondition:
"""Validate `self` and return it if correct, otherwise raise an exception."""
if not isinstance(self.field_expr, str) or not self.field_expr:
self._raise("Empty field name", error=TypeError)
operator = self.operator.lower()
if operator != self.operator:
warnings.warn(
(
f"Deprecated since 19.0, the domain condition "
f"{(self.field_expr, self.operator, self.value)!r} "
f"should have a lower-case operator"
),
DeprecationWarning,
# <MOD>
stacklevel=2,
# </MOD>
)
return DomainCondition(self.field_expr, operator, self.value).checked()
if operator not in CONDITION_OPERATORS:
# <MOD>
if operator not in GEO_OPERATORS:
# </MOD>
self._raise("Invalid operator")

# check already the consistency for domain manipulation
# these are common mistakes and optimizations,
# do them here to avoid recreating the domain
# - NewId is not a value
# - records are not accepted, use values
# - Query and Domain values should be using a relational operator
# <MOD>
# from .models import BaseModel # noqa: PLC0415
# </MOD>

value = self.value
if value is None:
value = False
elif isinstance(value, NewId):
_logger.warning(
"Domains don't support NewId, use .ids instead, for %r",
(self.field_expr, self.operator, self.value),
)
operator = "not in" if operator in NEGATIVE_CONDITION_OPERATORS else "in"
value = []
elif isinstance(value, BaseModel):
_logger.warning(
"The domain condition %r should not have a value which is a model",
(self.field_expr, self.operator, self.value),
)
value = value.ids
elif isinstance(value, (Domain, Query, SQL)) and operator not in (
"any",
"not any",
"any!",
"not any!",
"in",
"not in",
):
# accept SQL object in the right part for simple operators
# use case: compare 2 fields
_logger.warning(
"The domain condition %r should use the 'any' or 'not any' operator.",
(self.field_expr, self.operator, self.value),
)
if value is not self.value:
return DomainCondition(self.field_expr, operator, value)
return self


def _to_sql(self, model: BaseModel, alias: str, query: Query) -> SQL:
"""Enhanced _to_sql that handles geospatial operators."""
field_expr, operator, value = self.field_expr, self.operator, self.value

# Only handle geospatial operators here, delegate everything else to original method
if operator in GEO_OPERATORS:
# Ensure geospatial conditions are fully optimized
assert self._opt_level >= OptimizationLevel.FULL, (
"Must fully optimize before generating the query "
f"{(field_expr, operator, value)}"
)

field = self._field(model)
model._check_field_access(field, "read")
return field.condition_to_sql(field_expr, operator, value, model, alias, query)

# For all other operators, use the original method
return original__to_sql(self, model, alias, query)


def _optimize_step(self, model: BaseModel, level: OptimizationLevel) -> Domain:
"""Optimization step for geospatial operators."""
# For geospatial operators, we need to handle them specially during optimization
# If this is a geospatial operator, mark it as optimized at FULL level
if self.operator in GEO_OPERATORS:
# Perform basic validation and normalization
with contextlib.suppress(Exception):
field = self._field(model)
# Basic geospatial operator validation
if hasattr(field, "geo_type"): # It's a geospatial field
# Create optimized version with FULL level
optimized = DomainCondition(self.field_expr, self.operator, self.value)
object.__setattr__(optimized, "_opt_level", OptimizationLevel.FULL)
return optimized

# Fall back to original optimization for non-geo operators
return original__optimize_step(self, model, level)


# Store original methods before monkey patching
original__optimize_step = DomainCondition._optimize_step
original__to_sql = DomainCondition._to_sql

DomainCondition.checked = checked
DomainCondition._to_sql = _to_sql
DomainCondition._optimize_step = _optimize_step

domains.CONDITION_OPERATORS = domains.CONDITION_OPERATORS.union(GEO_OPERATORS)
72 changes: 45 additions & 27 deletions base_geoengine/expressions.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging
import random
import string

from odoo import fields
from odoo.fields import Domain
from odoo.models import BaseModel
from odoo.osv import expression
from odoo.osv.expression import TERM_OPERATORS
from odoo.tools import SQL, Query

from .fields import GeoField
from .geo_operators import GeoOperator

original___condition_to_sql = BaseModel._condition_to_sql
logger = logging.getLogger(__name__)

original___condition_to_sql = fields.Field._condition_to_sql

GEO_OPERATORS = {
"geo_greater": ">",
Expand All @@ -23,6 +26,7 @@
"geo_contains": "ST_Contains",
"geo_intersect": "ST_Intersects",
}

GEO_SQL_OPERATORS = {
"geo_greater": SQL(">"),
"geo_lesser": SQL("<"),
Expand All @@ -32,23 +36,23 @@
"geo_contains": SQL("ST_Contains"),
"geo_intersect": SQL("ST_Intersects"),
}
term_operators_list = list(TERM_OPERATORS)
for op in GEO_OPERATORS:
term_operators_list.append(op)

expression.TERM_OPERATORS = tuple(term_operators_list)
expression.SQL_OPERATORS.update(GEO_SQL_OPERATORS)


def _condition_to_sql(
self, alias: str, fname: str, operator: str, value, query: Query
self,
field_expr: str,
operator: str,
value,
model: BaseModel,
alias: str,
query: Query,
) -> SQL:
"""
This method has been monkey patched in order to be able to include
geo_operators into the Odoo search method.
"""
if operator in GEO_OPERATORS.keys():
current_field = self._fields.get(fname)
current_field = model._fields.get(field_expr)
current_operator = GeoOperator(current_field)
if current_field and isinstance(current_field, GeoField):
params = []
Expand All @@ -59,9 +63,9 @@ def _condition_to_sql(
sub_queries = []
for key in ref_search:
i = key.rfind(".")
rel_model = key[0:i]
rel_model_name = key[0:i]
rel_col = key[i + 1 :]
rel_model = self.env[rel_model]
rel_model = model.env[rel_model_name]
# we compute the attributes search on spatial rel
if ref_search[key]:
rel_alias = (
Expand All @@ -75,42 +79,52 @@ def _condition_to_sql(
active_test=True,
alias=rel_alias,
)
self._apply_ir_rules(rel_query, "read")
model._check_field_access(current_field, "read")
if operator == "geo_equal":
rel_query.add_where(
f'"{alias}"."{fname}" {GEO_OPERATORS[operator]} '
f'"{alias}"."{field_expr}" {GEO_OPERATORS[operator]} '
f"{rel_alias}.{rel_col}"
)
elif operator in ("geo_greater", "geo_lesser"):
rel_query.add_where(
f"ST_Area({alias}.{fname}) {GEO_OPERATORS[operator]} "
f"ST_Area({alias}.{field_expr}) "
f"{GEO_OPERATORS[operator]} "
f"ST_Area({rel_alias}.{rel_col})"
)
else:
rel_query.add_where(
f'{GEO_OPERATORS[operator]}("{alias}"."{fname}", '
f'{GEO_OPERATORS[operator]}("{alias}"."{field_expr}", '
f"{rel_alias}.{rel_col})"
)

subquery, subparams = rel_query.subselect("1")
subquery_sql = rel_query.subselect("1")
sub_query_mogrified = (
self.env.cr.mogrify(subquery, subparams)
model.env.cr.mogrify(subquery_sql.code, subquery_sql.params)
.decode("utf-8")
.replace(f"'{rel_model._table}'", f'"{rel_model._table}"')
.replace("%", "%%")
)
sub_queries.append(f"EXISTS({sub_query_mogrified})")
query = " AND ".join(sub_queries)
query_str = " AND ".join(sub_queries)
else:
query = get_geo_func(
current_operator, operator, fname, value, params, self._table
query_str = get_geo_func(
current_operator, operator, field_expr, value, params, model._table
)
return SQL(query, *params)
return SQL(query_str, *params)
return original___condition_to_sql(
self, alias=alias, fname=fname, operator=operator, value=value, query=query
self,
field_expr=field_expr,
operator=operator,
value=value,
model=model,
alias=alias,
query=query,
)


fields.Field._condition_to_sql = _condition_to_sql


def get_geo_func(current_operator, operator, left, value, params, table):
"""
This method will call the SQL query corresponding to the requested geo operator
Expand Down Expand Up @@ -149,8 +163,12 @@ def where_calc(model, domain, active_test=True, alias=None):

query = Query(model.env, alias, model._table)
if domain:
return expression.expression(domain, model, alias=alias, query=query).query
return query
# In Odoo 19, create Domain object and use its _to_sql method
domain_obj = Domain(domain)
optimized_domain = domain_obj.optimize_full(model)
sql_condition = optimized_domain._to_sql(model, alias, query)
query.add_where(sql_condition)

return query

BaseModel._condition_to_sql = _condition_to_sql
return query
Loading