Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 10 additions & 15 deletions base_geoengine/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ It will allow you to :
- Extend Odoo models with spatial columns

GeoEngine relies on `OpenLayers <http://openlayers.org>`__ and
`PostGIS <http://postgis.refractions.net/>`__ technologies.
`PostgGIS <http://postgis.refractions.net/>`__ technologies.

Postgis is used to store spatial information in databases. OpenLayer is
used to represent spatial data in other words to show maps and the
Expand All @@ -62,7 +62,11 @@ To install this module, you need to have

On Ubuntu:

.. code:: bash
::

.. code-block:: bash

..

sudo apt-get install postgis

Expand All @@ -71,11 +75,11 @@ The module also requires two additional python libs:
- `Shapely <http://pypi.python.org/pypi/Shapely>`__
- `geojson <http://pypi.python.org/pypi/geojson>`__

These two additional libs will be installed automatically with the
module.
When you will install the module this two additional libs will be
installed.

For a complete documentation please refer to the `public
documentation <http://oca.github.io/geospatial/index.html>`__
documenation <http://oca.github.io/geospatial/index.html>`__

Usage
=====
Expand Down Expand Up @@ -171,15 +175,6 @@ Known issues / Roadmap
Changelog
=========

19.0.1.0.1
----------

- Migration to Odoo 19.0.
- Migrated JavaScript frontend to OWL/ESM architecture.
- Updated OpenLayers to version 10.5.0.
- Updated Chroma.js to version 3.1.2.
- Refactored domain handling for Odoo 19 ORM compatibility.

16.0.1.0.0 (2023-03-20)
-----------------------

Expand Down Expand Up @@ -352,7 +347,7 @@ Contributors
- Antoni Marroig <amarroig@apsl.net>
- Miquel Alzanillas <malzanillas@apsl.net>

- Red Butay
- Red Butay <>
- Sergio Sancho <sersanchus@gmail.com>

