mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-20 01:52:01 +02:00
Initial commit: OCA Technical packages (595 packages)
This commit is contained in:
commit
2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions
|
|
@ -0,0 +1,25 @@
|
|||
##############################################################################
|
||||
#
|
||||
# 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
|
||||
from . import geo_vector_layer
|
||||
from . import ir_view
|
||||
from . import ir_model
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
|
||||
# Copyright 2016 Yannick Payot (Camptocamp SA)
|
||||
# Copyright 2023 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
import logging
|
||||
|
||||
from odoo import _, api, models
|
||||
from odoo.exceptions import MissingError, UserError
|
||||
from odoo.osv.expression import AND
|
||||
|
||||
from .. import fields as geo_fields
|
||||
|
||||
DEFAULT_EXTENT = (
|
||||
"-123164.85222423, 5574694.9538936, " "1578017.6490538, 6186191.1800898"
|
||||
)
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Base(models.AbstractModel):
|
||||
"""Extend Base class for to allow definition of geo fields."""
|
||||
|
||||
_inherit = "base"
|
||||
|
||||
# Array of ash that define layer and data to use
|
||||
_georepr = []
|
||||
|
||||
@api.model
|
||||
def fields_get(self, allfields=None, attributes=None):
|
||||
"""Add geo_type definition for geo fields"""
|
||||
res = super().fields_get(allfields=allfields, attributes=attributes)
|
||||
for f_name in res:
|
||||
field = self._fields.get(f_name)
|
||||
if field and field.type.startswith("geo_"):
|
||||
geo_type = {
|
||||
"type": field.type,
|
||||
"dim": int(field.dim),
|
||||
"srid": field.srid,
|
||||
"geo_type": field.geo_type,
|
||||
}
|
||||
# TODO
|
||||
if field.compute or field.related:
|
||||
if not field.dim:
|
||||
geo_type["dim"] = 2
|
||||
if not field.srid:
|
||||
geo_type["srid"] = 3857
|
||||
res[f_name]["geo_type"] = geo_type
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _get_geo_view(self):
|
||||
IrView = self.env["ir.ui.view"]
|
||||
geo_view = IrView.sudo().search(
|
||||
[("model", "=", self._name), ("type", "=", "geoengine")],
|
||||
limit=1,
|
||||
)
|
||||
if not geo_view:
|
||||
raise UserError(
|
||||
_("No GeoEngine view defined for the model %s") % self._name,
|
||||
_("Please create a view or modify view mode"),
|
||||
)
|
||||
return geo_view
|
||||
|
||||
@api.model
|
||||
def set_field_real_name(self, in_tuple):
|
||||
field_obj = self.env["ir.model.fields"]
|
||||
if not in_tuple:
|
||||
return in_tuple
|
||||
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):
|
||||
view_obj = self.env["ir.ui.view"]
|
||||
|
||||
if not view_id:
|
||||
view = self._get_geo_view()
|
||||
else:
|
||||
view = view_obj.browse(view_id)
|
||||
geoengine_layers = {
|
||||
"backgrounds": [],
|
||||
"actives": [],
|
||||
"projection": view.projection,
|
||||
"restricted_extent": view.restricted_extent,
|
||||
"default_extent": view.default_extent or DEFAULT_EXTENT,
|
||||
"default_zoom": view.default_zoom,
|
||||
}
|
||||
|
||||
for layer in view.raster_layer_ids:
|
||||
layer_dict = layer.read()[0]
|
||||
geoengine_layers["backgrounds"].append(layer_dict)
|
||||
for layer in view.vector_layer_ids:
|
||||
layer_dict = layer.read()[0]
|
||||
layer_dict["attribute_field_id"] = self.set_field_real_name(
|
||||
layer_dict.get("attribute_field_id", False)
|
||||
)
|
||||
layer_dict["geo_field_id"] = self.set_field_real_name(
|
||||
layer_dict.get("geo_field_id", False)
|
||||
)
|
||||
layer_dict["resModel"] = layer._name
|
||||
layer_dict["model"] = layer.model_id.model
|
||||
layer_dict["model_domain"] = layer.model_domain
|
||||
geoengine_layers["actives"].append(layer_dict)
|
||||
return geoengine_layers
|
||||
|
||||
@api.model
|
||||
def get_edit_info_for_geo_column(self, column):
|
||||
raster_obj = self.env["geoengine.raster.layer"]
|
||||
|
||||
field = self._fields.get(column)
|
||||
if not field or not isinstance(field, geo_fields.GeoField):
|
||||
raise ValueError(
|
||||
_("%s column does not exists or is not a geo field") % column
|
||||
)
|
||||
view = self._get_geo_view()
|
||||
raster = raster_obj.search(
|
||||
[("view_id", "=", view.id), ("use_to_edit", "=", True)], limit=1
|
||||
)
|
||||
if not raster:
|
||||
raster = raster_obj.search([("view_id", "=", view.id)], limit=1)
|
||||
if not raster:
|
||||
raise MissingError(_("No raster layer for view %s") % (view.name,))
|
||||
return {
|
||||
"edit_raster": raster.read()[0],
|
||||
"srid": field.srid,
|
||||
"projection": view.projection,
|
||||
"restricted_extent": view.restricted_extent,
|
||||
"default_extent": view.default_extent or DEFAULT_EXTENT,
|
||||
"default_zoom": view.default_zoom,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def geo_search(
|
||||
self, domain=None, geo_domain=None, offset=0, limit=None, order=None
|
||||
):
|
||||
"""Perform a geo search it allows direct domain:
|
||||
geo_search(
|
||||
domain=[('name', 'ilike', 'toto']),
|
||||
geo_domain=[('the_point', 'geo_intersect',
|
||||
myshaply_obj or mywkt or mygeojson)])
|
||||
|
||||
We can also support indirect geo_domain (
|
||||
‘geom’, ‘geo_operator’, {‘res.zip.poly’: [‘id’, ‘in’, [1,2,3]] })
|
||||
|
||||
The supported operators are :
|
||||
* geo_greater
|
||||
* geo_lesser
|
||||
* geo_equal
|
||||
* geo_touch
|
||||
* geo_within
|
||||
* geo_contains
|
||||
* geo_intersect"""
|
||||
# First we do a standard search in order to apply security rules
|
||||
# and do a search on standard attributes
|
||||
# Limit and offset are managed after, we may loose a lot of performance
|
||||
# here
|
||||
_logger.debug(
|
||||
_("geo_search is deprecated: uses search method defined on base model")
|
||||
)
|
||||
domain = domain or []
|
||||
geo_domain = geo_domain or []
|
||||
search_domain = domain or []
|
||||
if domain and geo_domain:
|
||||
search_domain = AND([domain, geo_domain])
|
||||
elif geo_domain:
|
||||
search_domain = geo_domain
|
||||
|
||||
if not search_domain:
|
||||
raise ValueError(_("You must at least provide one of domain or geo_domain"))
|
||||
|
||||
return self.search(search_domain, limit=limit, offset=offset, order=order)
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
|
||||
# Copyright 2016 Yannick Payot (Camptocamp SA)
|
||||
# Copyright 2023 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class GeoRasterLayerType(models.Model):
|
||||
_name = "geoengine.raster.layer.type"
|
||||
_description = "Raster Layer Type"
|
||||
|
||||
name = fields.Char(translate=True, required=True)
|
||||
code = fields.Char(required=True)
|
||||
service = fields.Char(required=True)
|
||||
|
||||
|
||||
class GeoRasterLayer(models.Model):
|
||||
_name = "geoengine.raster.layer"
|
||||
_description = "Raster Layer"
|
||||
|
||||
raster_type = fields.Selection(
|
||||
[
|
||||
("osm", "OpenStreetMap"),
|
||||
("wmts", "WMTS"),
|
||||
("d_wms", "Distant WMS"),
|
||||
("odoo", "Odoo field"),
|
||||
],
|
||||
string="Raster layer type",
|
||||
default="osm",
|
||||
required=True,
|
||||
)
|
||||
name = fields.Char("Layer Name", translate=True, required=True)
|
||||
url = fields.Char("Service URL")
|
||||
|
||||
# technical field to display or not wmts options
|
||||
is_wmts = fields.Boolean(compute="_compute_is_wmts")
|
||||
# technical field to display or not wms options
|
||||
is_wms = fields.Boolean(compute="_compute_is_wms")
|
||||
# wmts options
|
||||
matrix_set = fields.Char("Matrix set")
|
||||
format_suffix = fields.Char("Format", help="eg. png")
|
||||
request_encoding = fields.Char("Request encoding", help="eg. REST")
|
||||
projection = fields.Char(help="eg. EPSG:21781")
|
||||
units = fields.Char(help="eg. m") # Not used
|
||||
resolutions = fields.Char()
|
||||
max_extent = fields.Char("Max extent")
|
||||
dimensions = fields.Char(help="List of dimensions separated by ','")
|
||||
params = fields.Char(help="Dictiorary of values for dimensions as JSON")
|
||||
|
||||
# wms options
|
||||
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",
|
||||
)
|
||||
|
||||
# technical field to display or not layer type -- Not used
|
||||
has_type = fields.Boolean(compute="_compute_has_type")
|
||||
type_id = fields.Many2one(
|
||||
"geoengine.raster.layer.type", "Layer", domain="[('service', '=', raster_type)]"
|
||||
)
|
||||
type = fields.Char(related="type_id.code")
|
||||
sequence = fields.Integer("Layer priority", default=6)
|
||||
overlay = fields.Boolean("Is overlay layer?")
|
||||
field_id = fields.Many2one(
|
||||
"ir.model.fields",
|
||||
"Odoo layer field to use",
|
||||
domain=[("ttype", "ilike", "geo_"), ("model", "=", "view_id.model")],
|
||||
)
|
||||
view_id = fields.Many2one(
|
||||
"ir.ui.view", "Related View", domain=[("type", "=", "geoengine")], required=True
|
||||
)
|
||||
use_to_edit = fields.Boolean("Use to edit")
|
||||
|
||||
@api.depends("raster_type", "is_wmts")
|
||||
def _compute_has_type(self):
|
||||
for rec in self:
|
||||
rec.has_type = rec.raster_type == "is_wmts"
|
||||
|
||||
@api.depends("raster_type")
|
||||
def _compute_is_wmts(self):
|
||||
for rec in self:
|
||||
rec.is_wmts = rec.raster_type == "wmts"
|
||||
|
||||
@api.depends("raster_type")
|
||||
def _compute_is_wms(self):
|
||||
for rec in self:
|
||||
rec.is_wms = rec.raster_type == "d_wms"
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
|
||||
# Copyright 2016 Yannick Payot (Camptocamp SA)
|
||||
# Copyright 2023 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
SUPPORTED_ATT = [
|
||||
"float",
|
||||
"integer",
|
||||
"integer_big",
|
||||
"related",
|
||||
"function",
|
||||
"date",
|
||||
"datetime",
|
||||
"char",
|
||||
"text",
|
||||
"selection",
|
||||
]
|
||||
|
||||
NUMBER_ATT = ["float", "integer", "integer_big"]
|
||||
|
||||
|
||||
class GeoVectorLayer(models.Model):
|
||||
_name = "geoengine.vector.layer"
|
||||
_description = "Vector Layer"
|
||||
_order = "sequence ASC, name"
|
||||
|
||||
geo_repr = fields.Selection(
|
||||
[
|
||||
("basic", "Basic"),
|
||||
# Actually we have to think if we should separate it for colored
|
||||
("proportion", "Proportional Symbol"),
|
||||
("colored", "Colored range/Chroma.js"),
|
||||
],
|
||||
string="Representation mode",
|
||||
required=True,
|
||||
)
|
||||
|
||||
classification = fields.Selection(
|
||||
[
|
||||
("unique", "Unique value"),
|
||||
("interval", "Interval"),
|
||||
("quantile", "Quantile"),
|
||||
("custom", "Custom"),
|
||||
],
|
||||
string="Classification mode",
|
||||
required=False,
|
||||
)
|
||||
name = fields.Char("Layer Name", translate=True, required=True)
|
||||
begin_color = fields.Char("Begin color class", required=False, help="hex value")
|
||||
end_color = fields.Char(
|
||||
"End color class", required=False, help="hex value", default="#FF680A"
|
||||
)
|
||||
nb_class = fields.Integer("Number of class", default=1)
|
||||
geo_field_id = fields.Many2one(
|
||||
"ir.model.fields",
|
||||
"Geo field",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
domain=[("ttype", "ilike", "geo_")],
|
||||
)
|
||||
attribute_field_id = fields.Many2one(
|
||||
"ir.model.fields", "Attribute field", domain=[("ttype", "in", SUPPORTED_ATT)]
|
||||
)
|
||||
model_id = fields.Many2one(
|
||||
"ir.model",
|
||||
"Model to use",
|
||||
store=True,
|
||||
readonly=False,
|
||||
compute="_compute_model_id",
|
||||
)
|
||||
model_name = fields.Char(related="model_id.model", readonly=True)
|
||||
|
||||
view_id = fields.Many2one(
|
||||
"ir.ui.view", "Related View", domain=[("type", "=", "geoengine")], required=True
|
||||
)
|
||||
sequence = fields.Integer("Layer Priority", default=6)
|
||||
readonly = fields.Boolean("Layer is read only")
|
||||
display_polygon_labels = fields.Boolean("Display Labels on Polygon")
|
||||
active_on_startup = fields.Boolean(
|
||||
help="Layer will be shown on startup if checked."
|
||||
)
|
||||
layer_opacity = fields.Float(default=1.0)
|
||||
model_domain = fields.Char(default="[]")
|
||||
model_view_id = fields.Many2one(
|
||||
"ir.ui.view",
|
||||
"Model view",
|
||||
domain=[("type", "=", "geoengine")],
|
||||
compute="_compute_model_view_id",
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
@api.constrains("geo_field_id", "model_id")
|
||||
def _check_geo_field_id(self):
|
||||
for rec in self:
|
||||
if rec.model_id:
|
||||
if not rec.geo_field_id.model_id == rec.model_id:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"The geo_field_id must be a field in %s model",
|
||||
rec.model_id.display_name,
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("geo_repr", "attribute_field_id")
|
||||
def _check_geo_repr(self):
|
||||
for rec in self:
|
||||
if (
|
||||
rec.attribute_field_id
|
||||
and rec.attribute_field_id.ttype not in NUMBER_ATT
|
||||
):
|
||||
if (
|
||||
rec.geo_repr == "colored"
|
||||
and rec.classification != "unique"
|
||||
or rec.geo_repr == "proportion"
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You need to select a numeric field",
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("attribute_field_id", "geo_field_id")
|
||||
def _check_if_attribute_in_geo_field(self):
|
||||
for rec in self:
|
||||
if rec.attribute_field_id and rec.geo_field_id:
|
||||
if rec.attribute_field_id.model != rec.geo_field_id.model:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You need to provide an attribute that exists in %s model",
|
||||
rec.geo_field_id.model_id.display_name,
|
||||
)
|
||||
)
|
||||
|
||||
@api.depends("model_id")
|
||||
def _compute_model_view_id(self):
|
||||
for rec in self:
|
||||
if rec.model_id:
|
||||
for view in rec.model_id.view_ids:
|
||||
if view.type == "geoengine":
|
||||
rec.model_view_id = view
|
||||
else:
|
||||
rec.model_view_id = ""
|
||||
|
||||
@api.depends("geo_field_id", "view_id")
|
||||
def _compute_model_id(self):
|
||||
for rec in self:
|
||||
if rec.view_id and rec.geo_field_id:
|
||||
if rec.view_id.model != rec.geo_field_id.model:
|
||||
rec.model_id = rec.geo_field_id.model_id
|
||||
else:
|
||||
rec.model_id = ""
|
||||
else:
|
||||
rec.model_id = ""
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
|
||||
# Copyright 2023 Yannick Payot (Camptocamp SA)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo import fields, models
|
||||
|
||||
from odoo.addons import base
|
||||
|
||||
if "geoengine" not in base.models.ir_actions.VIEW_TYPES:
|
||||
base.models.ir_actions.VIEW_TYPES.append(("geoengine", "Geoengine"))
|
||||
|
||||
GEO_TYPES = [
|
||||
("geo_polygon", "geo_polygon"),
|
||||
("geo_multi_polygon", "geo_multi_polygon"),
|
||||
("geo_point", "geo_point"),
|
||||
("geo_multi_point", "geo_multi_point"),
|
||||
("geo_line", "geo_line"),
|
||||
("geo_multi_line", "geo_multi_line"),
|
||||
]
|
||||
|
||||
GEO_TYPES_ONDELETE = {
|
||||
"geo_polygon": "cascade",
|
||||
"geo_multi_polygon": "cascade",
|
||||
"geo_point": "cascade",
|
||||
"geo_multi_point": "cascade",
|
||||
"geo_line": "cascade",
|
||||
"geo_multi_line": "cascade",
|
||||
}
|
||||
|
||||
POSTGIS_GEO_TYPES = [
|
||||
("POINT", "POINT"),
|
||||
("MULTIPOINT", "MULTIPOINT"),
|
||||
("LINESTRING", "LINESTRING"),
|
||||
("MULTILINESTRING", "MULTILINESTRING"),
|
||||
("POLYGON", "POLYGON"),
|
||||
("MULTIPOLYGON", "MULTIPOLYGON"),
|
||||
]
|
||||
|
||||
|
||||
class IrModelField(models.Model):
|
||||
_inherit = "ir.model.fields"
|
||||
|
||||
srid = fields.Integer("srid", required=False)
|
||||
geo_type = fields.Selection(POSTGIS_GEO_TYPES, string="PostGIs type")
|
||||
dim = fields.Selection(
|
||||
[("2", "2"), ("3", "3"), ("4", "4")], string="PostGIs Dimension", default="2"
|
||||
)
|
||||
gist_index = fields.Boolean("Create gist index")
|
||||
ttype = fields.Selection(
|
||||
selection_add=GEO_TYPES,
|
||||
ondelete=GEO_TYPES_ONDELETE,
|
||||
)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
|
||||
# Copyright 2016-2023 Yannick Payot (Camptocamp SA)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class IrUIView(models.Model):
|
||||
_inherit = "ir.ui.view"
|
||||
|
||||
type = fields.Selection(
|
||||
selection_add=[("geoengine", "GeoEngine")],
|
||||
ondelete={"geoengine": "cascade"},
|
||||
)
|
||||
|
||||
raster_layer_ids = fields.One2many(
|
||||
"geoengine.raster.layer", "view_id", "Raster layers", required=False
|
||||
)
|
||||
|
||||
vector_layer_ids = fields.One2many(
|
||||
"geoengine.vector.layer", "view_id", "Vector layers", required=True
|
||||
)
|
||||
|
||||
projection = fields.Char(default="EPSG:3857", required=True)
|
||||
default_extent = fields.Char(
|
||||
"Default map extent",
|
||||
default="-123164.85222423, 5574694.9538936, 1578017.6490538,"
|
||||
" 6186191.1800898",
|
||||
)
|
||||
default_zoom = fields.Integer("Default map zoom")
|
||||
restricted_extent = fields.Char("Restricted map extent")
|
||||
Loading…
Add table
Add a link
Reference in a new issue