mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-19 11:52:00 +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,330 @@
|
|||
# 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 json
|
||||
import logging
|
||||
from operator import attrgetter
|
||||
|
||||
from odoo import _, fields
|
||||
from odoo.tools import sql
|
||||
|
||||
from . import geo_convertion_helper as convert
|
||||
from .geo_db import create_geo_column, create_geo_index
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
try:
|
||||
import geojson
|
||||
from shapely.geometry import Point, shape
|
||||
from shapely.geometry.base import BaseGeometry
|
||||
from shapely.wkb import loads as wkbloads
|
||||
except ImportError:
|
||||
logger.warning("Shapely or geojson are not available in the sys path")
|
||||
|
||||
|
||||
class GeoField(fields.Field):
|
||||
"""The field descriptor contains the field definition common to all
|
||||
specialized fields for geolocalization. Subclasses must define a type
|
||||
and a geo_type. The type is the name of the corresponding column type,
|
||||
the geo_type is the name of the corresponding type in the GIS system.
|
||||
"""
|
||||
|
||||
geo_type = None
|
||||
dim = 2
|
||||
srid = 3857
|
||||
gist_index = True
|
||||
|
||||
@property
|
||||
def column_type(self):
|
||||
return ("geometry", f"geometry({self.geo_type.upper()}, {self.srid})")
|
||||
|
||||
def convert_to_column(self, value, record, values=None):
|
||||
"""Convert value to database format
|
||||
|
||||
value can be geojson, wkt, shapely geometry object.
|
||||
If geo_direct_write in context you can pass diretly WKT"""
|
||||
if not value:
|
||||
return None
|
||||
shape_to_write = self.entry_to_shape(value, same_type=True)
|
||||
if shape_to_write.is_empty:
|
||||
return None
|
||||
else:
|
||||
return f"SRID={self.srid};{shape_to_write.wkt}"
|
||||
|
||||
def convert_to_cache(self, value, record, validate=True):
|
||||
val = value
|
||||
if isinstance(val, (bytes, str)):
|
||||
try:
|
||||
int(val, 16)
|
||||
except Exception:
|
||||
# not an hex value -> try to load from a sting
|
||||
# representation of a geometry
|
||||
value = convert.value_to_shape(value, use_wkb=False)
|
||||
if isinstance(value, BaseGeometry):
|
||||
val = value.wkb_hex
|
||||
return val
|
||||
|
||||
def convert_to_record(self, value, record):
|
||||
"""Value may be:
|
||||
- a GeoJSON string when field onchange is triggered
|
||||
- a geometry object hexcode from cache
|
||||
- a unicode containing dict
|
||||
"""
|
||||
if not value:
|
||||
return False
|
||||
return convert.value_to_shape(value, use_wkb=True)
|
||||
|
||||
def convert_to_read(self, value, record, use_name_get=True):
|
||||
if not isinstance(value, BaseGeometry):
|
||||
# read hexadecimal value from database
|
||||
shape = self.load_geo(value)
|
||||
else:
|
||||
shape = value
|
||||
if not shape or shape.is_empty:
|
||||
return False
|
||||
return geojson.dumps(shape)
|
||||
|
||||
#
|
||||
# Field description
|
||||
#
|
||||
|
||||
# properties used by get_description()
|
||||
_description_dim = property(attrgetter("dim"))
|
||||
_description_srid = property(attrgetter("srid"))
|
||||
_description_gist_index = property(attrgetter("gist_index"))
|
||||
|
||||
@classmethod
|
||||
def load_geo(cls, wkb):
|
||||
"""Load geometry into browse record after read was done"""
|
||||
if isinstance(wkb, BaseGeometry):
|
||||
return wkb
|
||||
return wkbloads(wkb, hex=True) if wkb else False
|
||||
|
||||
def entry_to_shape(self, value, same_type=False):
|
||||
"""Transform input into an object"""
|
||||
shape = convert.value_to_shape(value)
|
||||
if same_type and not shape.is_empty:
|
||||
if shape.geom_type.lower() != self.geo_type.lower():
|
||||
msg = _(
|
||||
"Geo Value %(geom_type)s must be of the same type %(geo_type)s as fields",
|
||||
geom_type=shape.geom_type.lower(),
|
||||
geo_type=self.geo_type.lower(),
|
||||
)
|
||||
raise TypeError(msg)
|
||||
return shape
|
||||
|
||||
def update_geo_db_column(self, model):
|
||||
"""Update the column type in the database."""
|
||||
cr = model._cr
|
||||
query = """SELECT srid, type, coord_dimension
|
||||
FROM geometry_columns
|
||||
WHERE f_table_name = %s
|
||||
AND f_geometry_column = %s"""
|
||||
cr.execute(query, (model._table, self.name))
|
||||
check_data = cr.fetchone()
|
||||
if not check_data:
|
||||
raise TypeError(
|
||||
_(
|
||||
"geometry_columns table seems to be corrupted."
|
||||
" SRID check is not possible"
|
||||
)
|
||||
)
|
||||
if check_data[0] != self.srid:
|
||||
raise TypeError(
|
||||
_(
|
||||
"Reprojection of column is not implemented."
|
||||
" We can not change srid %(srid)s to %(data)s",
|
||||
srid=self.srid,
|
||||
data=check_data[0],
|
||||
)
|
||||
)
|
||||
elif check_data[1] != self.geo_type.upper():
|
||||
raise TypeError(
|
||||
_(
|
||||
"Geo type modification is not implemented."
|
||||
" We can not change type %(data)s to %(geo_type)s",
|
||||
data=check_data[1],
|
||||
geo_type=self.geo_type.upper(),
|
||||
)
|
||||
)
|
||||
elif check_data[2] != self.dim:
|
||||
raise TypeError(
|
||||
_(
|
||||
"Geo dimention modification is not implemented."
|
||||
" We can not change dimention %(data)s to %(dim)s",
|
||||
data=check_data[2],
|
||||
dim=self.dim,
|
||||
)
|
||||
)
|
||||
if self.gist_index:
|
||||
create_geo_index(cr, model._table, self.name)
|
||||
return True
|
||||
|
||||
def update_db_column(self, model, column):
|
||||
"""Create/update the column corresponding to ``self``.
|
||||
|
||||
For creation of geo column
|
||||
|
||||
:param model: an instance of the field's model
|
||||
:param column: the column's configuration (dict)
|
||||
if it exists, or ``None``
|
||||
"""
|
||||
# the column does not exist, create it
|
||||
|
||||
if not column:
|
||||
create_geo_column(
|
||||
model._cr,
|
||||
model._table,
|
||||
self.name,
|
||||
self.geo_type.upper(),
|
||||
self.srid,
|
||||
self.dim,
|
||||
self.string,
|
||||
)
|
||||
if self.gist_index:
|
||||
create_geo_index(model._cr, model._table, self.name)
|
||||
return
|
||||
|
||||
if column["udt_name"] == self.column_type[0]:
|
||||
return
|
||||
|
||||
self.update_geo_db_column(model)
|
||||
|
||||
if column["udt_name"] in self.column_cast_from:
|
||||
sql.convert_column(model._cr, model._table, self.name, self.column_type[1])
|
||||
else:
|
||||
newname = (self.name + "_moved{}").format
|
||||
i = 0
|
||||
while sql.column_exists(model._cr, model._table, newname(i)):
|
||||
i += 1
|
||||
if column["is_nullable"] == "NO":
|
||||
sql.drop_not_null(model._cr, model._table, self.name)
|
||||
sql.rename_column(model._cr, model._table, self.name, newname(i))
|
||||
sql.create_column(
|
||||
model._cr, model._table, self.name, self.column_type[1], self.string
|
||||
)
|
||||
|
||||
|
||||
class GeoLine(GeoField):
|
||||
"""Field for POSTGIS geometry Line type"""
|
||||
|
||||
type = "geo_line"
|
||||
geo_type = "LineString"
|
||||
|
||||
@classmethod
|
||||
def from_points(cls, cr, point1, point2, srid=None):
|
||||
"""
|
||||
Converts given points in parameter to a line.
|
||||
:param cr: DB cursor
|
||||
:param point1: Point (BaseGeometry)
|
||||
:param point2: Point (BaseGeometry)
|
||||
:param srid: SRID
|
||||
:return: LINESTRING Object
|
||||
"""
|
||||
sql = """
|
||||
SELECT
|
||||
ST_MakeLine(
|
||||
ST_GeomFromText(%(wkt1)s, %(srid)s),
|
||||
ST_GeomFromText(%(wkt2)s, %(srid)s)
|
||||
)
|
||||
"""
|
||||
cr.execute(
|
||||
sql,
|
||||
{
|
||||
"wkt1": point1.wkt,
|
||||
"wkt2": point2.wkt,
|
||||
"srid": srid or cls.srid,
|
||||
},
|
||||
)
|
||||
res = cr.fetchone()
|
||||
return cls.load_geo(res[0])
|
||||
|
||||
|
||||
class GeoPoint(GeoField):
|
||||
"""Field for POSTGIS geometry Point type"""
|
||||
|
||||
type = "geo_point"
|
||||
geo_type = "Point"
|
||||
|
||||
@classmethod
|
||||
def from_latlon(cls, cr, latitude, longitude):
|
||||
"""Convert a (latitude, longitude) into an UTM coordinate Point:"""
|
||||
pt = Point(longitude, latitude)
|
||||
cr.execute(
|
||||
"""
|
||||
SELECT
|
||||
ST_Transform(
|
||||
ST_GeomFromText(%(wkt)s, 4326),
|
||||
%(srid)s)
|
||||
""",
|
||||
{"wkt": pt.wkt, "srid": cls.srid},
|
||||
)
|
||||
res = cr.fetchone()
|
||||
return cls.load_geo(res[0])
|
||||
|
||||
@classmethod
|
||||
def to_latlon(cls, cr, geopoint):
|
||||
"""Convert a UTM coordinate point to (latitude, longitude):"""
|
||||
# Line to execute to retrieve longitude, latitude from UTM in postgres command line:
|
||||
# SELECT ST_X(geom), ST_Y(geom) FROM (SELECT ST_TRANSFORM(ST_SetSRID(
|
||||
# ST_MakePoint(601179.61612, 6399375,681364), 900913), 4326) as geom) g;
|
||||
if isinstance(geopoint, BaseGeometry):
|
||||
geo_point_instance = geopoint
|
||||
else:
|
||||
geo_point_instance = shape(json.loads(geopoint))
|
||||
cr.execute(
|
||||
"""
|
||||
SELECT
|
||||
ST_TRANSFORM(
|
||||
ST_SetSRID(
|
||||
ST_MakePoint(
|
||||
%(coord_x)s, %(coord_y)s
|
||||
),
|
||||
%(srid)s
|
||||
), 4326)""",
|
||||
{
|
||||
"coord_x": geo_point_instance.x,
|
||||
"coord_y": geo_point_instance.y,
|
||||
"srid": cls.srid,
|
||||
},
|
||||
)
|
||||
|
||||
res = cr.fetchone()
|
||||
point_latlon = cls.load_geo(res[0])
|
||||
return point_latlon.x, point_latlon.y
|
||||
|
||||
|
||||
class GeoPolygon(GeoField):
|
||||
"""Field for POSTGIS geometry Polygon type"""
|
||||
|
||||
type = "geo_polygon"
|
||||
geo_type = "Polygon"
|
||||
|
||||
|
||||
class GeoMultiLine(GeoField):
|
||||
"""Field for POSTGIS geometry MultiLine type"""
|
||||
|
||||
type = "geo_multi_line"
|
||||
geo_type = "MultiLineString"
|
||||
|
||||
|
||||
class GeoMultiPoint(GeoField):
|
||||
"""Field for POSTGIS geometry MultiPoint type"""
|
||||
|
||||
type = "geo_multi_point"
|
||||
geo_type = "MultiPoint"
|
||||
|
||||
|
||||
class GeoMultiPolygon(GeoField):
|
||||
"""Field for POSTGIS geometry MultiPolygon type"""
|
||||
|
||||
type = "geo_multi_polygon"
|
||||
geo_type = "MultiPolygon"
|
||||
|
||||
|
||||
fields.GeoLine = GeoLine
|
||||
fields.GeoPoint = GeoPoint
|
||||
fields.GeoPolygon = GeoPolygon
|
||||
fields.GeoMultiLine = GeoMultiLine
|
||||
fields.GeoMultiPoint = GeoMultiPoint
|
||||
fields.GeoMultiPolygon = GeoMultiPolygon
|
||||
Loading…
Add table
Add a link
Reference in a new issue