Maintainers
Expand Down
3 changes: 0 additions & 3 deletions base_geoengine/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models
from . import expressions
from . import fields
Expand Down
4 changes: 3 additions & 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": "19.0.1.0.1",
"version": "19.0.1.0.2",
"category": "GeoBI",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"license": "AGPL-3",
Expand All @@ -25,6 +25,8 @@
"base_geoengine/static/src/css/style.css",
"base_geoengine/static/lib/geostats-2.1.0/geostats.css",
],
# Note: Third-party libraries (ol.js, chroma.js, geostats.js) are loaded
# dynamically via loadJS() in geoengine_libs.esm.js to avoid bundler issues
},
"external_dependencies": {"python": ["shapely", "geojson"]},
"installable": True,
Expand Down
2 changes: 0 additions & 2 deletions base_geoengine/domains.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import contextlib
import logging
import warnings
Expand Down
52 changes: 37 additions & 15 deletions base_geoengine/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ def _condition_to_sql(
active_test=True,
alias=rel_alias,
)
model._check_field_access(current_field, "read")
# Record rules (ir.rule) on the related model are applied
# inside where_calc(), mirroring BaseModel._search().
if operator == "geo_equal":
rel_query.add_where(
f'"{alias}"."{field_expr}" {GEO_OPERATORS[operator]} '
Expand Down Expand Up @@ -129,24 +130,35 @@ def get_geo_func(current_operator, operator, left, value, params, table):
"""
This method will call the SQL query corresponding to the requested geo operator
"""
geo_func_map = {
"geo_greater": current_operator.get_geo_greater_sql,
"geo_lesser": current_operator.get_geo_lesser_sql,
"geo_equal": current_operator.get_geo_equal_sql,
"geo_touch": current_operator.get_geo_touch_sql,
"geo_within": current_operator.get_geo_within_sql,
"geo_contains": current_operator.get_geo_contains_sql,
"geo_intersect": current_operator.get_geo_intersect_sql,
}
func = geo_func_map.get(operator)
if func is None:
raise NotImplementedError(f"The operator {operator} is not supported")
return func(table, left, value, params)
match operator:
case "geo_greater":
query = current_operator.get_geo_greater_sql(table, left, value, params)
case "geo_lesser":
query = current_operator.get_geo_lesser_sql(table, left, value, params)
case "geo_equal":
query = current_operator.get_geo_equal_sql(table, left, value, params)
case "geo_touch":
query = current_operator.get_geo_touch_sql(table, left, value, params)
case "geo_within":
query = current_operator.get_geo_within_sql(table, left, value, params)
case "geo_contains":
query = current_operator.get_geo_contains_sql(table, left, value, params)
case "geo_intersect":
query = current_operator.get_geo_intersect_sql(table, left, value, params)
case _:
raise NotImplementedError(f"The operator {operator} is not supported")
return query


def where_calc(model, domain, active_test=True, alias=None):
"""
This method is copied from base, we need to create our own query.

It mirrors ``BaseModel._search``: besides ``active_test`` filtering, it also
applies record rules (``ir.rule``) to the resulting query. This matters for
the indirect geo-operators, whose spatial sub-query is built here: without
it, the sub-query would match related records the user is not allowed to
read (row-level security bypass).
"""
# if the object has an active field ('active', 'x_active'), filter out all
# inactive records unless they were explicitly asked for
Expand All @@ -164,6 +176,16 @@ def where_calc(model, domain, active_test=True, alias=None):
sql_condition = optimized_domain._to_sql(model, alias, query)
query.add_where(sql_condition)

return query
# Apply record rules, like BaseModel._search does. Skipped for the
# superuser (env.su), exactly as in core.
if not model.env.su:
model.browse().check_access("read")
model_sudo = model.sudo().with_context(active_test=False)
sec_domain = model.env["ir.rule"]._compute_domain(model._name, "read")
sec_domain = sec_domain.optimize_full(model_sudo)
if sec_domain.is_false():
query.add_where(SQL("FALSE"))
elif not sec_domain.is_true():
query.add_where(sec_domain._to_sql(model_sudo, alias, query))

return query
11 changes: 6 additions & 5 deletions base_geoengine/geo_convertion_helper.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
import re

from odoo import _

logger = logging.getLogger(__name__)

_HEX_RE = re.compile(r"^[0-9a-fA-F]+$")

try:
import geojson
from shapely import wkb, wkt
Expand All @@ -29,10 +26,14 @@ def value_to_shape(value, use_wkb=False):
if "{" in value:
geo_dict = geojson.loads(value)
return shape(geo_dict)
elif use_wkb or _HEX_RE.match(value):
elif use_wkb:
return wkb.loads(value, hex=True)
else:
return wkt.loads(value)
try:
int(value, 16)
return wkb.loads(value, hex=True)
except Exception:
return wkt.loads(value)
elif hasattr(value, "wkt"):
if isinstance(value, BaseGeometry):
return value
Expand Down
22 changes: 20 additions & 2 deletions base_geoengine/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
##############################################################################
#
# Author: Nicolas Bessi
# Copyright 2011-2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
"""Module that manages map view and vector/raster layer"""

from . import base
from . import geo_raster_layer
Expand Down
30 changes: 3 additions & 27 deletions base_geoengine/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,33 +67,9 @@ def set_field_real_name(self, in_tuple):
field_obj = self.env["ir.model.fields"]
if not in_tuple:
return in_tuple
field_id = None
display_name = None

# Odoo may serialize many2one values as:
# - [id, display_name]
# - {"id": id, "display_name": "..."} (newer formats)
# - id
if isinstance(in_tuple, (list, tuple)):
if in_tuple:
field_id = in_tuple[0]
if len(in_tuple) > 1:
display_name = in_tuple[1]
elif isinstance(in_tuple, dict):
field_id = in_tuple.get("id")
display_name = in_tuple.get("display_name") or in_tuple.get("name")
elif isinstance(in_tuple, int):
field_id = in_tuple

if not field_id:
return in_tuple

field = field_obj.browse(field_id).exists()
technical_name = field.name if field else False
if display_name is None:
display_name = technical_name

return (field_id, technical_name, display_name)
name = field_obj.browse(in_tuple[0]).name
out = (in_tuple[0], name, in_tuple[1])
return out

@api.model
def get_geoengine_layers(self, view_id=None, view_type="geoengine", **options):
Expand Down
4 changes: 3 additions & 1 deletion base_geoengine/models/geo_raster_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ class GeoRasterLayer(models.Model):
params = fields.Char(help="Dictiorary of values for dimensions as JSON")

# wms options
params_wms = fields.Char(help="Need to provide at least a LAYERS param")
params_wms = fields.Char(
"Params WMS", help="Need to provide at least a LAYERS param"
)
server_type = fields.Char(
help="The type of the remote WMS server: mapserver, \
geoserver, carmentaserver, or qgis",
Expand Down
19 changes: 17 additions & 2 deletions base_geoengine/models/geo_vector_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,12 @@ class GeoVectorLayer(models.Model):
ondelete="cascade",
domain=[("ttype", "ilike", "geo_")],
)
attribute_field_id = fields.Many2one(
"ir.model.fields", "Attribute field", domain=[("ttype", "in", SUPPORTED_ATT)]

attribute_field_id_domain = fields.Binary(
compute="_compute_attribute_field_id_domain", readonly=True, store=False
)
attribute_field_id = fields.Many2one("ir.model.fields", "Attribute field")

model_id = fields.Many2one(
"ir.model",
"Model to use",
Expand Down Expand Up @@ -155,3 +158,15 @@ def _compute_model_id(self):
rec.model_id = ""
else:
rec.model_id = ""

@api.depends("geo_field_id")
def _compute_attribute_field_id_domain(self):
for rec in self:
rec.attribute_field_id_domain = (
[
("ttype", "in", SUPPORTED_ATT),
("model", "=", rec.geo_field_id.model_id.model),
]
if rec.geo_field_id
else [("ttype", "in", SUPPORTED_ATT)]
)
2 changes: 1 addition & 1 deletion base_geoengine/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
- [APSL-Nagarro](https://www.apsl.tech):
- Antoni Marroig \<<amarroig@apsl.net>\>
- Miquel Alzanillas \<<malzanillas@apsl.net>\>
- Red Butay
- Red Butay \<\>
- Sergio Sancho \<<sersanchus@gmail.com>\>
2 changes: 1 addition & 1 deletion base_geoengine/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ It will allow you to :
- Extend Odoo models with spatial columns

GeoEngine relies on [OpenLayers](http://openlayers.org) and
[PostGIS](http://postgis.refractions.net/) technologies.
[PostgGIS](http://postgis.refractions.net/) technologies.

Postgis is used to store spatial information in databases. OpenLayer is
used to represent spatial data in other words to show maps and the
Expand Down
8 changes: 0 additions & 8 deletions base_geoengine/readme/HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
## 19.0.1.0.1

- Migration to Odoo 19.0.
- Migrated JavaScript frontend to OWL/ESM architecture.
- Updated OpenLayers to version 10.5.0.
- Updated Chroma.js to version 3.1.2.
- Refactored domain handling for Odoo 19 ORM compatibility.

## 16.0.1.0.0 (2023-03-20)

- LayerSwitcher has been removed as it was not really practical. A
Expand Down
11 changes: 6 additions & 5 deletions base_geoengine/readme/INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ installed.

On Ubuntu:

```bash
sudo apt-get install postgis
```
.. code-block:: bash

> sudo apt-get install postgis

The module also requires two additional python libs:

- [Shapely](http://pypi.python.org/pypi/Shapely)
- [geojson](http://pypi.python.org/pypi/geojson)

These two additional libs will be installed automatically with the module.
When you will install the module this two additional libs will be
installed.

For a complete documentation please refer to the [public
documentation](http://oca.github.io/geospatial/index.html)
documenation](http://oca.github.io/geospatial/index.html)
1 change: 1 addition & 0 deletions base_geoengine/readme/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading