Initial commit: OCA Technical packages (595 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:03 +02:00
commit 2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions

View file

@ -0,0 +1,45 @@
# Geospatial support for Odoo
Odoo addon: base_geoengine
## Installation
```bash
pip install odoo-bringout-oca-geospatial-base_geoengine
```
## Dependencies
This addon depends on:
- base
- web
## Manifest Information
- **Name**: Geospatial support for Odoo
- **Version**: 16.0.1.2.0
- **Category**: GeoBI
- **License**: AGPL-3
- **Installable**: True
## Source
Based on [OCA/geospatial](https://github.com/OCA/geospatial) branch 16.0, addon `base_geoengine`.
## License
This package maintains the original AGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,307 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
===========================
Geospatial support for Odoo
===========================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:2faa9b7de06a2c3c20d9bc960073bdc6638396bc987e338093331e0099278604
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |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-OCA%2Fgeospatial-lightgray.png?logo=github
:target: https://github.com/OCA/geospatial/tree/16.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-16-0/geospatial-16-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=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
GeoEngine is an Odoo module that adds spatial/GIS capabilites to Odoo. It will allow you to :
* Visualize and query your business information on map
* Perform GeoBI and spatial query
* Configure your spatial layers and spatial datasources
* Extend Odoo models with spatial columns
GeoEngine relies on `OpenLayers <http://openlayers.org>`_ and `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 different spatial layers. The GeoEngine module acts as a data provider and as an OpenLayers configurator.
It also provides a complete extension to Odoo ORM.
**Table of contents**
.. contents::
:local:
Installation
============
To install this module, you need to have `PostGIS <http://postgis.net/>`_ installed.
On Ubuntu::
.. 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>`_
When you will install the module this two additional libs will be installed.
For a complete documentation please refer to the `public documenation <http://oca.github.io/geospatial/index.html>`_
Usage
=====
Geoengine Demo
==============
1. As a user/admin, when I am in the Geoengine Demo module and I go to the ZIP menu.
When I click on an item in the list view, I get to the form view showing me the different
information about the ZIP. We can see its ZIP, city, priority, total sales and his spatial
representation.
2. As a user, I can't modify the information in the form view.
3. As an admin, I can modify the information in the form view. I can click on the bin button to clear
the map and I can draw a new shape.
4. As a user, when I go the "Retail machines" tab and there are no items to display, it does not
show me anything.
5. As an admin, when I go the "Retail machines" tab and there are no items to display, the list view of
the retail machines suggests to me to add a new line.
6. As a user/admin, if there are items to be displayed in the "Retail machines" tab then I can click on an
item and the retail machines form view will be displayed. We can see its spatial representation by going
to "The point" tab and its attributes in "Attributes" tab.
7. As a user/admin, when I go to the geoengine zip view by clicking on the map button at the top right of the
screen. The geoengine view appears with the first 80 results displayed on the map. The vector layers
selected are those defined as "active on startup" by the admin. The selected raster layer is the first
one that is not an overlay layer.
8. As a user/admin, when I hover over an area on the map, the area changes its style.
9. As a user/admin, when I click on an area, a popup appears an I can see the different information about the
area. If I click on the cross, the popup will disappear. If I click somewhere else on the map, the
popup will also disappear. If I click on the about button, then the form view will be displayed.
10. As a user/admin, when I use the paging system, then the results displayed on the map are different
(corresponding to the request).
11. As a user/admin, if we use the search bar, we can search results by his zip or his city.
12. As an admin, if I change the sequence of layers with the handle button then the change are persisted in database.
13. As a user, if I change the sequence of layers with the handle button then the change are not persisted in database.
There are just the changes in the display.
14. As an admin, if I change the domain of a layer with the filter button then the change are persisted in database.
15. As a user, if I change the domain of a layer with the filter button then the change are not persisted in database.
There are just the changes in the display.
16. As an admin, I have the possibility to edit the layer with its corresponding button.
17. As a user/admin, I can open/close LayerPanel with its button.
18. As a user/admin, I can open/close RecordsPanel with its button.
19. As a user/admin, when I click on a record in RecordsPanel, a move is made on the map to the selected record.
20. As a user/admin, when I click on a record in RecordsPanel, I can also click on the left magnifying glass to zoom on the record.
21. As a user/admin, when I click on a record in RecordsPanel, I can also click on the right magnifying glass to get the original zoom.
22. As a user/admin, I can use the search bar to search in the RecordsPanel.
23. As an admin,If the geoengine view is in edit mode, I can create new records by drawing them in the view.
24. As an admin, If the geoengine view is in edit mode, I can modify its spatial representation.
Geoengine Backend
=================
1. As an admin, if I go into the configuration of the raster layers and it has elements, I can click
on one and see its information.
2. As an admin, if I want to create a new raster layer, I can click on "NEW" and fill out the form. The
required fields for OpenStreetMap type are "Layer Name" and "Related View". If we want to have a
WMTS (Web Map Tile Service) raster type. The required fields in addition to the precedents are "Service URL",
"Matrix set","Format", "Projection" and "Resolutions". If we take WMS (Web Map Service) raster type, then the
required fields are "Layer Name", "Related View", "Service URL", "Params", "Server Type".
3. As an admin,if I go into the configuration of the vector layers and it has elements, I can click
on one and see its information.
4. As an admin, if I want to create a new vector layer, I can click on "NEW" and fill out the form. The
required fields are "Layer Name", "Related View", "Geo field" and "Representation mode".
Changelog
=========
16.0.1.0.0 (2023-03-20)
========================
* LayerSwitcher has been removed as it was not really practical. A LayerPanel is now active.
* The geo_search method is now deprecated and replaced by the standard odoo search method.
* The widget "geo_edit_map" attribute is no longer necessary as the field is automatically detected by
his type. We can also provide an option attribute that allows us to pass an opacity and a color as
parameters.
.. code-block:: xml
<form>
<notebook colspan="4">
<page string="Geometry">
<field name="the_geom" options="{'opacity': 0.8, 'color': '#0000FF' }" />
</page>
</notebook>
</form>
* The method geo_search is now deprecated. We now need to use the standard odoo search method.
.. code-block:: python
obj.search([("the_point","geo_intersect",{"dummy.zip.the_geom": [("id", "=", rec.id)]})])
* We can now pass to the geoengine view a template to display the information we want
to see when clicking on a feature.
.. code-block:: xml
<geoengine>
<field name="name" />
<field name="city" />
<field name="total_sales" />
<field name="the_geom" />
<templates>
<t t-name="info_box">
<field name="city" widget="badge" />
<ul>
<li>ZIP : <field name="name" />
</li>
<li>Total Sales: <field name="total_sales" />
</li>
</ul>
</t>
</templates>
</geoengine>
* We can now pass a model to use to a layer to display other information on the map.
.. code-block:: xml
<record id="geoengine_vector_layer_hs_retail_machines" model="geoengine.vector.layer">
<field name="model_id" ref="base_geoengine_demo.model_geoengine_demo_automatic_retailing_machine"/>
<field name="model_domain">[('state', '=', 'hs')]</field>
<field name="geo_field_id" ref="base_geoengine_demo.field_geoengine_demo_automatic_retailing_machine__the_point"/>
<field name="name">HS retail machines</field>
<field name="view_id" ref="ir_ui_view_resbetterzipgeoview0" />
<field name="geo_repr">basic</field>
<field name="attribute_field_id" ref="base_geoengine_demo.field_geoengine_demo_automatic_retailing_machine__name"/>
<field name="begin_color">#FF0000</field>
<field name="display_polygon_labels" eval="0" />
<field name="layer_opacity">0.8</field>
</record>
* There is some new features in the LayerPanel.
1. If you are logged in as an admin, you have the possibility to edit the layer by clicking on the edit button. This will open a dialog box.
Changes will appear in real time on the view.
2. If you are logged in as an admin, you can also change the domain of the layer. If you are logged in as a user, changes will not be
persisted in the database. Changes will appear in real time on the view.
3. If you are logged in as an admin, you can also change the sequence of the layers by sliding them over each other. If you are logged in as a user, changes will not be
persisted in the database.
* Widget domain is now implemented for geo field This means that the geo-operators are also implemented and that there is the possibility to add a sub-domain.
If we want to add a domain that includes all the records that are displayed in the geoengine view (active_ids). We can use the two new operators :
"in active_ids" and "not in active_ids". These will automatically replace the marker with ids. Note that the widget will indicate that the domain is invalid
because of the marker.
* Creation of the RecordsPanel. This panel allows you to retrieve all active records. You can click on record to get the movement to the selected record.
Two magnifying glass are also available. You can click on the left one to zoom on the record. You can click on the right one to get the original zoom.
* A search bar is also available. It allows you to perform a search into the RecordsPanel.
* A button to open/close the panels is also available.
* The module has been translated in French.
* Now you can now make the geoengine view editable. Simply add editable attribute in the geoengine view.
.. code-block:: xml
<geoengine editable="1">
<field name="name" />
<field name="city" />
<field name="total_sales" />
<field name="the_geom" />
<field name="display_name" />
<templates>
<t t-name="info_box">
<field name="city" widget="badge" />
<ul>
<li>ZIP : <field name="name" />
</li>
<li>Total Sales: <field name="total_sales" />
</li>
</ul>
</t>
</templates>
</geoengine>
Thanks to that, you can create new records by drawing them directly in the geoengine view. You can also edit record in the same view.
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:%2016.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
~~~~~~~
* Camptocamp
* ACSONE SA/NV
Contributors
~~~~~~~~~~~~
* Nicolas Bessi <nicolas.bessi@camptocamp.com>
* Frederic Junod <frederic.junod@camptocamp.com>
* Yannick Payot <yannick.payot@camptocamp.com>
* Sandy Carter <sandy.carter@savoirfairelinux.com>
* Laurent Mignon <laurent.mignon@acsone.eu>
* Jonathan Nemry <jonathan.nemry@acsone.eu>
* David Lasley <dave@dlasley.net>
* Daniel Reis <dgreis@sapo.pt>
* Matthieu Dietrich <matthieu.dietrich@camptocamp.com>
* Alan Ramos <alan.ramos@jarsa.com.mx>
* Damien Crier <damien.crier@camptocamp.com>
* Cyril Gaudin <cyril.gaudin@camptocamp.com>
* Pierre Verkest <pverkest@anybox.fr>
* Benjamin Willig <benjamin.willig@acsone.eu>
* Devendra Kavthekar <dkatodoo@gmail.com>
* Emanuel Cino <ecino@compassion.ch>
* Thomas Nowicki <thomas.nowicki@camptocamp.com>
* Alexandre Saunier <alexandre.saunier@camptocamp.com>
* Sandip Mangukiya <smangukiya@opensourceintegrators.com>
* Samuel Kouff <s.kouff@student.helmo.be>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
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/16.0/base_geoengine>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1,6 @@
from . import models
from . import expressions
from . import fields
from . import geo_convertion_helper
from . import geo_operators
from .geo_db import init_postgis

View file

@ -0,0 +1,36 @@
# Copyright 2011-2015 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).
{
"name": "Geospatial support for Odoo",
"version": "16.0.1.2.0",
"category": "GeoBI",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "https://github.com/OCA/geospatial",
"depends": ["base", "web"],
"data": [
"security/data.xml",
"views/base_geoengine_view.xml",
"views/ir_model_view.xml",
"views/ir_view_view.xml",
"views/geo_raster_layer_view.xml",
"views/geo_vector_layer_view.xml",
"security/ir.model.access.csv",
],
"assets": {
"web.assets_backend": [
"base_geoengine/static/src/js/**/*",
"base_geoengine/static/src/css/style.css",
"web/static/src/libs/fontawesome/css/font-awesome.css",
("include", "web._assets_helpers"),
"web/static/src/scss/pre_variables.scss",
"web/static/lib/bootstrap/scss/_variables.scss",
("include", "web._assets_bootstrap"),
]
},
"external_dependencies": {"python": ["shapely", "geojson", "simplejson"]},
"installable": True,
"pre_init_hook": "init_postgis",
}

View file

@ -0,0 +1,138 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import random
import string
from odoo.osv import expression
from odoo.osv.expression import TERM_OPERATORS
from odoo.tools import Query
from .fields import GeoField
from .geo_operators import GeoOperator
original__leaf_to_sql = expression.expression._expression__leaf_to_sql
GEO_OPERATORS = {
"geo_greater": ">",
"geo_lesser": "<",
"geo_equal": "=",
"geo_touch": "ST_Touches",
"geo_within": "ST_Within",
"geo_contains": "ST_Contains",
"geo_intersect": "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)
def __leaf_to_sql(self, leaf, model, alias):
"""
This method has been monkey patched in order to be able to include
geo_operators into the Odoo search method.
"""
left, operator, right = leaf
if isinstance(leaf, (list, tuple)):
current_field = model._fields.get(left)
current_operator = GeoOperator(current_field)
if current_field and isinstance(current_field, GeoField):
params = []
if isinstance(right, dict):
# We are having indirect geo_operator like (geom, geo_...,
# {res.zip.poly: [id, in, [1,2,3]] })
ref_search = right
sub_queries = []
for key in ref_search:
i = key.rfind(".")
rel_model = key[0:i]
rel_col = key[i + 1 :]
rel_model = model.env[rel_model]
# we compute the attributes search on spatial rel
if ref_search[key]:
rel_alias = (
rel_model._table
+ "_"
+ "".join(random.choices(string.ascii_lowercase, k=5))
)
rel_query = where_calc(
rel_model,
ref_search[key],
active_test=True,
alias=rel_alias,
)
model._apply_ir_rules(rel_query, "read")
if operator == "geo_equal":
rel_query.add_where(
f'"{alias}"."{left}" {GEO_OPERATORS[operator]} '
f"{rel_alias}.{rel_col}"
)
elif operator in ("geo_greater", "geo_lesser"):
rel_query.add_where(
f"ST_Area({alias}.{left}) {GEO_OPERATORS[operator]} "
f"ST_Area({rel_alias}.{rel_col})"
)
else:
rel_query.add_where(
f'{GEO_OPERATORS[operator]}("{alias}"."{left}", '
f"{rel_alias}.{rel_col})"
)
subquery, subparams = rel_query.subselect("1")
sub_queries.append(f"EXISTS({subquery})")
params += subparams
query = " AND ".join(sub_queries)
else:
query = get_geo_func(
current_operator, operator, left, right, params, model._table
)
return query, params
return original__leaf_to_sql(self, leaf=leaf, model=model, alias=alias)
def get_geo_func(current_operator, operator, left, right, params, table):
"""
This method will call the SQL query corresponding to the requested geo operator
"""
if operator == "geo_greater":
query = current_operator.get_geo_greater_sql(table, left, right, params)
elif operator == "geo_lesser":
query = current_operator.get_geo_lesser_sql(table, left, right, params)
elif operator == "geo_equal":
query = current_operator.get_geo_equal_sql(table, left, right, params)
elif operator == "geo_touch":
query = current_operator.get_geo_touch_sql(table, left, right, params)
elif operator == "geo_within":
query = current_operator.get_geo_within_sql(table, left, right, params)
elif operator == "geo_contains":
query = current_operator.get_geo_contains_sql(table, left, right, params)
elif operator == "geo_intersect":
query = current_operator.get_geo_intersect_sql(table, left, right, params)
else:
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.
"""
# if the object has an active field ('active', 'x_active'), filter out all
# inactive records unless they were explicitly asked for
if model._active_name and active_test and model._context.get("active_test", True):
# the item[0] trick below works for domain items and '&'/'|'/'!'
# operators too
if not any(item[0] == model._active_name for item in domain):
domain = [(model._active_name, "=", 1)] + domain
query = Query(model.env.cr, alias, model._table)
if domain:
return expression.expression(domain, model, alias=alias, query=query).query
return query
expression.expression._expression__leaf_to_sql = __leaf_to_sql

View file

@ -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

View file

@ -0,0 +1,42 @@
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import _
try:
import geojson
from shapely import wkb, wkt
from shapely.geometry import shape
from shapely.geometry.base import BaseGeometry
except ImportError:
logger = logging.getLogger(__name__)
logger.warning(_("Shapely or geojson are not available in the sys path"))
def value_to_shape(value, use_wkb=False):
"""Transforms input into a Shapely object"""
if not value:
return wkt.loads("GEOMETRYCOLLECTION EMPTY")
if isinstance(value, str):
# We try to do this before parsing json exception
# exception are ressource costly
if "{" in value:
geo_dict = geojson.loads(value)
return shape(geo_dict)
elif use_wkb:
return wkb.loads(value, hex=True)
else:
return wkt.loads(value)
elif hasattr(value, "wkt"):
if isinstance(value, BaseGeometry):
return value
else:
return wkt.loads(value.wkt)
else:
raise TypeError(
_(
"Write/create/search geo type must be wkt/geojson "
"string or must respond to wkt"
)
)

View file

@ -0,0 +1,92 @@
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
"""Helper to setup Postgis"""
import logging
from odoo import _
from odoo.exceptions import MissingError
from odoo.tools import sql
logger = logging.getLogger("geoengine.sql")
_schema = logging.getLogger("odoo.schema")
def init_postgis(cr):
"""Initialize postgis
Add PostGIS support to the database. PostGIS is a spatial database
extender for PostgreSQL object-relational database. It adds support for
geographic objects allowing location queries to be run in SQL.
"""
cr.execute(
"""
SELECT
tablename
FROM
pg_tables
WHERE
tablename='spatial_ref_sys';
"""
)
check = cr.fetchone()
if check:
return {}
try:
cr.execute(
"""
CREATE EXTENSION postgis;
CREATE EXTENSION postgis_topology;
"""
)
except Exception as exc:
raise MissingError(
_(
"Error, can not automatically initialize spatial postgis"
" support. Database user may have to be superuser and"
" postgres/postgis extensions with their devel header have"
" to be installed. If you do not want Odoo to connect with a"
" super user you can manually prepare your database. To do"
" this, open a client to your database using a super user and"
" run:\n"
"CREATE EXTENSION postgis;\n"
"CREATE EXTENSION postgis_topology;\n"
)
) from exc
def create_geo_column(cr, tablename, columnname, geotype, srid, dim, comment=None):
"""Create a geometry column with the given type.
:params: srid: geometry's projection srid
:params: dim: geometry's dimension (2D or 3D)
"""
cr.execute(
"SELECT AddGeometryColumn( %s, %s, %s, %s, %s)",
(tablename, columnname, srid, geotype, dim),
)
if comment:
# pylint: disable=E8103
cr.execute(
'COMMENT ON COLUMN "{}"."{}" IS %s'.format(tablename, columnname),
(comment,),
)
_schema.debug(
"Table %r: added geometry column %r of type %s", tablename, columnname, geotype
)
def _postgis_index_name(table, col_name):
return "{}_{}_gist_index".format(table, col_name)
def create_geo_index(cr, tablename, columnname):
"""Create the given index unless it exists."""
indexname = _postgis_index_name(tablename, columnname)
if sql.index_exists(cr, indexname):
return
# pylint: disable=E8103
cr.execute(
"CREATE INDEX {} ON {} USING GIST ( {} )".format(
indexname, tablename, columnname
)
)
_schema.debug("Table %r: created index %r", tablename, indexname)

View file

@ -0,0 +1,77 @@
# Copyright 2011-2012 Nicolas Bessi (Camptocamp SA)
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
class GeoOperator(object):
def __init__(self, geo_field):
self.geo_field = geo_field
def _get_direct_como_op_sql(self, table, col, value, params, op=""):
"""provide raw sql for geater and lesser operators"""
if isinstance(value, (int, float)):
return " ST_Area({}.{}) {} {}".format(table, col, op, value)
else:
base = self.geo_field.entry_to_shape(value, same_type=False)
params.append(base.wkt)
return " ST_Area({}.{}) {} ST_Area(ST_GeomFromText(%s))".format(
table, col, op
)
def _get_postgis_comp_sql(self, table, col, value, params, op=""):
"""return raw sql for all search based on St_**(a, b) posgis operator"""
base = self.geo_field.entry_to_shape(value, same_type=False)
srid = self.geo_field.srid
params.append(base.wkt)
params.append(srid)
return f"{op}({table}.{col}, ST_GeomFromText(%s, %s))"
def get_geo_greater_sql(self, table, col, value, params):
"""Returns raw sql for geo_greater operator
(used for area comparison)
"""
return self._get_direct_como_op_sql(table, col, value, params, op=">")
def get_geo_lesser_sql(self, table, col, value, params):
"""Returns raw sql for geo_lesser operator
(used for area comparison)"""
return self._get_direct_como_op_sql(table, col, value, params, op="<")
def get_geo_equal_sql(
self,
table,
col,
value,
params,
):
"""Returns raw sql for geo_equal operator
(used for equality comparison)
"""
base = self.geo_field.entry_to_shape(value, same_type=False)
compare_to = "ST_GeomFromText(%s)"
params.append(base.wkt)
return " {}.{} = {}".format(table, col, compare_to)
def get_geo_intersect_sql(self, table, col, value, params):
"""Returns raw sql for geo_intersec operator
(used for spatial comparison)
"""
return self._get_postgis_comp_sql(table, col, value, params, op="ST_Intersects")
def get_geo_touch_sql(self, table, col, value, params):
"""Returns raw sql for geo_touch operator
(used for spatial comparison)
"""
return self._get_postgis_comp_sql(table, col, value, params, op="ST_Touches")
def get_geo_within_sql(self, table, col, value, params):
"""Returns raw sql for geo_within operator
(used for spatial comparison)
"""
return self._get_postgis_comp_sql(table, col, value, params, op="ST_Within")
def get_geo_contains_sql(self, table, col, value, params):
"""Returns raw sql for geo_contains operator
(used for spatial comparison)
"""
return self._get_postgis_comp_sql(table, col, value, params, op="ST_Contains")

View file

@ -0,0 +1,971 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * base_geoengine
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "%s column does not exists or is not a geo field"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__active_on_startup
msgid "Active On Startup"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__attribute_field_id
msgid "Attribute field"
msgstr ""
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_base
msgid "Base"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__basic
msgid "Basic"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__begin_color
msgid "Begin color class"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Classification"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__classification
msgid "Classification mode"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__type
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__code
msgid "Code"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__colored
msgid "Colored range/Chroma.js"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Colors"
msgstr ""
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_base_view_menu
msgid "Configuration"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__gist_index
msgid "Create gist index"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__create_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__create_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__create_uid
msgid "Created by"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__create_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__create_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__create_date
msgid "Created on"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__custom
msgid "Custom"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__default_extent
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__default_extent
msgid "Default map extent"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__default_zoom
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__default_zoom
msgid "Default map zoom"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__params
msgid "Dictiorary of values for dimensions as JSON"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__dimensions
msgid "Dimensions"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.xml:0
#, python-format
msgid "Discard"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__display_polygon_labels
msgid "Display Labels on Polygon"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__display_name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__display_name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__display_name
msgid "Display Name"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__d_wms
msgid "Distant WMS"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.esm.js:0
#, python-format
msgid "Domain editing"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.esm.js:0
#, python-format
msgid "Editing vector layer"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__end_color
msgid "End color class"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_db.py:0
#, python-format
msgid ""
"Error, can not automatically initialize spatial postgis support. Database user may have to be superuser and postgres/postgis extensions with their devel header have to be installed. If you do not want Odoo to connect with a super user you can manually prepare your database. To do this, open a client to your database using a super user and run:\n"
"CREATE EXTENSION postgis;\n"
"CREATE EXTENSION postgis_topology;\n"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__ttype
msgid "Field Type"
msgstr ""
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_ir_model_fields
msgid "Fields"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__format_suffix
msgid "Format"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "General"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo Value %(geom_type)s must be of the same type %(geo_type)s as fields"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo dimention modification is not implemented. We can not change dimention "
"%(data)s to %(dim)s"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__geo_field_id
msgid "Geo field"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo type modification is not implemented. We can not change type %(data)s to"
" %(geo_type)s"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_ui_view__type__geoengine
msgid "GeoEngine"
msgstr ""
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_base_menu
msgid "GeoEngine Backend"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "GeoEngine Data"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_view.esm.js:0
#, python-format
msgid "Geoengine"
msgstr ""
#. module: base_geoengine
#: model:res.groups,name:base_geoengine.group_geoengine_admin
msgid "Geoengine Admin"
msgstr ""
#. module: base_geoengine
#: model:res.groups,name:base_geoengine.group_geoengine_user
msgid "Geoengine User"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__has_type
msgid "Has Type"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__id
msgid "ID"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__interval
msgid "Interval"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__is_wms
msgid "Is Wms"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__is_wmts
msgid "Is Wmts"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__overlay
msgid "Is overlay layer?"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__linestring
msgid "LINESTRING"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer____last_update
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type____last_update
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer____last_update
msgid "Last Modified on"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__write_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__write_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__write_uid
msgid "Last Updated by"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__write_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__write_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__write_date
msgid "Last Updated on"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__type_id
msgid "Layer"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__name
msgid "Layer Name"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__layer_opacity
msgid "Layer Opacity"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__sequence
msgid "Layer Priority"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Layer data"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__readonly
msgid "Layer is read only"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__sequence
msgid "Layer priority"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__active_on_startup
msgid "Layer will be shown on startup if checked."
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Layers ("
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__dimensions
msgid "List of dimensions separated by ','"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multilinestring
msgid "MULTILINESTRING"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multipoint
msgid "MULTIPOINT"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multipolygon
msgid "MULTIPOLYGON"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__matrix_set
msgid "Matrix set"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__max_extent
msgid "Max extent"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_arch_parser.esm.js:0
#, python-format
msgid "Missing ${INFO_BOX_ATTRIBUTE} template."
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_name
msgid "Model"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_domain
msgid "Model Domain"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_id
msgid "Model to use"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_view_id
msgid "Model view"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__name
msgid "Name"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__params_wms
msgid "Need to provide at least a LAYERS param"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid "New record"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "No GeoEngine view defined for the model %s"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "No raster layer for view %s"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__nb_class
msgid "Number of class"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_renderer/geoengine_renderer.xml:0
#, python-format
msgid "OPEN"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__odoo
msgid "Odoo field"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Odoo layer data (Not implemented)"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__field_id
msgid "Odoo layer field to use"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__osm
msgid "OpenStreetMap"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__point
msgid "POINT"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__polygon
msgid "POLYGON"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__params
msgid "Params"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__params_wms
msgid "Params WMS"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "Please create a view or modify view mode"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__dim
msgid "PostGIs Dimension"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__geo_type
msgid "PostGIs type"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__projection
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__projection
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__projection
msgid "Projection"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__proportion
msgid "Proportional Symbol"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__quantile
msgid "Quantile"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "Raster (Background layers)"
msgstr ""
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_rater_action
#: model:ir.model,name:base_geoengine.model_geoengine_raster_layer
msgid "Raster Layer"
msgstr ""
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_raster_layer_menu
msgid "Raster Layer Management"
msgstr ""
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_geoengine_raster_layer_type
msgid "Raster Layer Type"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Raster Layer View"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__raster_type
msgid "Raster layer type"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__raster_layer_ids
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__raster_layer_ids
msgid "Raster layers"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Rasters"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/records_panel.xml:0
#, python-format
msgid "Records ("
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Related Model"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__view_id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__view_id
msgid "Related View"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Representation"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__geo_repr
msgid "Representation mode"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Reprojection of column is not implemented. We can not change srid %(srid)s "
"to %(data)s"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__request_encoding
msgid "Request encoding"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__resolutions
msgid "Resolutions"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__restricted_extent
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__restricted_extent
msgid "Restricted map extent"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.xml:0
#, python-format
msgid "Save"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/search_bar_records/search_bar_records.xml:0
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/search_bar_records/search_bar_records.xml:0
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/search_bar_records/search_bar_records.xml:0
#, python-format
msgid "Search..."
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__server_type
msgid "Server Type"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__service
msgid "Service"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__url
msgid "Service URL"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_convertion_helper.py:0
#, python-format
msgid "Shapely or geojson are not available in the sys path"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field_input/domain_selector_geo_field_input.esm.js:0
#, python-format
msgid "Subdomain"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "The geo_field_id must be a field in %s model"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__server_type
msgid ""
"The type of the remote WMS server: mapserver, geoserver, carmentaserver, or "
"qgis"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__unique
msgid "Unique value"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__units
msgid "Units"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__use_to_edit
msgid "Use to edit"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Vector"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "Vector (Active layers)"
msgstr ""
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_raster_action
#: model:ir.actions.act_window,name:base_geoengine.geo_vector_geoengine_view_action
#: model:ir.model,name:base_geoengine.model_geoengine_vector_layer
msgid "Vector Layer"
msgstr ""
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_vector_layer_menu
msgid "Vector Layer Management"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__vector_layer_ids
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__vector_layer_ids
msgid "Vector layers"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Vectors"
msgstr ""
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_ir_ui_view
msgid "View"
msgstr ""
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_view_menu
msgid "View Management"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__type
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__type
msgid "View Type"
msgstr ""
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_action
msgid "Views"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "WMS options"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__wmts
msgid "WMTS"
msgstr ""
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "WMTS options"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid "Warning"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_convertion_helper.py:0
#, python-format
msgid ""
"Write/create/search geo type must be wkt/geojson string or must respond to "
"wkt"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid ""
"You are about to create a new record without having displayed all the "
"others. A risk of overlap could occur. Would you like to continue ?"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "You must at least provide one of domain or geo_domain"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "You need to provide an attribute that exists in %s model"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "You need to select a numeric field"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__projection
msgid "eg. EPSG:21781"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__request_encoding
msgid "eg. REST"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__units
msgid "eg. m"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__format_suffix
msgid "eg. png"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_contains"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_equal"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_greater"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_intersect"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_lesser"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_line
msgid "geo_line"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_line
msgid "geo_multi_line"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_point
msgid "geo_multi_point"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_polygon
msgid "geo_multi_polygon"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_point
msgid "geo_point"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_polygon
msgid "geo_polygon"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "geo_search is deprecated: uses search method defined on base model"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_touch"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_within"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"geometry_columns table seems to be corrupted. SRID check is not possible"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__begin_color
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__end_color
msgid "hex value"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_number_field/domain_selector_number_field.esm.js:0
#, python-format
msgid "in active_ids"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_number_field/domain_selector_number_field.esm.js:0
#, python-format
msgid "not in active_ids"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__srid
msgid "srid"
msgstr ""

View file

@ -0,0 +1,971 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * base_geoengine
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "%s column does not exists or is not a geo field"
msgstr "%s kolona ne postoji ili nije geo polje"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__active_on_startup
msgid "Active On Startup"
msgstr "Aktivan pri pokretanju"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__attribute_field_id
msgid "Attribute field"
msgstr "Polje atributa"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_base
msgid "Base"
msgstr "Osnova"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__basic
msgid "Basic"
msgstr "Osnovno"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__begin_color
msgid "Begin color class"
msgstr "Početna klasa boja"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Classification"
msgstr "Klasifikacija"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__classification
msgid "Classification mode"
msgstr "Režim klasifikacije"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__type
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__code
msgid "Code"
msgstr "Šifra"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__colored
msgid "Colored range/Chroma.js"
msgstr "Obojeni raspon/Chroma.js"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Colors"
msgstr "Boje"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_base_view_menu
msgid "Configuration"
msgstr "Konfiguracija"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__gist_index
msgid "Create gist index"
msgstr "Kreiraj gist indeks"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__create_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__create_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__create_uid
msgid "Created by"
msgstr "Kreirao"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__create_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__create_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__create_date
msgid "Created on"
msgstr "Kreirano"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__custom
msgid "Custom"
msgstr "Prilagođeno"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__default_extent
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__default_extent
msgid "Default map extent"
msgstr "Zadani opseg karte"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__default_zoom
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__default_zoom
msgid "Default map zoom"
msgstr "Zadani zoom karte"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__params
msgid "Dictiorary of values for dimensions as JSON"
msgstr "Rječnik vrijednosti za dimenzije kao JSON"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__dimensions
msgid "Dimensions"
msgstr "Dimenzije"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.xml:0
#, python-format
msgid "Discard"
msgstr "Napusti"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__display_polygon_labels
msgid "Display Labels on Polygon"
msgstr "Prikaži labele na poligonu"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__display_name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__display_name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__display_name
msgid "Display Name"
msgstr "Prikazani naziv"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__d_wms
msgid "Distant WMS"
msgstr "Udaljeni WMS"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.esm.js:0
#, python-format
msgid "Domain editing"
msgstr "Uređivanje domena"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.esm.js:0
#, python-format
msgid "Editing vector layer"
msgstr "Uređivanje vektorskog sloja"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__end_color
msgid "End color class"
msgstr "Završna klasa boja"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_db.py:0
#, python-format
msgid ""
"Error, can not automatically initialize spatial postgis support. Database user may have to be superuser and postgres/postgis extensions with their devel header have to be installed. If you do not want Odoo to connect with a super user you can manually prepare your database. To do this, open a client to your database using a super user and run:\n"
"CREATE EXTENSION postgis;\n"
"CREATE EXTENSION postgis_topology;\n"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__ttype
msgid "Field Type"
msgstr "Tip polja"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_ir_model_fields
msgid "Fields"
msgstr "Polja"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__format_suffix
msgid "Format"
msgstr "Format"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "General"
msgstr "Opšte"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo Value %(geom_type)s must be of the same type %(geo_type)s as fields"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo dimention modification is not implemented. We can not change dimention "
"%(data)s to %(dim)s"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__geo_field_id
msgid "Geo field"
msgstr "Geo polje"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo type modification is not implemented. We can not change type %(data)s to"
" %(geo_type)s"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_ui_view__type__geoengine
msgid "GeoEngine"
msgstr "GeoEngine"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_base_menu
msgid "GeoEngine Backend"
msgstr "GeoEngine Backend"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "GeoEngine Data"
msgstr "GeoEngine podaci"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_view.esm.js:0
#, python-format
msgid "Geoengine"
msgstr "Geoengine"
#. module: base_geoengine
#: model:res.groups,name:base_geoengine.group_geoengine_admin
msgid "Geoengine Admin"
msgstr "Geoengine administrator"
#. module: base_geoengine
#: model:res.groups,name:base_geoengine.group_geoengine_user
msgid "Geoengine User"
msgstr "Geoengine korisnik"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__has_type
msgid "Has Type"
msgstr "Ima tip"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__id
msgid "ID"
msgstr "ID"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__interval
msgid "Interval"
msgstr "Razdoblje"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__is_wms
msgid "Is Wms"
msgstr "Je Wms"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__is_wmts
msgid "Is Wmts"
msgstr "Je Wmts"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__overlay
msgid "Is overlay layer?"
msgstr "Je prekrivni sloj?"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__linestring
msgid "LINESTRING"
msgstr "LINESTRING"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer____last_update
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type____last_update
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer____last_update
msgid "Last Modified on"
msgstr "Zadnje mijenjano"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__write_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__write_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__write_uid
msgid "Last Updated by"
msgstr "Zadnji ažurirao"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__write_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__write_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__write_date
msgid "Last Updated on"
msgstr "Zadnje ažurirano"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__type_id
msgid "Layer"
msgstr "Sloj"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__name
msgid "Layer Name"
msgstr "Naziv sloja"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__layer_opacity
msgid "Layer Opacity"
msgstr "Neprozirnost sloja"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__sequence
msgid "Layer Priority"
msgstr "Prioritet sloja"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Layer data"
msgstr "Podaci sloja"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__readonly
msgid "Layer is read only"
msgstr "Sloj je samo za čitanje"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__sequence
msgid "Layer priority"
msgstr "Prioritet sloja"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__active_on_startup
msgid "Layer will be shown on startup if checked."
msgstr "Sloj će biti prikazan pri pokretanju ako je označeno."
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Layers ("
msgstr "Slojevi ("
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__dimensions
msgid "List of dimensions separated by ','"
msgstr "Lista dimenzija odvojena sa ','"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multilinestring
msgid "MULTILINESTRING"
msgstr "MULTILINESTRING"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multipoint
msgid "MULTIPOINT"
msgstr "MULTIPOINT"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multipolygon
msgid "MULTIPOLYGON"
msgstr "MULTIPOLYGON"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__matrix_set
msgid "Matrix set"
msgstr "Matrica set"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__max_extent
msgid "Max extent"
msgstr "Maksimalni opseg"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_arch_parser.esm.js:0
#, python-format
msgid "Missing ${INFO_BOX_ATTRIBUTE} template."
msgstr "Nedostaje ${INFO_BOX_ATTRIBUTE} šablon."
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_name
msgid "Model"
msgstr "Model"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_domain
msgid "Model Domain"
msgstr "Domen modela"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_id
msgid "Model to use"
msgstr "Model za korištenje"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_view_id
msgid "Model view"
msgstr "Pogled modela"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__name
msgid "Name"
msgstr "Naziv:"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__params_wms
msgid "Need to provide at least a LAYERS param"
msgstr "Trebate pružiti najmanje LAYERS parametar"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid "New record"
msgstr "Novi zapis"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "No GeoEngine view defined for the model %s"
msgstr "Nije definisan GeoEngine pogled za model %s"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "No raster layer for view %s"
msgstr "Nema rasterskog sloja za pogled %s"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__nb_class
msgid "Number of class"
msgstr "Broj klasa"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_renderer/geoengine_renderer.xml:0
#, python-format
msgid "OPEN"
msgstr "OTVORI"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__odoo
msgid "Odoo field"
msgstr "Odoo polje"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Odoo layer data (Not implemented)"
msgstr "Odoo podaci sloja (nije implementirano)"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__field_id
msgid "Odoo layer field to use"
msgstr "Odoo polje sloja za korištenje"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__osm
msgid "OpenStreetMap"
msgstr "OpenStreetMap"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__point
msgid "POINT"
msgstr "POINT"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__polygon
msgid "POLYGON"
msgstr "POLYGON"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__params
msgid "Params"
msgstr "Parametri"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__params_wms
msgid "Params WMS"
msgstr "Parametri WMS"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "Please create a view or modify view mode"
msgstr "Molimo kreirajte pogled ili modificirajte režim pogleda"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__dim
msgid "PostGIs Dimension"
msgstr "PostGIs dimenzija"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__geo_type
msgid "PostGIs type"
msgstr "PostGIs tip"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__projection
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__projection
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__projection
msgid "Projection"
msgstr "Projekcija"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__proportion
msgid "Proportional Symbol"
msgstr "Proporcionalni simbol"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__quantile
msgid "Quantile"
msgstr "Kvantil"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "Raster (Background layers)"
msgstr "Raster (pozadinski slojevi)"
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_rater_action
#: model:ir.model,name:base_geoengine.model_geoengine_raster_layer
msgid "Raster Layer"
msgstr "Rasterski sloj"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_raster_layer_menu
msgid "Raster Layer Management"
msgstr "Upravljanje rasterskim slojem"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_geoengine_raster_layer_type
msgid "Raster Layer Type"
msgstr "Tip rasterskog sloja"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Raster Layer View"
msgstr "Pogled rasterskog sloja"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__raster_type
msgid "Raster layer type"
msgstr "Tip rasterskog sloja"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__raster_layer_ids
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__raster_layer_ids
msgid "Raster layers"
msgstr "Rasterski slojevi"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Rasters"
msgstr "Rasteri"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/records_panel.xml:0
#, python-format
msgid "Records ("
msgstr "Zapisi ("
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Related Model"
msgstr "Kreirano"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__view_id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__view_id
msgid "Related View"
msgstr "Povezani pogled"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Representation"
msgstr "Reprezentacija"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__geo_repr
msgid "Representation mode"
msgstr "Režim reprezentacije"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Reprojection of column is not implemented. We can not change srid %(srid)s "
"to %(data)s"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__request_encoding
msgid "Request encoding"
msgstr "Kodiranje zahtjeva"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__resolutions
msgid "Resolutions"
msgstr "Rezolucije"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__restricted_extent
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__restricted_extent
msgid "Restricted map extent"
msgstr "Ograničeni opseg karte"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.xml:0
#, python-format
msgid "Save"
msgstr "Spremi"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/search_bar_records/search_bar_records.xml:0
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/search_bar_records/search_bar_records.xml:0
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/search_bar_records/search_bar_records.xml:0
#, python-format
msgid "Search..."
msgstr "Pretraži..."
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__server_type
msgid "Server Type"
msgstr "Tip poslužitelja"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__service
msgid "Service"
msgstr "Usluga"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__url
msgid "Service URL"
msgstr "URL servisa"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_convertion_helper.py:0
#, python-format
msgid "Shapely or geojson are not available in the sys path"
msgstr "Shapely ili geojson nisu dostupni u sys path"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field_input/domain_selector_geo_field_input.esm.js:0
#, python-format
msgid "Subdomain"
msgstr "Poddomen"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "The geo_field_id must be a field in %s model"
msgstr "geo_field_id mora biti polje u %s modelu"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__server_type
msgid ""
"The type of the remote WMS server: mapserver, geoserver, carmentaserver, or "
"qgis"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__unique
msgid "Unique value"
msgstr "Jedinstvena vrijednost"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__units
msgid "Units"
msgstr "Jedinice"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__use_to_edit
msgid "Use to edit"
msgstr "Koristi za uređivanje"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Vector"
msgstr "Vektor"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "Vector (Active layers)"
msgstr "Vektor (aktivni slojevi)"
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_raster_action
#: model:ir.actions.act_window,name:base_geoengine.geo_vector_geoengine_view_action
#: model:ir.model,name:base_geoengine.model_geoengine_vector_layer
msgid "Vector Layer"
msgstr "Vektorski sloj"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_vector_layer_menu
msgid "Vector Layer Management"
msgstr "Upravljanje vektorskim slojem"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__vector_layer_ids
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__vector_layer_ids
msgid "Vector layers"
msgstr "Vektorski slojevi"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Vectors"
msgstr "Vektori"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_ir_ui_view
msgid "View"
msgstr "Pregled"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_view_menu
msgid "View Management"
msgstr "Upravljanje pogledima"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__type
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__type
msgid "View Type"
msgstr "Vrsta pregleda"
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_action
msgid "Views"
msgstr "Pogledi"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "WMS options"
msgstr "WMS opcije"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__wmts
msgid "WMTS"
msgstr "WMTS"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "WMTS options"
msgstr "WMTS opcije"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid "Warning"
msgstr "Upozorenje"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_convertion_helper.py:0
#, python-format
msgid ""
"Write/create/search geo type must be wkt/geojson string or must respond to "
"wkt"
msgstr ""
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid ""
"You are about to create a new record without having displayed all the "
"others. A risk of overlap could occur. Would you like to continue ?"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "You must at least provide one of domain or geo_domain"
msgstr "Morate pružiti najmanje jedan od domena ili geo_domena"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "You need to provide an attribute that exists in %s model"
msgstr "Trebate pružiti atribut koji postoji u %s modelu"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "You need to select a numeric field"
msgstr "Trebate odabrati numeričko polje"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__projection
msgid "eg. EPSG:21781"
msgstr "npr. EPSG:21781"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__request_encoding
msgid "eg. REST"
msgstr "npr. REST"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__units
msgid "eg. m"
msgstr "npr. m"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__format_suffix
msgid "eg. png"
msgstr "npr. png"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_contains"
msgstr "geo_contains"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_equal"
msgstr "geo_equal"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_greater"
msgstr "geo_greater"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_intersect"
msgstr "geo_intersect"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_lesser"
msgstr "geo_lesser"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_line
msgid "geo_line"
msgstr "geo_line"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_line
msgid "geo_multi_line"
msgstr "geo_multi_line"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_point
msgid "geo_multi_point"
msgstr "geo_multi_point"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_polygon
msgid "geo_multi_polygon"
msgstr "geo_multi_polygon"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_point
msgid "geo_point"
msgstr "geo_point"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_polygon
msgid "geo_polygon"
msgstr "geo_polygon"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "geo_search is deprecated: uses search method defined on base model"
msgstr "geo_search je zastarjelo: koristi search metodu definisanu na osnovnom modelu"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_touch"
msgstr "geo_touch"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_within"
msgstr "geo_within"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"geometry_columns table seems to be corrupted. SRID check is not possible"
msgstr ""
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__begin_color
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__end_color
msgid "hex value"
msgstr "hex vrijednost"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_number_field/domain_selector_number_field.esm.js:0
#, python-format
msgid "in active_ids"
msgstr "u active_ids"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_number_field/domain_selector_number_field.esm.js:0
#, python-format
msgid "not in active_ids"
msgstr "nije u active_ids"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__srid
msgid "srid"
msgstr "srid"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,998 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * base_geoengine
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-07-28 09:25+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.4\n"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "%s column does not exists or is not a geo field"
msgstr "La colonna %s non esiste, o non è un campo geo"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__active_on_startup
msgid "Active On Startup"
msgstr "Attivo all'avvio"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__attribute_field_id
msgid "Attribute field"
msgstr "Campo attributo"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_base
msgid "Base"
msgstr "Base"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__basic
msgid "Basic"
msgstr "Base"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__begin_color
msgid "Begin color class"
msgstr "Classe colore inizio"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Classification"
msgstr "Classificazione"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__classification
msgid "Classification mode"
msgstr "Modo di classificazione"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__type
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__code
msgid "Code"
msgstr "Codice"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__colored
msgid "Colored range/Chroma.js"
msgstr "Intervallo colorato/Chroma.js"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Colors"
msgstr "Colori"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_base_view_menu
msgid "Configuration"
msgstr "Configurazione"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__gist_index
msgid "Create gist index"
msgstr "Crea indice gist"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__create_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__create_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__create_uid
msgid "Created by"
msgstr "Creato da"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__create_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__create_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__create_date
msgid "Created on"
msgstr "Creato il"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__custom
msgid "Custom"
msgstr "Personalizzato"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__default_extent
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__default_extent
msgid "Default map extent"
msgstr "Estensione predefinita della mappa"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__default_zoom
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__default_zoom
msgid "Default map zoom"
msgstr "Zoom predefinito della mappa"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__params
msgid "Dictiorary of values for dimensions as JSON"
msgstr "Dizionario JSON di valori delle dimensioni"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__dimensions
msgid "Dimensions"
msgstr "Dimensioni"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.xml:0
#, python-format
msgid "Discard"
msgstr "Abbandona"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__display_polygon_labels
msgid "Display Labels on Polygon"
msgstr "Mostra etichette sul poligono"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__display_name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__display_name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__display_name
msgid "Display Name"
msgstr "Nome visualizzato"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__d_wms
msgid "Distant WMS"
msgstr "WMS remoto"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.esm.js:0
#, python-format
msgid "Domain editing"
msgstr "Modifica dminio"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.esm.js:0
#, python-format
msgid "Editing vector layer"
msgstr "Modifica livello vettore"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__end_color
msgid "End color class"
msgstr "Classe colore fine"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_db.py:0
#, python-format
msgid ""
"Error, can not automatically initialize spatial postgis support. Database "
"user may have to be superuser and postgres/postgis extensions with their "
"devel header have to be installed. If you do not want Odoo to connect with a "
"super user you can manually prepare your database. To do this, open a client "
"to your database using a super user and run:\n"
"CREATE EXTENSION postgis;\n"
"CREATE EXTENSION postgis_topology;\n"
msgstr ""
"Errore, impossibile inizializzare automaticamente il supporto spaziale "
"postgis. L'utente database deve essere superutente e le estensioni postgres/"
"postgis con le loro intestazioni devono essere installate. Se non si vuole "
"che Odoo si connetta con un super utente si può preparare manualmente il "
"database. Per questo, aprire un client verso il database utilizzando un "
"super utente ed eseguendo:\n"
"CREATE EXTENSION postgis;\n"
"CREATE EXTENSION postgis_topology;\n"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__ttype
msgid "Field Type"
msgstr "Tipo campo"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_ir_model_fields
msgid "Fields"
msgstr "Campi"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__format_suffix
msgid "Format"
msgstr "Formato"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "General"
msgstr "Generale"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid "Geo Value %(geom_type)s must be of the same type %(geo_type)s as fields"
msgstr ""
"Il valore geo %(geom_type)s deve essere dello stesso tipo %(geo_type)s come "
"i campi"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo dimention modification is not implemented. We can not change dimention "
"%(data)s to %(dim)s"
msgstr ""
"La modifica della dimensione geo non è implementata. Non si possono cambiare "
"le dimensioni %(data)s a %(dim)s"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__geo_field_id
msgid "Geo field"
msgstr "Campo geo"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo type modification is not implemented. We can not change type %(data)s to "
"%(geo_type)s"
msgstr ""
"La modifica del tipo geo non è implementata. Non si può cambiare il tipo "
"%(data)s a %(geo_type)s"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_ui_view__type__geoengine
msgid "GeoEngine"
msgstr "GeoEngine"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_base_menu
msgid "GeoEngine Backend"
msgstr "Backend GeoEngine"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "GeoEngine Data"
msgstr "Dati GeoEngine"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_view.esm.js:0
#, python-format
msgid "Geoengine"
msgstr "GeoEngine"
#. module: base_geoengine
#: model:res.groups,name:base_geoengine.group_geoengine_admin
msgid "Geoengine Admin"
msgstr "Amministratore Geoengine"
#. module: base_geoengine
#: model:res.groups,name:base_geoengine.group_geoengine_user
msgid "Geoengine User"
msgstr "Utente GeoEngine"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__has_type
msgid "Has Type"
msgstr "Ha tipo"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__id
msgid "ID"
msgstr "ID"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__interval
msgid "Interval"
msgstr "Intervallo"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__is_wms
msgid "Is Wms"
msgstr "È WMS"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__is_wmts
msgid "Is Wmts"
msgstr "È WMTS"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__overlay
msgid "Is overlay layer?"
msgstr "È livello in sovraimpressione?"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__linestring
msgid "LINESTRING"
msgstr "LINESTRING"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer____last_update
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type____last_update
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__write_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__write_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__write_uid
msgid "Last Updated by"
msgstr "Ultimo aggiornamento di"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__write_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__write_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__write_date
msgid "Last Updated on"
msgstr "Ultimo aggiornamento il"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__type_id
msgid "Layer"
msgstr "Livello"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__name
msgid "Layer Name"
msgstr "Nome livello"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__layer_opacity
msgid "Layer Opacity"
msgstr "Opacità ivello"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__sequence
msgid "Layer Priority"
msgstr "Priorità livello"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Layer data"
msgstr "Dati ivello"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__readonly
msgid "Layer is read only"
msgstr "Il ivello è in sola lettura"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__sequence
msgid "Layer priority"
msgstr "Priorità livello"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__active_on_startup
msgid "Layer will be shown on startup if checked."
msgstr "Il livello verrà mostrato all'avvio se selezionato."
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Layers ("
msgstr "Livelli ("
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__dimensions
msgid "List of dimensions separated by ','"
msgstr "Elenco dimensioni separate da ','"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multilinestring
msgid "MULTILINESTRING"
msgstr "MULTILINESTRING"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multipoint
msgid "MULTIPOINT"
msgstr "MULTIPOINT"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multipolygon
msgid "MULTIPOLYGON"
msgstr "MULTIPOLYGON"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__matrix_set
msgid "Matrix set"
msgstr "Set matrice"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__max_extent
msgid "Max extent"
msgstr "Estensione massima"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_arch_parser.esm.js:0
#, python-format
msgid "Missing ${INFO_BOX_ATTRIBUTE} template."
msgstr "Modello ${INFO_BOX_ATTRIBUTE} non trovato."
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_name
msgid "Model"
msgstr "Modello"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_domain
msgid "Model Domain"
msgstr "Dominio modello"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_id
msgid "Model to use"
msgstr "Modello da usare"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_view_id
msgid "Model view"
msgstr "Vista modello"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__name
msgid "Name"
msgstr "Nome"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__params_wms
msgid "Need to provide at least a LAYERS param"
msgstr "Serve fornire almeno un parametro LAYERS"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid "New record"
msgstr "Nuovo record"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "No GeoEngine view defined for the model %s"
msgstr "Nessuna vista GeoEngine definita per il modello %s"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "No raster layer for view %s"
msgstr "Nessuno livello raster per la vista %s"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__nb_class
msgid "Number of class"
msgstr "Numero di classi"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_renderer/geoengine_renderer.xml:0
#, python-format
msgid "OPEN"
msgstr "OPEN"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__odoo
msgid "Odoo field"
msgstr "Campo Odoo"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Odoo layer data (Not implemented)"
msgstr "Livello dati Odoo (non implementato)"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__field_id
msgid "Odoo layer field to use"
msgstr "Livello campo Odoo da usare"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__osm
msgid "OpenStreetMap"
msgstr "OpenStreetMap"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__point
msgid "POINT"
msgstr "POINT"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__polygon
msgid "POLYGON"
msgstr "POLYGON"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__params
msgid "Params"
msgstr "Parametri"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__params_wms
msgid "Params WMS"
msgstr "Parametri WMS"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "Please create a view or modify view mode"
msgstr "Creare una vista o modificare la modalità di visualizzazione"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__dim
msgid "PostGIs Dimension"
msgstr "Dimensione PostGIs"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__geo_type
msgid "PostGIs type"
msgstr "Tipo PostGIs"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__projection
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__projection
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__projection
msgid "Projection"
msgstr "Proiezione"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__proportion
msgid "Proportional Symbol"
msgstr "Simbolo proporzionale"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__quantile
msgid "Quantile"
msgstr "Quantile"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "Raster (Background layers)"
msgstr "Raster (livello di sfondo)"
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_rater_action
#: model:ir.model,name:base_geoengine.model_geoengine_raster_layer
msgid "Raster Layer"
msgstr "Livello raster"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_raster_layer_menu
msgid "Raster Layer Management"
msgstr "Gestione livello raster"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_geoengine_raster_layer_type
msgid "Raster Layer Type"
msgstr "Tipo livello raster"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Raster Layer View"
msgstr "Vista livello raster"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__raster_type
msgid "Raster layer type"
msgstr "Tipo livello raster"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__raster_layer_ids
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__raster_layer_ids
msgid "Raster layers"
msgstr "Livelli raster"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Rasters"
msgstr "Raster"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/records_panel.xml:0
#, python-format
msgid "Records ("
msgstr "Record ("
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Related Model"
msgstr "Modello correlato"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__view_id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__view_id
msgid "Related View"
msgstr "Viste correlata"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Representation"
msgstr "Rappresentazione"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__geo_repr
msgid "Representation mode"
msgstr "Modalità di rappresentazione"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Reprojection of column is not implemented. We can not change srid %(srid)s "
"to %(data)s"
msgstr ""
"Riproiezione della colonna nn implementata. Non si può modificare srid "
"%(srid)s a %(data)s"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__request_encoding
msgid "Request encoding"
msgstr "Codifica richiesta"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__resolutions
msgid "Resolutions"
msgstr "Risoluzione"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__restricted_extent
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__restricted_extent
msgid "Restricted map extent"
msgstr "Estensione ristretta della mappa"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.xml:0
#, python-format
msgid "Save"
msgstr "Salva"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/search_bar_records/search_bar_records.xml:0
#, python-format
msgid "Search..."
msgstr "Cerca..."
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__server_type
msgid "Server Type"
msgstr "Tipo server"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__service
msgid "Service"
msgstr "Servizio"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__url
msgid "Service URL"
msgstr "URL servizio"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_convertion_helper.py:0
#, python-format
msgid "Shapely or geojson are not available in the sys path"
msgstr "Shapely o geojson non sono disponibili nel path di sistema"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field_input/domain_selector_geo_field_input.esm.js:0
#, python-format
msgid "Subdomain"
msgstr "Sottodominio"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "The geo_field_id must be a field in %s model"
msgstr "Il geo_field_id deve essere un campo nel modello %s"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__server_type
msgid ""
"The type of the remote WMS server: mapserver, geoserver, carmentaserver, or "
"qgis"
msgstr ""
"Il tipo sel server WMS remoto: mapserver, geoserver, carmentaserver, o qgis"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__unique
msgid "Unique value"
msgstr "Valore unico"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__units
msgid "Units"
msgstr "Unità"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__use_to_edit
msgid "Use to edit"
msgstr "Usa per modificare"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Vector"
msgstr "Vettore"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "Vector (Active layers)"
msgstr "Vettore (livelli attivi)"
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_raster_action
#: model:ir.actions.act_window,name:base_geoengine.geo_vector_geoengine_view_action
#: model:ir.model,name:base_geoengine.model_geoengine_vector_layer
msgid "Vector Layer"
msgstr "Livello vettoriale"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_vector_layer_menu
msgid "Vector Layer Management"
msgstr "Gestione livello vettoriale"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__vector_layer_ids
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__vector_layer_ids
msgid "Vector layers"
msgstr "Livelli vettoriali"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Vectors"
msgstr "Vettori"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_ir_ui_view
msgid "View"
msgstr "Vista"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_view_menu
msgid "View Management"
msgstr "Gestione vista"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__type
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__type
msgid "View Type"
msgstr "Tipo vista"
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_action
msgid "Views"
msgstr "Viste"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "WMS options"
msgstr "Opzioni WMS"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__wmts
msgid "WMTS"
msgstr "WMTS"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "WMTS options"
msgstr "Opzioni WMTS"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid "Warning"
msgstr "Attenzione"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_convertion_helper.py:0
#, python-format
msgid ""
"Write/create/search geo type must be wkt/geojson string or must respond to "
"wkt"
msgstr ""
"Il tipo geo scrittura/creazione/ricerca deve essere una stringa wkt/geojson "
"odeve rispondere a wkt"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid ""
"You are about to create a new record without having displayed all the "
"others. A risk of overlap could occur. Would you like to continue ?"
msgstr ""
"Si sta per creare un nuovo record senza aver disabilitato tutti gli altri. "
"C'è il rischio di sovrapposizione. Si vuole continuare?"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "You must at least provide one of domain or geo_domain"
msgstr "Bisogna fornire almeno un dominio o un geo_domain"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "You need to provide an attribute that exists in %s model"
msgstr "Bisogna fornire un attributo che esista nel modello %s"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "You need to select a numeric field"
msgstr "Bisogna selezionare un campo numerico"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__projection
msgid "eg. EPSG:21781"
msgstr "es. EPSG:21781"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__request_encoding
msgid "eg. REST"
msgstr "es. REST"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__units
msgid "eg. m"
msgstr "es. m"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__format_suffix
msgid "eg. png"
msgstr "es. png"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_contains"
msgstr "geo_contains"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_equal"
msgstr "geo_equal"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_greater"
msgstr "geo_greater"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_intersect"
msgstr "geo_intersect"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_lesser"
msgstr "geo_lesser"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_line
msgid "geo_line"
msgstr "geo_line"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_line
msgid "geo_multi_line"
msgstr "geo_multi_line"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_point
msgid "geo_multi_point"
msgstr "geo_multi_point"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_polygon
msgid "geo_multi_polygon"
msgstr "geo_multi_polygon"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_point
msgid "geo_point"
msgstr "geo_point"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_polygon
msgid "geo_polygon"
msgstr "geo_polygon"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "geo_search is deprecated: uses search method defined on base model"
msgstr ""
"geo_search è deprecat: usare il motodo ricerca definito nel modello base"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_touch"
msgstr "geo_touch"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_within"
msgstr "geo_within"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"geometry_columns table seems to be corrupted. SRID check is not possible"
msgstr ""
"la tabella geometry_columns sembra corrotta.Il controllo SRID non è possibile"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__begin_color
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__end_color
msgid "hex value"
msgstr "valore esadecimale"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_number_field/domain_selector_number_field.esm.js:0
#, python-format
msgid "in active_ids"
msgstr "in active_ids"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_number_field/domain_selector_number_field.esm.js:0
#, python-format
msgid "not in active_ids"
msgstr "not in active_ids"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__srid
msgid "srid"
msgstr "srid"

View file

@ -0,0 +1,994 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * base_geoengine
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-06-19 12:51+0000\n"
"Last-Translator: jakobkrabbe <jakob@syscare.se>\n"
"Language-Team: none\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "%s column does not exists or is not a geo field"
msgstr "%s kolumnen finns inte eller är inte ett geofält"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__active_on_startup
msgid "Active On Startup"
msgstr "Aktiv vid uppstart"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__attribute_field_id
msgid "Attribute field"
msgstr "Attributfält"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_base
msgid "Base"
msgstr "Bas"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__basic
msgid "Basic"
msgstr "Grundläggande"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__begin_color
msgid "Begin color class"
msgstr "Börja färgklass"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Classification"
msgstr "Klassificering"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__classification
msgid "Classification mode"
msgstr "Klassificeringsläge"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__type
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__code
msgid "Code"
msgstr "Kod"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__colored
msgid "Colored range/Chroma.js"
msgstr "Färgat område/Chroma.js"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Colors"
msgstr "Färger"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_base_view_menu
msgid "Configuration"
msgstr "Konfiguration"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__gist_index
msgid "Create gist index"
msgstr "Skapa huvudindex"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__create_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__create_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__create_uid
msgid "Created by"
msgstr "Skapad av"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__create_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__create_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__create_date
msgid "Created on"
msgstr "Skapad på"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__custom
msgid "Custom"
msgstr "Anpassad"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__default_extent
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__default_extent
msgid "Default map extent"
msgstr "Standard kartomfattning"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__default_zoom
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__default_zoom
msgid "Default map zoom"
msgstr "Standard kartzoom"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__params
msgid "Dictiorary of values for dimensions as JSON"
msgstr "Ordbok med värden för dimensioner som JSON"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__dimensions
msgid "Dimensions"
msgstr "Dimensioner"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.xml:0
#, python-format
msgid "Discard"
msgstr "Kassera"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__display_polygon_labels
msgid "Display Labels on Polygon"
msgstr "Visa etiketter på polygon"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__display_name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__display_name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__display_name
msgid "Display Name"
msgstr "Visningsnamn"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__d_wms
msgid "Distant WMS"
msgstr "Avlägsen WMS"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.esm.js:0
#, python-format
msgid "Domain editing"
msgstr "Domänredigering"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.esm.js:0
#, python-format
msgid "Editing vector layer"
msgstr "Redigera vektorlager"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__end_color
msgid "End color class"
msgstr "Avsluta färgklass"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_db.py:0
#, python-format
msgid ""
"Error, can not automatically initialize spatial postgis support. Database "
"user may have to be superuser and postgres/postgis extensions with their "
"devel header have to be installed. If you do not want Odoo to connect with a "
"super user you can manually prepare your database. To do this, open a client "
"to your database using a super user and run:\n"
"CREATE EXTENSION postgis;\n"
"CREATE EXTENSION postgis_topology;\n"
msgstr ""
"Fel, kan inte automatiskt initiera spatial postgis-stöd. Databasanvändare "
"kan behöva vara superanvändare och postgres/postgis-tillägg med deras "
"utvecklingshuvud måste installeras. Om du inte vill att Odoo ska ansluta "
"till en superanvändare kan du manuellt förbereda din databas. För att göra "
"detta, öppna en klient till din databas med en superanvändare och kör:\n"
"CREATE EXTENSION postgis;\n"
"CREATE EXTENSION postgis_topology;\n"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__ttype
msgid "Field Type"
msgstr "Fälttyp"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_ir_model_fields
msgid "Fields"
msgstr "Fält"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__format_suffix
msgid "Format"
msgstr "Format"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "General"
msgstr "Generell"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid "Geo Value %(geom_type)s must be of the same type %(geo_type)s as fields"
msgstr "Geovärde %(geom_type)s måste vara av samma typ %(geo_type)s som fält"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo dimention modification is not implemented. We can not change dimention "
"%(data)s to %(dim)s"
msgstr ""
"Modifiering av geografisk dimension är inte implementerad. Vi kan inte ändra "
"dimension %(data)s till %(dim)s"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__geo_field_id
msgid "Geo field"
msgstr "Geofält"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Geo type modification is not implemented. We can not change type %(data)s to "
"%(geo_type)s"
msgstr ""
"Ändring av geotyp är inte implementerad. Vi kan inte ändra typ %(data)s till "
"%(geo_type)s"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_ui_view__type__geoengine
msgid "GeoEngine"
msgstr "GeoMotor"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_base_menu
msgid "GeoEngine Backend"
msgstr "GeoMotor Backend"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "GeoEngine Data"
msgstr "GeoMotor data"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_view.esm.js:0
#, python-format
msgid "Geoengine"
msgstr "Geomotor"
#. module: base_geoengine
#: model:res.groups,name:base_geoengine.group_geoengine_admin
msgid "Geoengine Admin"
msgstr "Geomotor Admin"
#. module: base_geoengine
#: model:res.groups,name:base_geoengine.group_geoengine_user
msgid "Geoengine User"
msgstr "Geomotor användare"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__has_type
msgid "Has Type"
msgstr "Har typ"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__id
msgid "ID"
msgstr "ID"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__interval
msgid "Interval"
msgstr "Intervall"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__is_wms
msgid "Is Wms"
msgstr "Är Wms"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__is_wmts
msgid "Is Wmts"
msgstr "Är Wmts"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__overlay
msgid "Is overlay layer?"
msgstr "Är överläggslager?"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__linestring
msgid "LINESTRING"
msgstr "LINESTRING"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer____last_update
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type____last_update
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer____last_update
msgid "Last Modified on"
msgstr "Senast modifierad på"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__write_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__write_uid
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__write_uid
msgid "Last Updated by"
msgstr "Senast uppdaterad av"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__write_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__write_date
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__write_date
msgid "Last Updated on"
msgstr "Senast uppdaterad på"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__type_id
msgid "Layer"
msgstr "Lager"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__name
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__name
msgid "Layer Name"
msgstr "Lagernamn"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__layer_opacity
msgid "Layer Opacity"
msgstr "Lageropacitet"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__sequence
msgid "Layer Priority"
msgstr "Lagerprioritet"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Layer data"
msgstr "Lagerdata"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__readonly
msgid "Layer is read only"
msgstr "Layer är skrivskyddad"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__sequence
msgid "Layer priority"
msgstr "Lagerprioritet"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__active_on_startup
msgid "Layer will be shown on startup if checked."
msgstr "Lager kommer att visas vid start om det är markerat."
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Layers ("
msgstr "Lager ("
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__dimensions
msgid "List of dimensions separated by ','"
msgstr "Lista över dimensioner avgränsade med ','"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multilinestring
msgid "MULTILINESTRING"
msgstr "MULTILINJESTRING"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multipoint
msgid "MULTIPOINT"
msgstr "MULTIPOINT"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__multipolygon
msgid "MULTIPOLYGON"
msgstr "MULTIPOLYGON"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__matrix_set
msgid "Matrix set"
msgstr "Matris set"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__max_extent
msgid "Max extent"
msgstr "Max omfattning"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_arch_parser.esm.js:0
#, python-format
msgid "Missing ${INFO_BOX_ATTRIBUTE} template."
msgstr "${INFO_BOX_ATTRIBUTE} mall saknas."
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_name
msgid "Model"
msgstr "Modell"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_domain
msgid "Model Domain"
msgstr "Modelldomän"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_id
msgid "Model to use"
msgstr "Modell att använda"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__model_view_id
msgid "Model view"
msgstr "Modellvy"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__name
msgid "Name"
msgstr "Namn"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__params_wms
msgid "Need to provide at least a LAYERS param"
msgstr "Behöver tillhandahålla minst en LAGER parameter"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid "New record"
msgstr "Ny post"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "No GeoEngine view defined for the model %s"
msgstr "Ingen GeoMotor-vy definierad för modellen %s"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "No raster layer for view %s"
msgstr "Inget rasterlager för vyn %s"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__nb_class
msgid "Number of class"
msgstr "Nummer av klass"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_renderer/geoengine_renderer.xml:0
#, python-format
msgid "OPEN"
msgstr "ÖPPNA"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__odoo
msgid "Odoo field"
msgstr "Odoofält"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Odoo layer data (Not implemented)"
msgstr "Odoo lagerdata (inte implementerad)"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__field_id
msgid "Odoo layer field to use"
msgstr "Odoo lagerfält att använda"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__osm
msgid "OpenStreetMap"
msgstr "OpenStreetMap"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__point
msgid "POINT"
msgstr "PEKA"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__geo_type__polygon
msgid "POLYGON"
msgstr "POLYGON"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__params
msgid "Params"
msgstr "Parametrar"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__params_wms
msgid "Params WMS"
msgstr ""
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "Please create a view or modify view mode"
msgstr "Vänligen skapa en vy eller ändra visningsläge"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__dim
msgid "PostGIs Dimension"
msgstr "PostGIs dimension"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__geo_type
msgid "PostGIs type"
msgstr "PostGIs typ"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__projection
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__projection
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__projection
msgid "Projection"
msgstr "Projektion"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__geo_repr__proportion
msgid "Proportional Symbol"
msgstr "Proportionell symbol"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__quantile
msgid "Quantile"
msgstr "Kvantil"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "Raster (Background layers)"
msgstr "Raster (bakgrundslager)"
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_rater_action
#: model:ir.model,name:base_geoengine.model_geoengine_raster_layer
msgid "Raster Layer"
msgstr "Raster lager"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_raster_layer_menu
msgid "Raster Layer Management"
msgstr "Rasterlagerhantering"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_geoengine_raster_layer_type
msgid "Raster Layer Type"
msgstr "Rasterlagertyp"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "Raster Layer View"
msgstr "Rasterlagervy"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__raster_type
msgid "Raster layer type"
msgstr "Rasterlagertyp"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__raster_layer_ids
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__raster_layer_ids
msgid "Raster layers"
msgstr "Rasterlager"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Rasters"
msgstr "Raster"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/records_panel.xml:0
#, python-format
msgid "Records ("
msgstr "Poster ("
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Related Model"
msgstr "Relaterad modell"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__view_id
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__view_id
msgid "Related View"
msgstr "Relaterad vy"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_geoengine_view_form
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Representation"
msgstr "Representation"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_vector_layer__geo_repr
msgid "Representation mode"
msgstr "Representationsläge"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"Reprojection of column is not implemented. We can not change srid %(srid)s "
"to %(data)s"
msgstr ""
"Omprojektering av kolumn är inte implementerad. Vi kan inte ändra srid "
"%(srid)s till %(data)s"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__request_encoding
msgid "Request encoding"
msgstr "Begär kodning"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__resolutions
msgid "Resolutions"
msgstr "Upplösningar"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__restricted_extent
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__restricted_extent
msgid "Restricted map extent"
msgstr "Begränsad kartutsträckning"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.xml:0
#, python-format
msgid "Save"
msgstr "Spara"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/records_panel/search_bar_records/search_bar_records.xml:0
#, python-format
msgid "Search..."
msgstr "Sök..."
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__server_type
msgid "Server Type"
msgstr "Servertyp"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer_type__service
msgid "Service"
msgstr "Service"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__url
msgid "Service URL"
msgstr "URL för tjänsten"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_convertion_helper.py:0
#, python-format
msgid "Shapely or geojson are not available in the sys path"
msgstr "Shapely eller geojson är inte tillgängliga i sys-sökvägen"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field_input/domain_selector_geo_field_input.esm.js:0
#, python-format
msgid "Subdomain"
msgstr "Subdomän"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "The geo_field_id must be a field in %s model"
msgstr "Värdet i geo_field_id fältet måste vara i %s modellen"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__server_type
msgid ""
"The type of the remote WMS server: mapserver, geoserver, carmentaserver, or "
"qgis"
msgstr ""
"Typen av fjärr-WMS-servern: mapserver, geoserver, carmentaserver eller qgis"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_vector_layer__classification__unique
msgid "Unique value"
msgstr "Unikt värde"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__units
msgid "Units"
msgstr "Enheter"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_geoengine_raster_layer__use_to_edit
msgid "Use to edit"
msgstr "Använd för att redigera"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_vector_view_form
msgid "Vector"
msgstr "Vektor"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geoengine_view_form
msgid "Vector (Active layers)"
msgstr "Vektor (aktivt lager)"
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_raster_action
#: model:ir.actions.act_window,name:base_geoengine.geo_vector_geoengine_view_action
#: model:ir.model,name:base_geoengine.model_geoengine_vector_layer
msgid "Vector Layer"
msgstr "Vektor lager"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_vector_layer_menu
msgid "Vector Layer Management"
msgstr "Vektor lagerhantering"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__vector_layer_ids
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__vector_layer_ids
msgid "Vector layers"
msgstr "Vektor lager"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/layers_panel/layers_panel.xml:0
#, python-format
msgid "Vectors"
msgstr "Vektorer"
#. module: base_geoengine
#: model:ir.model,name:base_geoengine.model_ir_ui_view
msgid "View"
msgstr "Vy"
#. module: base_geoengine
#: model:ir.ui.menu,name:base_geoengine.geoengine_view_menu
msgid "View Management"
msgstr "Visningshanterare"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_ui_view__type
#: model:ir.model.fields,field_description:base_geoengine.field_website_page__type
msgid "View Type"
msgstr "Vytyp"
#. module: base_geoengine
#: model:ir.actions.act_window,name:base_geoengine.geo_engine_view_action
msgid "Views"
msgstr "Vyer"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "WMS options"
msgstr "WMS alternativ"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__geoengine_raster_layer__raster_type__wmts
msgid "WMTS"
msgstr "WMTS"
#. module: base_geoengine
#: model_terms:ir.ui.view,arch_db:base_geoengine.geo_raster_view_form
msgid "WMTS options"
msgstr "WMTS alternativ"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid "Warning"
msgstr "Varning"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/geo_convertion_helper.py:0
#, python-format
msgid ""
"Write/create/search geo type must be wkt/geojson string or must respond to "
"wkt"
msgstr ""
"Skriv/skapa/sök geotyp måste vara wkt/geojson-sträng eller måste svara på wkt"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/views/geoengine/geoengine_controller/geoengine_controller.esm.js:0
#, python-format
msgid ""
"You are about to create a new record without having displayed all the "
"others. A risk of overlap could occur. Would you like to continue ?"
msgstr ""
"Du är på väg att skapa en ny post utan att ha visat alla de andra. Det kan "
"uppstå risk för överlappning. Vill du fortsätta?"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "You must at least provide one of domain or geo_domain"
msgstr "Du måste åtminstone tillhandahålla en av domän eller geo_domän"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "You need to provide an attribute that exists in %s model"
msgstr "Du behöver ange ett attribut som finns i %s model"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/geo_vector_layer.py:0
#, python-format
msgid "You need to select a numeric field"
msgstr "Du måste välja ett numeriskt fält"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__projection
msgid "eg. EPSG:21781"
msgstr "t.ex. EPSG:21781"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__request_encoding
msgid "eg. REST"
msgstr "t.ex. REST"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__units
msgid "eg. m"
msgstr "t.ex. m"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_raster_layer__format_suffix
msgid "eg. png"
msgstr "t.ex. png"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_contains"
msgstr "geo_innehåller"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_equal"
msgstr "geo_jämlik"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_greater"
msgstr "geo_större"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_intersect"
msgstr "geo_intersect"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_lesser"
msgstr "geo_lesser"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_line
msgid "geo_line"
msgstr "geo_linje"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_line
msgid "geo_multi_line"
msgstr "geo_multi_line"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_point
msgid "geo_multi_point"
msgstr "geo_multi_punkt"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_multi_polygon
msgid "geo_multi_polygon"
msgstr "geo_multi_polygon"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_point
msgid "geo_point"
msgstr "geo_punkt"
#. module: base_geoengine
#: model:ir.model.fields.selection,name:base_geoengine.selection__ir_model_fields__ttype__geo_polygon
msgid "geo_polygon"
msgstr "geo_polygon"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/models/base.py:0
#, python-format
msgid "geo_search is deprecated: uses search method defined on base model"
msgstr ""
"geo_search är föråldrad: använder sökmetod som definieras i basmodellen"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_touch"
msgstr "geo_touch"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_geo_field/domain_selector_geo_field.esm.js:0
#, python-format
msgid "geo_within"
msgstr "geo_inom"
#. module: base_geoengine
#. odoo-python
#: code:addons/base_geoengine/fields.py:0
#, python-format
msgid ""
"geometry_columns table seems to be corrupted. SRID check is not possible"
msgstr ""
"geometry_columns-tabellen verkar vara skadad. SRID-kontroll är inte möjlig"
#. module: base_geoengine
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__begin_color
#: model:ir.model.fields,help:base_geoengine.field_geoengine_vector_layer__end_color
msgid "hex value"
msgstr "hex-värde"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_number_field/domain_selector_number_field.esm.js:0
#, python-format
msgid "in active_ids"
msgstr "i aktiva_id"
#. module: base_geoengine
#. odoo-javascript
#: code:addons/base_geoengine/static/src/js/widgets/domain_selector_geo_field/domain_selector_number_field/domain_selector_number_field.esm.js:0
#, python-format
msgid "not in active_ids"
msgstr "inte i active_ids"
#. module: base_geoengine
#: model:ir.model.fields,field_description:base_geoengine.field_ir_model_fields__srid
msgid "srid"
msgstr "srid"

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View file

@ -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

View file

@ -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)

View file

@ -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"

View file

@ -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 = ""

View file

@ -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,
)

View file

@ -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")

View file

@ -0,0 +1,20 @@
* Nicolas Bessi <nicolas.bessi@camptocamp.com>
* Frederic Junod <frederic.junod@camptocamp.com>
* Yannick Payot <yannick.payot@camptocamp.com>
* Sandy Carter <sandy.carter@savoirfairelinux.com>
* Laurent Mignon <laurent.mignon@acsone.eu>
* Jonathan Nemry <jonathan.nemry@acsone.eu>
* David Lasley <dave@dlasley.net>
* Daniel Reis <dgreis@sapo.pt>
* Matthieu Dietrich <matthieu.dietrich@camptocamp.com>
* Alan Ramos <alan.ramos@jarsa.com.mx>
* Damien Crier <damien.crier@camptocamp.com>
* Cyril Gaudin <cyril.gaudin@camptocamp.com>
* Pierre Verkest <pverkest@anybox.fr>
* Benjamin Willig <benjamin.willig@acsone.eu>
* Devendra Kavthekar <dkatodoo@gmail.com>
* Emanuel Cino <ecino@compassion.ch>
* Thomas Nowicki <thomas.nowicki@camptocamp.com>
* Alexandre Saunier <alexandre.saunier@camptocamp.com>
* Sandip Mangukiya <smangukiya@opensourceintegrators.com>
* Samuel Kouff <s.kouff@student.helmo.be>

View file

@ -0,0 +1,12 @@
GeoEngine is an Odoo module that adds spatial/GIS capabilites to Odoo. It will allow you to :
* Visualize and query your business information on map
* Perform GeoBI and spatial query
* Configure your spatial layers and spatial datasources
* Extend Odoo models with spatial columns
GeoEngine relies on `OpenLayers <http://openlayers.org>`_ and `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 different spatial layers. The GeoEngine module acts as a data provider and as an OpenLayers configurator.
It also provides a complete extension to Odoo ORM.

View file

@ -0,0 +1,108 @@
16.0.1.0.0 (2023-03-20)
========================
* LayerSwitcher has been removed as it was not really practical. A LayerPanel is now active.
* The geo_search method is now deprecated and replaced by the standard odoo search method.
* The widget "geo_edit_map" attribute is no longer necessary as the field is automatically detected by
his type. We can also provide an option attribute that allows us to pass an opacity and a color as
parameters.
.. code-block:: xml
<form>
<notebook colspan="4">
<page string="Geometry">
<field name="the_geom" options="{'opacity': 0.8, 'color': '#0000FF' }" />
</page>
</notebook>
</form>
* The method geo_search is now deprecated. We now need to use the standard odoo search method.
.. code-block:: python
obj.search([("the_point","geo_intersect",{"dummy.zip.the_geom": [("id", "=", rec.id)]})])
* We can now pass to the geoengine view a template to display the information we want
to see when clicking on a feature.
.. code-block:: xml
<geoengine>
<field name="name" />
<field name="city" />
<field name="total_sales" />
<field name="the_geom" />
<templates>
<t t-name="info_box">
<field name="city" widget="badge" />
<ul>
<li>ZIP : <field name="name" />
</li>
<li>Total Sales: <field name="total_sales" />
</li>
</ul>
</t>
</templates>
</geoengine>
* We can now pass a model to use to a layer to display other information on the map.
.. code-block:: xml
<record id="geoengine_vector_layer_hs_retail_machines" model="geoengine.vector.layer">
<field name="model_id" ref="base_geoengine_demo.model_geoengine_demo_automatic_retailing_machine"/>
<field name="model_domain">[('state', '=', 'hs')]</field>
<field name="geo_field_id" ref="base_geoengine_demo.field_geoengine_demo_automatic_retailing_machine__the_point"/>
<field name="name">HS retail machines</field>
<field name="view_id" ref="ir_ui_view_resbetterzipgeoview0" />
<field name="geo_repr">basic</field>
<field name="attribute_field_id" ref="base_geoengine_demo.field_geoengine_demo_automatic_retailing_machine__name"/>
<field name="begin_color">#FF0000</field>
<field name="display_polygon_labels" eval="0" />
<field name="layer_opacity">0.8</field>
</record>
* There is some new features in the LayerPanel.
1. If you are logged in as an admin, you have the possibility to edit the layer by clicking on the edit button. This will open a dialog box.
Changes will appear in real time on the view.
2. If you are logged in as an admin, you can also change the domain of the layer. If you are logged in as a user, changes will not be
persisted in the database. Changes will appear in real time on the view.
3. If you are logged in as an admin, you can also change the sequence of the layers by sliding them over each other. If you are logged in as a user, changes will not be
persisted in the database.
* Widget domain is now implemented for geo field This means that the geo-operators are also implemented and that there is the possibility to add a sub-domain.
If we want to add a domain that includes all the records that are displayed in the geoengine view (active_ids). We can use the two new operators :
"in active_ids" and "not in active_ids". These will automatically replace the marker with ids. Note that the widget will indicate that the domain is invalid
because of the marker.
* Creation of the RecordsPanel. This panel allows you to retrieve all active records. You can click on record to get the movement to the selected record.
Two magnifying glass are also available. You can click on the left one to zoom on the record. You can click on the right one to get the original zoom.
* A search bar is also available. It allows you to perform a search into the RecordsPanel.
* A button to open/close the panels is also available.
* The module has been translated in French.
* Now you can now make the geoengine view editable. Simply add editable attribute in the geoengine view.
.. code-block:: xml
<geoengine editable="1">
<field name="name" />
<field name="city" />
<field name="total_sales" />
<field name="the_geom" />
<field name="display_name" />
<templates>
<t t-name="info_box">
<field name="city" widget="badge" />
<ul>
<li>ZIP : <field name="name" />
</li>
<li>Total Sales: <field name="total_sales" />
</li>
</ul>
</t>
</templates>
</geoengine>
Thanks to that, you can create new records by drawing them directly in the geoengine view. You can also edit record in the same view.

View file

@ -0,0 +1,18 @@
To install this module, you need to have `PostGIS <http://postgis.net/>`_ installed.
On Ubuntu::
.. 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>`_
When you will install the module this two additional libs will be installed.
For a complete documentation please refer to the `public documenation <http://oca.github.io/geospatial/index.html>`_

View file

@ -0,0 +1,58 @@
Geoengine Demo
==============
1. As a user/admin, when I am in the Geoengine Demo module and I go to the ZIP menu.
When I click on an item in the list view, I get to the form view showing me the different
information about the ZIP. We can see its ZIP, city, priority, total sales and his spatial
representation.
2. As a user, I can't modify the information in the form view.
3. As an admin, I can modify the information in the form view. I can click on the bin button to clear
the map and I can draw a new shape.
4. As a user, when I go the "Retail machines" tab and there are no items to display, it does not
show me anything.
5. As an admin, when I go the "Retail machines" tab and there are no items to display, the list view of
the retail machines suggests to me to add a new line.
6. As a user/admin, if there are items to be displayed in the "Retail machines" tab then I can click on an
item and the retail machines form view will be displayed. We can see its spatial representation by going
to "The point" tab and its attributes in "Attributes" tab.
7. As a user/admin, when I go to the geoengine zip view by clicking on the map button at the top right of the
screen. The geoengine view appears with the first 80 results displayed on the map. The vector layers
selected are those defined as "active on startup" by the admin. The selected raster layer is the first
one that is not an overlay layer.
8. As a user/admin, when I hover over an area on the map, the area changes its style.
9. As a user/admin, when I click on an area, a popup appears an I can see the different information about the
area. If I click on the cross, the popup will disappear. If I click somewhere else on the map, the
popup will also disappear. If I click on the about button, then the form view will be displayed.
10. As a user/admin, when I use the paging system, then the results displayed on the map are different
(corresponding to the request).
11. As a user/admin, if we use the search bar, we can search results by his zip or his city.
12. As an admin, if I change the sequence of layers with the handle button then the change are persisted in database.
13. As a user, if I change the sequence of layers with the handle button then the change are not persisted in database.
There are just the changes in the display.
14. As an admin, if I change the domain of a layer with the filter button then the change are persisted in database.
15. As a user, if I change the domain of a layer with the filter button then the change are not persisted in database.
There are just the changes in the display.
16. As an admin, I have the possibility to edit the layer with its corresponding button.
17. As a user/admin, I can open/close LayerPanel with its button.
18. As a user/admin, I can open/close RecordsPanel with its button.
19. As a user/admin, when I click on a record in RecordsPanel, a move is made on the map to the selected record.
20. As a user/admin, when I click on a record in RecordsPanel, I can also click on the left magnifying glass to zoom on the record.
21. As a user/admin, when I click on a record in RecordsPanel, I can also click on the right magnifying glass to get the original zoom.
22. As a user/admin, I can use the search bar to search in the RecordsPanel.
23. As an admin,If the geoengine view is in edit mode, I can create new records by drawing them in the view.
24. As an admin, If the geoengine view is in edit mode, I can modify its spatial representation.
Geoengine Backend
=================
1. As an admin, if I go into the configuration of the raster layers and it has elements, I can click
on one and see its information.
2. As an admin, if I want to create a new raster layer, I can click on "NEW" and fill out the form. The
required fields for OpenStreetMap type are "Layer Name" and "Related View". If we want to have a
WMTS (Web Map Tile Service) raster type. The required fields in addition to the precedents are "Service URL",
"Matrix set","Format", "Projection" and "Resolutions". If we take WMS (Web Map Service) raster type, then the
required fields are "Layer Name", "Related View", "Service URL", "Params", "Server Type".
3. As an admin,if I go into the configuration of the vector layers and it has elements, I can click
on one and see its information.
4. As an admin, if I want to create a new vector layer, I can click on "NEW" and fill out the form. The
required fields are "Layer Name", "Related View", "Geo field" and "Representation mode".

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="group_geoengine_user" model="res.groups">
<field name="name">Geoengine User</field>
</record>
<record id="group_geoengine_admin" model="res.groups">
<field name="name">Geoengine Admin</field>
<field name="implied_ids" eval="[(4, ref('group_geoengine_user')),]" />
<field name="users" eval="[(4, ref('base.user_root'))]" />
</record>
<record id="base.group_user" model="res.groups">
<field name="implied_ids" eval="[Command.link(ref('group_geoengine_user'))]" />
</record>
<record id="base.group_erp_manager" model="res.groups">
<field name="implied_ids" eval="[Command.link(ref('group_geoengine_admin'))]" />
</record>
</odoo>

View file

@ -0,0 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_geo_user_vector_user,geoengine.user.vector.layer,base_geoengine.model_geoengine_vector_layer,base_geoengine.group_geoengine_user,1,0,0,0
access_geo_admin_vector_user,geoengine.admin.vector.layer,base_geoengine.model_geoengine_vector_layer,base_geoengine.group_geoengine_admin,1,1,1,1
access_geo_user_raster_user,geoengine.user.raster.layer,base_geoengine.model_geoengine_raster_layer,base_geoengine.group_geoengine_user,1,0,0,0
access_geo_admin_raster_user,geoengine.admin.raster.layer,base_geoengine.model_geoengine_raster_layer,base_geoengine.group_geoengine_admin,1,1,1,1
access_geo_user_raster_type_user,geoengine.user.raster.layer.type,base_geoengine.model_geoengine_raster_layer_type,base_geoengine.group_geoengine_user,1,0,0,0
access_geo_admin_raster_type_user,geoengine.admin.raster.layer.type,base_geoengine.model_geoengine_raster_layer_type,base_geoengine.group_geoengine_admin,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_geo_user_vector_user geoengine.user.vector.layer base_geoengine.model_geoengine_vector_layer base_geoengine.group_geoengine_user 1 0 0 0
3 access_geo_admin_vector_user geoengine.admin.vector.layer base_geoengine.model_geoengine_vector_layer base_geoengine.group_geoengine_admin 1 1 1 1
4 access_geo_user_raster_user geoengine.user.raster.layer base_geoengine.model_geoengine_raster_layer base_geoengine.group_geoengine_user 1 0 0 0
5 access_geo_admin_raster_user geoengine.admin.raster.layer base_geoengine.model_geoengine_raster_layer base_geoengine.group_geoengine_admin 1 1 1 1
6 access_geo_user_raster_type_user geoengine.user.raster.layer.type base_geoengine.model_geoengine_raster_layer_type base_geoengine.group_geoengine_user 1 0 0 0
7 access_geo_admin_raster_type_user geoengine.admin.raster.layer.type base_geoengine.model_geoengine_raster_layer_type base_geoengine.group_geoengine_admin 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

View file

@ -0,0 +1,659 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="geospatial-support-for-odoo">
<h1>Geospatial support for Odoo</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:2faa9b7de06a2c3c20d9bc960073bdc6638396bc987e338093331e0099278604
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/geospatial/tree/16.0/base_geoengine"><img alt="OCA/geospatial" src="https://img.shields.io/badge/github-OCA%2Fgeospatial-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/geospatial-16-0/geospatial-16-0-base_geoengine"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/geospatial&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>GeoEngine is an Odoo module that adds spatial/GIS capabilites to Odoo. It will allow you to :</p>
<ul class="simple">
<li>Visualize and query your business information on map</li>
<li>Perform GeoBI and spatial query</li>
<li>Configure your spatial layers and spatial datasources</li>
<li>Extend Odoo models with spatial columns</li>
</ul>
<p>GeoEngine relies on <a class="reference external" href="http://openlayers.org">OpenLayers</a> and <a class="reference external" href="http://postgis.refractions.net/">PostgGIS</a> technologies.</p>
<p>Postgis is used to store spatial information in databases. OpenLayer is used to represent spatial data in other words to show maps
and the different spatial layers. The GeoEngine module acts as a data provider and as an OpenLayers configurator.
It also provides a complete extension to Odoo ORM.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#installation" id="toc-entry-1">Installation</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#geoengine-demo" id="toc-entry-3">Geoengine Demo</a></li>
<li><a class="reference internal" href="#geoengine-backend" id="toc-entry-4">Geoengine Backend</a></li>
<li><a class="reference internal" href="#changelog" id="toc-entry-5">Changelog</a></li>
<li><a class="reference internal" href="#section-1" id="toc-entry-6">16.0.1.0.0 (2023-03-20)</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-7">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-8">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-9">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-10">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-11">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="installation">
<h2><a class="toc-backref" href="#toc-entry-1">Installation</a></h2>
<p>To install this module, you need to have <a class="reference external" href="http://postgis.net/">PostGIS</a> installed.</p>
<p>On Ubuntu:</p>
<pre class="literal-block">
.. code-block:: bash
</pre>
<blockquote>
sudo apt-get install postgis</blockquote>
<p>The module also requires two additional python libs:</p>
<ul class="simple">
<li><a class="reference external" href="http://pypi.python.org/pypi/Shapely">Shapely</a></li>
<li><a class="reference external" href="http://pypi.python.org/pypi/geojson">geojson</a></li>
</ul>
<p>When you will install the module this two additional libs will be installed.</p>
<p>For a complete documentation please refer to the <a class="reference external" href="http://oca.github.io/geospatial/index.html">public documenation</a></p>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
</div>
<div class="section" id="geoengine-demo">
<h2><a class="toc-backref" href="#toc-entry-3">Geoengine Demo</a></h2>
<ol class="arabic simple">
<li>As a user/admin, when I am in the Geoengine Demo module and I go to the ZIP menu.
When I click on an item in the list view, I get to the form view showing me the different
information about the ZIP. We can see its ZIP, city, priority, total sales and his spatial
representation.</li>
<li>As a user, I cant modify the information in the form view.</li>
<li>As an admin, I can modify the information in the form view. I can click on the bin button to clear
the map and I can draw a new shape.</li>
<li>As a user, when I go the “Retail machines” tab and there are no items to display, it does not
show me anything.</li>
<li>As an admin, when I go the “Retail machines” tab and there are no items to display, the list view of
the retail machines suggests to me to add a new line.</li>
<li>As a user/admin, if there are items to be displayed in the “Retail machines” tab then I can click on an
item and the retail machines form view will be displayed. We can see its spatial representation by going
to “The point” tab and its attributes in “Attributes” tab.</li>
<li>As a user/admin, when I go to the geoengine zip view by clicking on the map button at the top right of the
screen. The geoengine view appears with the first 80 results displayed on the map. The vector layers
selected are those defined as “active on startup” by the admin. The selected raster layer is the first
one that is not an overlay layer.</li>
<li>As a user/admin, when I hover over an area on the map, the area changes its style.</li>
<li>As a user/admin, when I click on an area, a popup appears an I can see the different information about the
area. If I click on the cross, the popup will disappear. If I click somewhere else on the map, the
popup will also disappear. If I click on the about button, then the form view will be displayed.</li>
<li>As a user/admin, when I use the paging system, then the results displayed on the map are different
(corresponding to the request).</li>
<li>As a user/admin, if we use the search bar, we can search results by his zip or his city.</li>
<li>As an admin, if I change the sequence of layers with the handle button then the change are persisted in database.</li>
<li>As a user, if I change the sequence of layers with the handle button then the change are not persisted in database.
There are just the changes in the display.</li>
<li>As an admin, if I change the domain of a layer with the filter button then the change are persisted in database.</li>
<li>As a user, if I change the domain of a layer with the filter button then the change are not persisted in database.
There are just the changes in the display.</li>
<li>As an admin, I have the possibility to edit the layer with its corresponding button.</li>
<li>As a user/admin, I can open/close LayerPanel with its button.</li>
<li>As a user/admin, I can open/close RecordsPanel with its button.</li>
<li>As a user/admin, when I click on a record in RecordsPanel, a move is made on the map to the selected record.</li>
<li>As a user/admin, when I click on a record in RecordsPanel, I can also click on the left magnifying glass to zoom on the record.</li>
<li>As a user/admin, when I click on a record in RecordsPanel, I can also click on the right magnifying glass to get the original zoom.</li>
<li>As a user/admin, I can use the search bar to search in the RecordsPanel.</li>
<li>As an admin,If the geoengine view is in edit mode, I can create new records by drawing them in the view.</li>
<li>As an admin, If the geoengine view is in edit mode, I can modify its spatial representation.</li>
</ol>
</div>
<div class="section" id="geoengine-backend">
<h2><a class="toc-backref" href="#toc-entry-4">Geoengine Backend</a></h2>
<ol class="arabic simple">
<li>As an admin, if I go into the configuration of the raster layers and it has elements, I can click
on one and see its information.</li>
<li>As an admin, if I want to create a new raster layer, I can click on “NEW” and fill out the form. The
required fields for OpenStreetMap type are “Layer Name” and “Related View”. If we want to have a
WMTS (Web Map Tile Service) raster type. The required fields in addition to the precedents are “Service URL”,
“Matrix set”,”Format”, “Projection” and “Resolutions”. If we take WMS (Web Map Service) raster type, then the
required fields are “Layer Name”, “Related View”, “Service URL”, “Params”, “Server Type”.</li>
<li>As an admin,if I go into the configuration of the vector layers and it has elements, I can click
on one and see its information.</li>
<li>As an admin, if I want to create a new vector layer, I can click on “NEW” and fill out the form. The
required fields are “Layer Name”, “Related View”, “Geo field” and “Representation mode”.</li>
</ol>
</div>
<div class="section" id="changelog">
<h2><a class="toc-backref" href="#toc-entry-5">Changelog</a></h2>
</div>
<div class="section" id="section-1">
<h2><a class="toc-backref" href="#toc-entry-6">16.0.1.0.0 (2023-03-20)</a></h2>
<ul class="simple">
<li>LayerSwitcher has been removed as it was not really practical. A LayerPanel is now active.</li>
<li>The geo_search method is now deprecated and replaced by the standard odoo search method.</li>
<li>The widget “geo_edit_map” attribute is no longer necessary as the field is automatically detected by
his type. We can also provide an option attribute that allows us to pass an opacity and a color as
parameters.</li>
</ul>
<pre class="code xml literal-block">
<span class="nt">&lt;form&gt;</span><span class="w">
</span><span class="nt">&lt;notebook</span><span class="w"> </span><span class="na">colspan=</span><span class="s">&quot;4&quot;</span><span class="nt">&gt;</span><span class="w">
</span><span class="nt">&lt;page</span><span class="w"> </span><span class="na">string=</span><span class="s">&quot;Geometry&quot;</span><span class="nt">&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;the_geom&quot;</span><span class="w"> </span><span class="na">options=</span><span class="s">&quot;{'opacity': 0.8, 'color': '#0000FF' }&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;/page&gt;</span><span class="w">
</span><span class="nt">&lt;/notebook&gt;</span><span class="w">
</span><span class="nt">&lt;/form&gt;</span>
</pre>
<ul class="simple">
<li>The method geo_search is now deprecated. We now need to use the standard odoo search method.</li>
</ul>
<pre class="code python literal-block">
<span class="n">obj</span><span class="o">.</span><span class="n">search</span><span class="p">([(</span><span class="s2">&quot;the_point&quot;</span><span class="p">,</span><span class="s2">&quot;geo_intersect&quot;</span><span class="p">,{</span><span class="s2">&quot;dummy.zip.the_geom&quot;</span><span class="p">:</span> <span class="p">[(</span><span class="s2">&quot;id&quot;</span><span class="p">,</span> <span class="s2">&quot;=&quot;</span><span class="p">,</span> <span class="n">rec</span><span class="o">.</span><span class="n">id</span><span class="p">)]})])</span>
</pre>
<ul class="simple">
<li>We can now pass to the geoengine view a template to display the information we want
to see when clicking on a feature.</li>
</ul>
<pre class="code xml literal-block">
<span class="nt">&lt;geoengine&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;name&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;city&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;total_sales&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;the_geom&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;templates&gt;</span><span class="w">
</span><span class="nt">&lt;t</span><span class="w"> </span><span class="na">t-name=</span><span class="s">&quot;info_box&quot;</span><span class="nt">&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;city&quot;</span><span class="w"> </span><span class="na">widget=</span><span class="s">&quot;badge&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;ul&gt;</span><span class="w">
</span><span class="nt">&lt;li&gt;</span>ZIP<span class="w"> </span>:<span class="w"> </span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;name&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;/li&gt;</span><span class="w">
</span><span class="nt">&lt;li&gt;</span>Total<span class="w"> </span>Sales:<span class="w"> </span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;total_sales&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;/li&gt;</span><span class="w">
</span><span class="nt">&lt;/ul&gt;</span><span class="w">
</span><span class="nt">&lt;/t&gt;</span><span class="w">
</span><span class="nt">&lt;/templates&gt;</span><span class="w">
</span><span class="nt">&lt;/geoengine&gt;</span>
</pre>
<ul class="simple">
<li>We can now pass a model to use to a layer to display other information on the map.</li>
</ul>
<pre class="code xml literal-block">
<span class="nt">&lt;record</span><span class="w"> </span><span class="na">id=</span><span class="s">&quot;geoengine_vector_layer_hs_retail_machines&quot;</span><span class="w"> </span><span class="na">model=</span><span class="s">&quot;geoengine.vector.layer&quot;</span><span class="nt">&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;model_id&quot;</span><span class="w"> </span><span class="na">ref=</span><span class="s">&quot;base_geoengine_demo.model_geoengine_demo_automatic_retailing_machine&quot;</span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;model_domain&quot;</span><span class="nt">&gt;</span>[('state',<span class="w"> </span>'=',<span class="w"> </span>'hs')]<span class="nt">&lt;/field&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;geo_field_id&quot;</span><span class="w"> </span><span class="na">ref=</span><span class="s">&quot;base_geoengine_demo.field_geoengine_demo_automatic_retailing_machine__the_point&quot;</span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;name&quot;</span><span class="nt">&gt;</span>HS<span class="w"> </span>retail<span class="w"> </span>machines<span class="nt">&lt;/field&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;view_id&quot;</span><span class="w"> </span><span class="na">ref=</span><span class="s">&quot;ir_ui_view_resbetterzipgeoview0&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;geo_repr&quot;</span><span class="nt">&gt;</span>basic<span class="nt">&lt;/field&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;attribute_field_id&quot;</span><span class="w"> </span><span class="na">ref=</span><span class="s">&quot;base_geoengine_demo.field_geoengine_demo_automatic_retailing_machine__name&quot;</span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;begin_color&quot;</span><span class="nt">&gt;</span>#FF0000<span class="nt">&lt;/field&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;display_polygon_labels&quot;</span><span class="w"> </span><span class="na">eval=</span><span class="s">&quot;0&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;layer_opacity&quot;</span><span class="nt">&gt;</span>0.8<span class="nt">&lt;/field&gt;</span><span class="w">
</span><span class="nt">&lt;/record&gt;</span>
</pre>
<ul class="simple">
<li>There is some new features in the LayerPanel.</li>
</ul>
<ol class="arabic simple">
<li>If you are logged in as an admin, you have the possibility to edit the layer by clicking on the edit button. This will open a dialog box.
Changes will appear in real time on the view.</li>
<li>If you are logged in as an admin, you can also change the domain of the layer. If you are logged in as a user, changes will not be
persisted in the database. Changes will appear in real time on the view.</li>
<li>If you are logged in as an admin, you can also change the sequence of the layers by sliding them over each other. If you are logged in as a user, changes will not be
persisted in the database.</li>
</ol>
<ul class="simple">
<li>Widget domain is now implemented for geo field This means that the geo-operators are also implemented and that there is the possibility to add a sub-domain.
If we want to add a domain that includes all the records that are displayed in the geoengine view (active_ids). We can use the two new operators :
“in active_ids” and “not in active_ids”. These will automatically replace the marker with ids. Note that the widget will indicate that the domain is invalid
because of the marker.</li>
<li>Creation of the RecordsPanel. This panel allows you to retrieve all active records. You can click on record to get the movement to the selected record.
Two magnifying glass are also available. You can click on the left one to zoom on the record. You can click on the right one to get the original zoom.</li>
<li>A search bar is also available. It allows you to perform a search into the RecordsPanel.</li>
<li>A button to open/close the panels is also available.</li>
<li>The module has been translated in French.</li>
<li>Now you can now make the geoengine view editable. Simply add editable attribute in the geoengine view.</li>
</ul>
<pre class="code xml literal-block">
<span class="nt">&lt;geoengine</span><span class="w"> </span><span class="na">editable=</span><span class="s">&quot;1&quot;</span><span class="nt">&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;name&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;city&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;total_sales&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;the_geom&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;display_name&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;templates&gt;</span><span class="w">
</span><span class="nt">&lt;t</span><span class="w"> </span><span class="na">t-name=</span><span class="s">&quot;info_box&quot;</span><span class="nt">&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;city&quot;</span><span class="w"> </span><span class="na">widget=</span><span class="s">&quot;badge&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;ul&gt;</span><span class="w">
</span><span class="nt">&lt;li&gt;</span>ZIP<span class="w"> </span>:<span class="w"> </span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;name&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;/li&gt;</span><span class="w">
</span><span class="nt">&lt;li&gt;</span>Total<span class="w"> </span>Sales:<span class="w"> </span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;total_sales&quot;</span><span class="w"> </span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;/li&gt;</span><span class="w">
</span><span class="nt">&lt;/ul&gt;</span><span class="w">
</span><span class="nt">&lt;/t&gt;</span><span class="w">
</span><span class="nt">&lt;/templates&gt;</span><span class="w">
</span><span class="nt">&lt;/geoengine&gt;</span><span class="w">
</span>Thanks<span class="w"> </span>to<span class="w"> </span>that,<span class="w"> </span>you<span class="w"> </span>can<span class="w"> </span>create<span class="w"> </span>new<span class="w"> </span>records<span class="w"> </span>by<span class="w"> </span>drawing<span class="w"> </span>them<span class="w"> </span>directly<span class="w"> </span>in<span class="w"> </span>the<span class="w"> </span>geoengine<span class="w"> </span>view.<span class="w"> </span>You<span class="w"> </span>can<span class="w"> </span>also<span class="w"> </span>edit<span class="w"> </span>record<span class="w"> </span>in<span class="w"> </span>the<span class="w"> </span>same<span class="w"> </span>view.
</pre>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-7">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/geospatial/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/geospatial/issues/new?body=module:%20base_geoengine%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-8">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-9">Authors</a></h3>
<ul class="simple">
<li>Camptocamp</li>
<li>ACSONE SA/NV</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-10">Contributors</a></h3>
<ul class="simple">
<li>Nicolas Bessi &lt;<a class="reference external" href="mailto:nicolas.bessi&#64;camptocamp.com">nicolas.bessi&#64;camptocamp.com</a>&gt;</li>
<li>Frederic Junod &lt;<a class="reference external" href="mailto:frederic.junod&#64;camptocamp.com">frederic.junod&#64;camptocamp.com</a>&gt;</li>
<li>Yannick Payot &lt;<a class="reference external" href="mailto:yannick.payot&#64;camptocamp.com">yannick.payot&#64;camptocamp.com</a>&gt;</li>
<li>Sandy Carter &lt;<a class="reference external" href="mailto:sandy.carter&#64;savoirfairelinux.com">sandy.carter&#64;savoirfairelinux.com</a>&gt;</li>
<li>Laurent Mignon &lt;<a class="reference external" href="mailto:laurent.mignon&#64;acsone.eu">laurent.mignon&#64;acsone.eu</a>&gt;</li>
<li>Jonathan Nemry &lt;<a class="reference external" href="mailto:jonathan.nemry&#64;acsone.eu">jonathan.nemry&#64;acsone.eu</a>&gt;</li>
<li>David Lasley &lt;<a class="reference external" href="mailto:dave&#64;dlasley.net">dave&#64;dlasley.net</a>&gt;</li>
<li>Daniel Reis &lt;<a class="reference external" href="mailto:dgreis&#64;sapo.pt">dgreis&#64;sapo.pt</a>&gt;</li>
<li>Matthieu Dietrich &lt;<a class="reference external" href="mailto:matthieu.dietrich&#64;camptocamp.com">matthieu.dietrich&#64;camptocamp.com</a>&gt;</li>
<li>Alan Ramos &lt;<a class="reference external" href="mailto:alan.ramos&#64;jarsa.com.mx">alan.ramos&#64;jarsa.com.mx</a>&gt;</li>
<li>Damien Crier &lt;<a class="reference external" href="mailto:damien.crier&#64;camptocamp.com">damien.crier&#64;camptocamp.com</a>&gt;</li>
<li>Cyril Gaudin &lt;<a class="reference external" href="mailto:cyril.gaudin&#64;camptocamp.com">cyril.gaudin&#64;camptocamp.com</a>&gt;</li>
<li>Pierre Verkest &lt;<a class="reference external" href="mailto:pverkest&#64;anybox.fr">pverkest&#64;anybox.fr</a>&gt;</li>
<li>Benjamin Willig &lt;<a class="reference external" href="mailto:benjamin.willig&#64;acsone.eu">benjamin.willig&#64;acsone.eu</a>&gt;</li>
<li>Devendra Kavthekar &lt;<a class="reference external" href="mailto:dkatodoo&#64;gmail.com">dkatodoo&#64;gmail.com</a>&gt;</li>
<li>Emanuel Cino &lt;<a class="reference external" href="mailto:ecino&#64;compassion.ch">ecino&#64;compassion.ch</a>&gt;</li>
<li>Thomas Nowicki &lt;<a class="reference external" href="mailto:thomas.nowicki&#64;camptocamp.com">thomas.nowicki&#64;camptocamp.com</a>&gt;</li>
<li>Alexandre Saunier &lt;<a class="reference external" href="mailto:alexandre.saunier&#64;camptocamp.com">alexandre.saunier&#64;camptocamp.com</a>&gt;</li>
<li>Sandip Mangukiya &lt;<a class="reference external" href="mailto:smangukiya&#64;opensourceintegrators.com">smangukiya&#64;opensourceintegrators.com</a>&gt;</li>
<li>Samuel Kouff &lt;<a class="reference external" href="mailto:s.kouff&#64;student.helmo.be">s.kouff&#64;student.helmo.be</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-11">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/geospatial/tree/16.0/base_geoengine">OCA/geospatial</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

View file

@ -0,0 +1,28 @@
chroma.js - JavaScript library for color conversions
Copyright (c) 2011-2015, Gregor Aisch
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name Gregor Aisch may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,24 @@
.geostats-legend div {
margin:3px 10px 5px 10px;
clear:left;
}
.geostats-legend-title {
font-weight: bold;
margin-bottom: 4px;
}
.geostats-legend-block {
border: 1px solid #555555;
display: block;
float: left;
height: 12px;
margin: 0 5px 0 20px;
width: 20px;
}
.geostats-legend-counter {
font-size: 0.8em;
color:#666;
font-style: italic;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,402 @@
/* OpenLayers Style */
:root,
:host {
--ol-background-color: white;
--ol-accent-background-color: #f5f5f5;
--ol-subtle-background-color: rgba(128, 128, 128, 0.25);
--ol-partial-background-color: rgba(255, 255, 255, 0.75);
--ol-foreground-color: #333333;
--ol-subtle-foreground-color: #666666;
--ol-brand-color: #00aaff;
}
.ol-box {
box-sizing: border-box;
border-radius: 2px;
border: 1.5px solid var(--ol-background-color);
background-color: var(--ol-partial-background-color);
}
.ol-mouse-position {
top: 8px;
right: 8px;
position: absolute;
}
.ol-scale-line {
background: var(--ol-partial-background-color);
border-radius: 4px;
bottom: 8px;
left: 8px;
padding: 2px;
position: absolute;
}
.ol-scale-line-inner {
border: 1px solid var(--ol-subtle-foreground-color);
border-top: none;
color: var(--ol-foreground-color);
font-size: 10px;
text-align: center;
margin: 1px;
will-change: contents, width;
transition: all 0.25s;
}
.ol-scale-bar {
position: absolute;
bottom: 8px;
left: 8px;
}
.ol-scale-bar-inner {
display: flex;
}
.ol-scale-step-marker {
width: 1px;
height: 15px;
background-color: var(--ol-foreground-color);
float: right;
z-index: 10;
}
.ol-scale-step-text {
position: absolute;
bottom: -5px;
font-size: 10px;
z-index: 11;
color: var(--ol-foreground-color);
text-shadow: -1.5px 0 var(--ol-partial-background-color),
0 1.5px var(--ol-partial-background-color),
1.5px 0 var(--ol-partial-background-color),
0 -1.5px var(--ol-partial-background-color);
}
.ol-scale-text {
position: absolute;
font-size: 12px;
text-align: center;
bottom: 25px;
color: var(--ol-foreground-color);
text-shadow: -1.5px 0 var(--ol-partial-background-color),
0 1.5px var(--ol-partial-background-color),
1.5px 0 var(--ol-partial-background-color),
0 -1.5px var(--ol-partial-background-color);
}
.ol-scale-singlebar {
position: relative;
height: 10px;
z-index: 9;
box-sizing: border-box;
border: 1px solid var(--ol-foreground-color);
}
.ol-scale-singlebar-even {
background-color: var(--ol-subtle-foreground-color);
}
.ol-scale-singlebar-odd {
background-color: var(--ol-background-color);
}
.ol-unsupported {
display: none;
}
.ol-viewport,
.ol-unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.ol-viewport canvas {
all: unset;
}
.ol-selectable {
-webkit-touch-callout: default;
-webkit-user-select: text;
-moz-user-select: text;
user-select: text;
}
.ol-grabbing {
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
.ol-grab {
cursor: move;
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
.ol-control {
position: absolute;
background-color: var(--ol-subtle-background-color);
border-radius: 4px;
}
.ol-zoom {
top: 0.5em;
left: 0.5em;
}
.draw-control {
top: 4em;
left: 0.5em;
}
.select-control {
top: 8em;
left: 0.5em;
}
.edit-control {
top: 6em;
left: 0.5em;
}
#map-legend {
bottom: 17em;
right: 24em;
z-index: 1054;
background-color: transparent;
}
.legend {
margin-bottom: 15px;
display: none;
background-color: #f5f5f5;
border-radius: 5px;
}
.selected-control > i {
color: #71639e;
}
.ol-rotate {
top: 0.5em;
right: 0.5em;
transition: opacity 0.25s linear, visibility 0s linear;
}
.ol-rotate.ol-hidden {
opacity: 0;
visibility: hidden;
transition: opacity 0.25s linear, visibility 0s linear 0.25s;
}
.ol-zoom-extent {
top: 4.643em;
left: 0.5em;
}
.ol-full-screen {
right: 0.5em;
top: 0.5em;
}
.ol-control button {
display: block;
margin: 1px;
padding: 0;
color: var(--ol-subtle-foreground-color);
font-weight: bold;
text-decoration: none;
font-size: inherit;
text-align: center;
height: 1.375em;
width: 1.375em;
line-height: 0.4em;
background-color: var(--ol-background-color);
border: none;
border-radius: 2px;
}
.ol-control button::-moz-focus-inner {
border: none;
padding: 0;
}
.ol-zoom-extent button {
line-height: 1.4em;
}
.ol-compass {
display: block;
font-weight: normal;
will-change: transform;
}
.ol-touch .ol-control button {
font-size: 1.5em;
}
.ol-touch .ol-zoom-extent {
top: 5.5em;
}
.ol-control button:hover,
.ol-control button:focus {
text-decoration: none;
outline: 1px solid var(--ol-subtle-foreground-color);
color: var(--ol-foreground-color);
}
.ol-zoom .ol-zoom-in {
border-radius: 2px 2px 0 0;
}
.ol-zoom .ol-zoom-out {
border-radius: 0 0 2px 2px;
}
.ol-attribution {
text-align: right;
bottom: 0.5em;
right: 0.5em;
max-width: calc(100% - 1.3em);
display: flex;
flex-flow: row-reverse;
align-items: center;
}
.ol-attribution a {
color: var(--ol-subtle-foreground-color);
text-decoration: none;
}
.ol-attribution ul {
margin: 0;
padding: 1px 0.5em;
color: var(--ol-foreground-color);
text-shadow: 0 0 2px var(--ol-background-color);
font-size: 12px;
}
.ol-attribution li {
display: inline;
list-style: none;
}
.ol-attribution li:not(:last-child):after {
content: " ";
}
.ol-attribution img {
max-height: 2em;
max-width: inherit;
vertical-align: middle;
}
.ol-attribution button {
flex-shrink: 0;
}
.ol-attribution.ol-collapsed ul {
display: none;
}
.ol-attribution:not(.ol-collapsed) {
background: var(--ol-partial-background-color);
}
.ol-attribution.ol-uncollapsible {
bottom: 0;
right: 0;
border-radius: 4px 0 0;
}
.ol-attribution.ol-uncollapsible img {
margin-top: -0.2em;
max-height: 1.6em;
}
.ol-attribution.ol-uncollapsible button {
display: none;
}
.ol-zoomslider {
top: 4.5em;
left: 0.5em;
height: 200px;
}
.ol-zoomslider button {
position: relative;
height: 10px;
}
.ol-touch .ol-zoomslider {
top: 5.5em;
}
.ol-overviewmap {
left: 0.5em;
bottom: 0.5em;
}
.ol-overviewmap.ol-uncollapsible {
bottom: 0;
left: 0;
border-radius: 0 4px 0 0;
}
.ol-overviewmap .ol-overviewmap-map,
.ol-overviewmap button {
display: block;
}
.ol-overviewmap .ol-overviewmap-map {
border: 1px solid var(--ol-subtle-foreground-color);
height: 150px;
width: 150px;
}
.ol-overviewmap:not(.ol-collapsed) button {
bottom: 0;
left: 0;
position: absolute;
}
.ol-overviewmap.ol-collapsed .ol-overviewmap-map,
.ol-overviewmap.ol-uncollapsible button {
display: none;
}
.ol-overviewmap:not(.ol-collapsed) {
background: var(--ol-subtle-background-color);
}
.ol-overviewmap-box {
border: 1.5px dotted var(--ol-subtle-foreground-color);
}
.ol-overviewmap .ol-overviewmap-box:hover {
cursor: move;
}
.ol-clear {
top: 65px;
left: 0.5em;
}
.form-check {
display: block;
min-height: 1.625rem;
padding-left: 0;
margin-bottom: 0.125rem;
margin-left: 1.5em;
}
#popup-closer {
cursor: pointer;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,9 @@
{
"globals": {
"$": false,
"_": false,
"ol": false,
"chroma": false,
"geostats": false
}
}

View file

@ -0,0 +1,42 @@
/** @odoo-module */
import {reactive} from "@odoo/owl";
class RasterLayersStore {
/**
* Set raster layers to the store.
* @param {*} rasters
*/
setRasters(rasters) {
const newRasters = rasters.map((raster) => {
Object.defineProperty(raster, "isVisible", {
value: false,
writable: true,
});
raster.isVisible = !raster.overlay;
return raster;
});
this.rasters = newRasters;
}
/**
* This is called when a raster layer is changed. This will notify observers of the change.
* @param {*} newRastersLayer
*/
onRasterLayerChanged(newRastersLayer) {
this.rasters = newRastersLayer;
}
get rastersLayers() {
return this.rasters;
}
getRaster(id) {
return this.rasters.find((el) => el.id === id);
}
get count() {
return this.rasters.length;
}
}
export const rasterLayersStore = reactive(new RasterLayersStore());

View file

@ -0,0 +1,36 @@
/** @odoo-module */
import {reactive} from "@odoo/owl";
class VectorLayersStore {
/**
* Set vector layers to the store.
* @param {*} rasters
*/
setVectors(vectors) {
const newVectors = vectors.map((vector) => {
Object.defineProperty(vector, "isVisible", {
value: false,
writable: true,
});
if (vector.active_on_startup) {
vector.isVisible = true;
}
return vector;
});
this.vectors = newVectors;
}
get vectorsLayers() {
return this.vectors;
}
getVector(resId) {
return this.vectors.find((el) => el.resId === resId);
}
get count() {
return this.vectors.length;
}
}
export const vectorLayersStore = reactive(new VectorLayersStore());

View file

@ -0,0 +1,87 @@
/** @odoo-module */
/**
* Copyright 2023 ACSONE SA/NV
*/
import {addFieldDependencies} from "@web/views/utils";
import {Field} from "@web/views/fields/field";
import {Widget} from "@web/views/widgets/widget";
import {XMLParser} from "@web/core/utils/xml";
import {_lt} from "@web/core/l10n/translation";
export const INFO_BOX_ATTRIBUTE = "info_box";
export class GeoengineArchParser extends XMLParser {
/**
* Allow you to browse and process the xml template of the geoengine view.
* @param {*} arch
* @param {*} models
* @param {*} modelName
* @returns {Object}
*/
parse(arch, models, modelName) {
const xmlDoc = this.parseXML(arch);
const templateDocs = {};
const fieldNodes = {};
const jsClass = xmlDoc.getAttribute("js_class");
const activeFields = {};
const geoengineAttr = {};
this.visitXML(xmlDoc, (node) => {
if (["geoengine"].includes(node.tagName)) {
geoengineAttr.editable = Boolean(
Number(xmlDoc.getAttribute("editable"))
);
}
// Get the info box template
if (node.hasAttribute("t-name")) {
templateDocs[node.getAttribute("t-name")] = node;
return;
}
if (node.tagName === "field") {
const fieldInfo = Field.parseFieldNode(
node,
models,
modelName,
"geoengine",
jsClass
);
const name = fieldInfo.name;
fieldNodes[name] = fieldInfo;
node.setAttribute("field_id", name);
addFieldDependencies(
activeFields,
models[modelName],
fieldInfo.FieldComponent.fieldDependencies
);
}
if (node.tagName === "widget") {
const {WidgetComponent} = Widget.parseWidgetNode(node);
addFieldDependencies(
activeFields,
models[modelName],
WidgetComponent.fieldDependencies
);
}
});
const infoBox = templateDocs[INFO_BOX_ATTRIBUTE];
if (!infoBox) {
throw new Error(_lt(`Missing ${INFO_BOX_ATTRIBUTE} template.`));
}
for (const [key, field] of Object.entries(fieldNodes)) {
activeFields[key] = field;
}
return {
arch,
templateDocs,
activeFields,
fieldNodes,
...geoengineAttr,
};
}
}

View file

@ -0,0 +1,9 @@
/** @odoo-module */
/**
* Copyright 2023 ACSONE SA/NV
*/
import {ViewCompiler} from "@web/views/view_compiler";
export class GeoengineCompiler extends ViewCompiler {}

View file

@ -0,0 +1,143 @@
/** @odoo-module */
/**
* Copyright 2023 ACSONE SA/NV
*/
import {Layout} from "@web/search/layout";
import {useModel} from "@web/views/model";
import {usePager} from "@web/search/pager_hook";
import {useOwnedDialogs, useService} from "@web/core/utils/hooks";
import {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog";
import {WarningDialog} from "@web/core/errors/error_dialogs";
import {Component, useState} from "@odoo/owl";
export class GeoengineController extends Component {
/**
* Setup the controller by using the useModel hook.
*/
setup() {
this.state = useState({isSavedOrDiscarded: false});
this.actionService = useService("action");
this.view = useService("view");
this.addDialog = useOwnedDialogs();
this.editable = this.props.archInfo.editable;
this.model = useModel(this.props.Model, {
activeFields: this.props.archInfo.activeFields,
resModel: this.props.resModel,
fields: this.props.fields,
limit: this.props.limit,
});
/**
* Allow you to display records on the map thanks to the paging located
* at the top right of the screen.
*/
usePager(() => {
const list = this.model.root;
const {count, limit, offset} = list;
return {
offset: offset,
limit: limit,
total: count,
onUpdate: async ({offset, limit}) => {
await list.load({limit, offset});
this.render(true);
},
};
});
}
/**
* Allow you to open the form editing view for the filled-in model.
* @param {*} resModel
* @param {*} resId
*/
async openRecord(resModel, resId) {
const {views} = await this.view.loadViews({resModel, views: [[false, "form"]]});
this.actionService.doAction({
type: "ir.actions.act_window",
res_model: resModel,
views: [[views.form.id, "form"]],
res_id: resId,
target: "new",
context: {edit: false, create: false},
});
}
/**
* When you finished drawing a new shape, this method is called to open form view and create the record.
* @param {*} resModel
* @param {*} field
* @param {*} value
*/
async createRecord(resModel, field, value) {
const {views} = await this.view.loadViews({resModel, views: [[false, "form"]]});
const context = {};
context[`default_${field}`] = value;
this.addDialog(FormViewDialog, {
resModel: resModel,
title: this.env._t("New record"),
viewId: views.form.id,
context,
onRecordSaved: async () => await this.onSaveRecord(),
});
}
/**
* This method is called when you have finished to create a new record.
*/
async onSaveRecord() {
const offset = this.model.root.count + 1;
await this.model.root.load({offset});
this.render(true);
}
/**
* This method is called when you click on save button after edit a spatial representation.
*/
async onClickSave() {
await this.model.root.editedRecord.save();
this.state.isSavedOrDiscarded = true;
}
/**
* This method is called when you click on discard button after edit a spatial representation.
*/
async onClickDiscard() {
await this.model.root.editedRecord.discard();
this.state.isSavedOrDiscarded = true;
}
/**
* When you have finished edtiting a spatial representation, this method is called to update the value.
* @param {*} value
*/
async updateRecord(value) {
this.state.isSavedOrDiscarded = false;
const newValue = {};
const key = Object.keys(this.model.root.fields).find(
(el) => this.model.root.fields[el].geo_type !== undefined
);
newValue[key] = value;
await this.model.root.editedRecord.update(newValue);
}
/**
* This method warns you if you start creating a record without having displayed the others.
*/
onDrawStart() {
const {count, records} = this.model.root;
if (records.length < count) {
this.addDialog(WarningDialog, {
title: this.env._t("Warning"),
message: this.env._t(
"You are about to create a new record without having displayed all the others. A risk of overlap could occur. Would you like to continue ?"
),
});
}
}
}
GeoengineController.template = "base_geoengine.GeoengineController";
GeoengineController.components = {Layout};

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates>
<t t-name="base_geoengine.GeoengineController" owl="1">
<Layout display="props.display" className="'h-100'">
<t t-set-slot="layout-buttons">
<t t-if="model.root.editedRecord">
<button
type="button"
class="btn btn-primary o_list_button_save"
data-hotkey="s"
t-on-click.stop="onClickSave"
>
Save
</button>
<button
type="button"
class="btn btn-secondary o_list_button_discard"
data-hotkey="j"
t-on-click="onClickDiscard"
>
Discard
</button>
</t>
</t>
<t
t-component="props.Renderer"
isSavedOrDiscarded="state.isSavedOrDiscarded"
archInfo="props.archInfo"
data="model.root"
editable="editable"
openRecord.bind="openRecord"
updateRecord.bind="updateRecord"
onClickDiscard.bind="onClickDiscard"
createRecord.bind="createRecord"
onDrawStart.bind="onDrawStart"
/>
</Layout>
</t>
</templates>

View file

@ -0,0 +1,56 @@
/** @odoo-module */
/**
* Copyright 2023 ACSONE SA/NV
*/
import {Field} from "@web/views/fields/field";
import {GeoengineCompiler} from "../geoengine_compiler.esm";
import {INFO_BOX_ATTRIBUTE} from "../geoengine_arch_parser.esm";
import {registry} from "@web/core/registry";
import {useViewCompiler} from "@web/views/view_compiler";
import {Component, onWillUpdateProps} from "@odoo/owl";
const formatters = registry.category("formatters");
function getValue(record, fieldName) {
const field = record.fields[fieldName];
const value = record._values[fieldName];
const formatter = formatters.get(field.type, String);
return formatter(value, {field, data: record._values});
}
export class GeoengineRecord extends Component {
/**
* Setup the record by compiling the arch and the info-box template.
*/
setup() {
const {archInfo, templates} = this.props;
const {arch} = archInfo;
const ViewCompiler = this.constructor.Compiler;
this.templates = useViewCompiler(ViewCompiler, arch, templates);
this.createRecord(this.props);
onWillUpdateProps(this.createRecord);
}
/**
* Create record with formatter.
* @param {*} props
*/
createRecord(props) {
const {record} = props;
this.record = Object.create(null);
for (const fieldName in record._values) {
this.record[fieldName] = {
get value() {
return getValue(record, fieldName);
},
};
}
}
}
GeoengineRecord.template = "base_geoengine_GeoengineRecord";
GeoengineRecord.Compiler = GeoengineCompiler;
GeoengineRecord.components = {Field};
GeoengineRecord.INFO_BOX_ATTRIBUTE = INFO_BOX_ATTRIBUTE;

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates xml:space="preserve">
<t t-name="base_geoengine_GeoengineRecord" owl="1">
<div>
<t t-call="{{ templates[this.constructor.INFO_BOX_ATTRIBUTE] }}" />
</div>
</t>
</templates>

View file

@ -0,0 +1,67 @@
#olmap {
width: 100%;
height: 100%;
}
.map_container {
width: 100%;
height: 100%;
}
.view {
max-height: 90%;
height: 100% !important;
}
.menu {
z-index: 2;
flex-grow: 0;
flex-shrink: 0;
padding-right: 0.5rem;
padding-bottom: 3rem;
padding-left: 2.5rem;
height: 100%;
}
.ol-popup {
position: absolute;
background-color: white;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
padding: 15px;
border-radius: 10px;
border: 1px solid #cccccc;
bottom: 12px;
left: -50px;
min-width: 280px;
}
.ol-popup:after,
.ol-popup:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.ol-popup:after {
border-top-color: white;
border-width: 10px;
left: 48px;
margin-left: -10px;
}
.ol-popup:before {
border-top-color: #cccccc;
border-width: 11px;
left: 48px;
margin-left: -11px;
}
.ol-popup-closer {
text-decoration: none;
position: absolute;
top: 2px;
right: 8px;
}
.ol-popup-closer:after {
content: "";
}

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates xml:space="preserve">
<t t-name="base_geoengine.GeoengineRenderer" owl="1">
<div class="d-flex view w-100">
<LayersPanel model="props.data.resModel" vectorModel="vectorModel.root" />
<div class="map_container">
<div id="olmap" />
</div>
<RecordsPanel
list="props.data"
onDisplayPopupRecord.bind="onDisplayPopupRecord"
zoomOnFeature.bind="zoomOnFeature"
zoomOutOnFeature.bind="getOriginalZoom"
/>
</div>
<div id="popup" class="ol-popup">
<div
id="popup-closer"
class="ol-popup-closer text-primary"
t-on-click="clickToHidePopup"
/>
<div id="popup-content" />
<div class="row d-flex justify-content-center">
<button
t-on-click="onInfoBoxClicked"
class="btn btn-secondary border w-50"
>OPEN</button>
</div>
</div>
<div id="map-legend" class="ol-control" />
</t>
</templates>

View file

@ -0,0 +1,40 @@
/** @odoo-module */
/**
* Copyright 2023 ACSONE SA/NV
*/
import {_lt} from "@web/core/l10n/translation";
import {GeoengineController} from "./geoengine_controller/geoengine_controller.esm";
import {GeoengineRenderer} from "./geoengine_renderer/geoengine_renderer.esm";
import {GeoengineArchParser} from "./geoengine_arch_parser.esm";
import {GeoengineCompiler} from "./geoengine_compiler.esm";
import {RelationalModel} from "@web/views/relational_model";
import {registry} from "@web/core/registry";
export const geoengineView = {
type: "geoengine",
display_name: _lt("Geoengine"),
icon: "fa fa-map-o",
multiRecord: true,
ArchParser: GeoengineArchParser,
Controller: GeoengineController,
Model: RelationalModel,
Renderer: GeoengineRenderer,
Compiler: GeoengineCompiler,
props: (genericProps, view) => {
const {ArchParser} = view;
const {arch, relatedModels, resModel} = genericProps;
const archInfo = new ArchParser().parse(arch, relatedModels, resModel);
return {
...genericProps,
Model: view.Model,
Renderer: view.Renderer,
archInfo,
};
},
};
registry.category("views").add("geoengine", geoengineView);

View file

@ -0,0 +1,246 @@
/** @odoo-module */
/**
* Copyright 2023 ACSONE SA/NV
*/
import {CheckBox} from "@web/core/checkbox/checkbox";
import {rasterLayersStore} from "../../../raster_layers_store.esm";
import {vectorLayersStore} from "../../../vector_layers_store.esm";
import {useOwnedDialogs, useService} from "@web/core/utils/hooks";
import {DomainSelectorGeoFieldDialog} from "../../../widgets/domain_selector_geo_field/domain_selector_geo_field_dialog/domain_selector_geo_field_dialog.esm";
import {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog";
import {useSortable} from "@web/core/utils/sortable";
import {Component, onWillStart, useRef, useState} from "@odoo/owl";
export class LayersPanel extends Component {
setup() {
this.orm = useService("orm");
this.actionService = useService("action");
this.view = useService("view");
this.rpc = useService("rpc");
this.user = useService("user");
this.state = useState({geoengineLayers: {}, isFolded: false});
this.addDialog = useOwnedDialogs();
let dataRowId = "";
/**
* Call the model method "get_geoengine_layers" to get all the layers
* in the database and add them to the store.
*/
onWillStart(async () => {
await Promise.all([this.loadIsAdmin(), this.loadLayers()]);
/**
* Get resId of records to allow resequence of elements.
*/
this.state.geoengineLayers.actives.forEach((val) => {
const element = this.props.vectorModel.records.find(
(el) => el.resId === val.id
);
const obj = {id: element.id, resId: element.resId};
Object.assign(val, obj);
});
// Set layers in the store
rasterLayersStore.setRasters(this.state.geoengineLayers.backgrounds);
vectorLayersStore.setVectors(this.state.geoengineLayers.actives);
this.numberOfLayers = vectorLayersStore.count + rasterLayersStore.count;
});
/**
* Allows you to change the priority of the layer by sliding them over each other
*/
useSortable({
ref: useRef("root"),
elements: ".item",
handle: ".fa-sort",
onDragStart({element}) {
dataRowId = element.dataset.id;
},
onDrop: (params) => this.sort(dataRowId, params),
});
}
async loadIsAdmin() {
return this.user
.hasGroup("base_geoengine.group_geoengine_admin")
.then((result) => {
this.isGeoengineAdmin = result;
});
}
async loadLayers() {
return this.orm
.call(this.props.model, "get_geoengine_layers", [])
.then((result) => {
this.state.geoengineLayers = result;
});
}
async sort(dataRowId, {previous}) {
const refId = previous ? previous.dataset.id : null;
this.resquence(dataRowId, refId);
if (this.isGeoengineAdmin) {
await this.resequenceAndUpdate(dataRowId, refId);
} else {
this.state.geoengineLayers.actives.forEach((element, index) => {
this.onVectorChange(element, "onSequenceChanged", index + 1);
});
}
}
/**
* Resequence the order of layers but not update them (When a user modify them).
* @param {*} dataRowId
* @param {*} refId
*/
resquence(dataRowId, refId) {
const fromIndex = this.state.geoengineLayers.actives.findIndex(
(r) => r.id === dataRowId
);
let toIndex = 0;
if (refId !== null) {
const targetIndex = this.state.geoengineLayers.actives.findIndex(
(r) => r.id === refId
);
toIndex = fromIndex > targetIndex ? targetIndex + 1 : targetIndex;
}
const [record] = this.state.geoengineLayers.actives.splice(fromIndex, 1);
this.state.geoengineLayers.actives.splice(toIndex, 0, record);
}
/**
* Resequence the order of layers and update them (When an admin modify them).
* @param {*} dataRowId
* @param {*} refId
*/
async resequenceAndUpdate(dataRowId, refId) {
this.resequencePromise = this.props.vectorModel.resequence(dataRowId, refId, {
handleField: "sequence",
});
await this.resequencePromise;
this.props.vectorModel.records.forEach((element) => {
this.onVectorChange(element, "onSequenceChanged", element.data.sequence);
});
}
/**
* This is called when a raster layer is changed. The raster layer is set to visible and then
* the method notifies the store of the change.
* @param {*} layer
*/
onRasterChange(layer) {
const indexRaster = rasterLayersStore.rastersLayers.findIndex(
(raster) => raster.name === layer.name
);
const newRasters = rasterLayersStore.rastersLayers.map((item, index) => {
if (index !== indexRaster) {
item.isVisible = false;
} else {
item.isVisible = true;
}
return item;
});
rasterLayersStore.onRasterLayerChanged(newRasters);
}
/**
* This is called when a vector layer is changed. The vector layer is changed by an action and then
* the method notifies the store of the change.
* @param {*} layer
* @param {*} action
* @param {*} value
*/
async onVectorChange(layer, action, value) {
vectorLayersStore.vectorsLayers.forEach((layer) => {
layer.onDomainChanged = false;
layer.onLayerChanged = false;
layer.onSequenceChanged = false;
});
const vectorLayer = vectorLayersStore.getVector(layer.resId);
switch (action) {
case "onDomainChanged":
Object.assign(vectorLayer, {
model_domain: value,
onDomainChanged: true,
});
break;
case "onVisibleChanged":
Object.assign(vectorLayer, {isVisible: value, onVisibleChanged: true});
break;
case "onLayerChanged":
const geo_field_id = await this.orm.call(
vectorLayer.resModel,
"set_field_real_name",
[value.geo_field_id]
);
const attribute_field_id = await this.orm.call(
vectorLayer.resModel,
"set_field_real_name",
[value.attribute_field_id]
);
value.geo_field_id = geo_field_id;
value.attribute_field_id = attribute_field_id;
Object.assign(vectorLayer, {...value, onLayerChanged: true});
break;
case "onSequenceChanged":
if (vectorLayer !== undefined) {
Object.assign(vectorLayer, {
sequence: value,
onSequenceChanged: true,
});
}
break;
}
}
onEditFilterButtonSelected(vector) {
this.addDialog(DomainSelectorGeoFieldDialog, {
resModel: vector.model,
initialValue: vector.model_domain,
readonly: false,
isDebugMode: Boolean(this.env.debug),
model: vector,
onSelected: (value) => this.onEditFilterDomainChanged(vector, value),
title: this.env._t("Domain editing"),
});
}
async onEditFilterDomainChanged(vector, value) {
if (this.isGeoengineAdmin) {
const record = this.props.vectorModel.records.find(
(el) => el.resId === vector.resId
);
await record.update({model_domain: value});
await record.save();
}
this.onVectorChange(vector, "onDomainChanged", value);
}
async onEditButtonSelected(vector) {
const view = await this.rpc("/web/action/load", {
action_id: "base_geoengine.geo_vector_geoengine_view_action",
});
this.addDialog(FormViewDialog, {
resModel: vector.resModel,
title: this.env._t("Editing vector layer"),
viewId: view.view_id[0],
resId: vector.resId,
onRecordSaved: (record) =>
this.onVectorChange(vector, "onLayerChanged", record.data),
});
}
/**
* This method allows you to open/close the panel.
*/
fold() {
this.state.isFolded = !this.state.isFolded;
}
}
LayersPanel.template = "base_geoengine.LayersPanel";
LayersPanel.props = {
model: {type: String, optional: false},
vectorModel: {type: Object, optional: false},
};
LayersPanel.components = {CheckBox};

View file

@ -0,0 +1,37 @@
.btn-edit {
font-size: 1.2em;
}
.o_layer_panel {
width: 250px;
font-size: 1em;
padding-left: 1rem;
}
.raster {
list-style: none;
}
.fold {
font-size: 1.3em;
}
.unfold {
font-size: 1.3em;
opacity: 0.5;
}
.o_layer_panel_fold {
width: 50px;
padding-left: 0;
padding-right: 0;
background-color: #e9ecefd9;
}
.title-panel {
writing-mode: vertical-rl;
text-orientation: mixed;
font-size: 1.3em;
margin-top: 1rem;
opacity: 0.5;
}

View file

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates>
<t t-name="base_geoengine.LayersPanel" owl="1">
<div
class="menu border-end"
t-attf-class="{{ state.isFolded ? 'o_layer_panel_fold ' : 'o_layer_panel bg-view' }}"
>
<div
class="w-100 d-flex"
t-attf-class="{{ state.isFolded ? 'justify-content-center' : 'justify-content-end'}}"
>
<button
class="btn"
t-attf-class="{{state.isFolded ? 'unfold text-900': 'fold'}}"
t-on-click="fold"
>
<i class="fa fa-arrows-h" role="img" />
</button>
</div>
<div t-if="!state.isFolded">
<section class="o_search_panel_section">
<header
class="o_search_panel_section_header text-uppercase cursor-default"
>
<span class="fs-6 fw-bold">Vectors</span>
</header>
<div t-ref="root" class="root">
<ul class="list p-0">
<li
t-foreach="state.geoengineLayers.actives"
t-as="vector"
t-key="vector.resId"
class="item d-flex align-items-center"
t-att-data-id="vector.id"
>
<i class="fa fa-sort m-3" />
<div
class="d-flex justify-content-between align-items-center"
>
<CheckBox
value="vector.isVisible"
t-on-change="() => this.onVectorChange(vector, 'onVisibleChanged', !vector.isVisible)"
>
<t t-esc="vector.name" />
</CheckBox>
<button
t-if="vector.model_id !== false"
class="btn btn-edit"
t-on-click.prevent="() => this.onEditFilterButtonSelected(vector)"
>
<i class="fa fa-filter text-primary" />
</button>
<button
t-if="isGeoengineAdmin"
class="btn btn-edit"
t-on-click.prevent="() => this.onEditButtonSelected(vector)"
>
<i class="fa fa-edit text-primary" />
</button>
</div>
</li>
</ul>
</div>
</section>
<section class="o_search_panel_section">
<header
class="o_search_panel_section_header pt-4 pb-2 text-uppercase cursor-default"
>
<span class="fs-6 fw-bold">Rasters</span>
</header>
<ul class="raster list-group d-block o_search_panel_field">
<li
t-foreach="state.geoengineLayers.backgrounds"
t-as="layer"
t-key="layer.id"
>
<div class="form-check o_radio_item" aria-atomic="true">
<input
type="radio"
t-att-checked="layer.isVisible"
class="form-check-input o_radio_input"
t-att-id="layer.name"
name="raster"
t-att-value="layer.id"
t-on-change="() => this.onRasterChange(layer)"
/>
<label
class="form-check-label o_form_label"
t-att-for="layer.name"
t-esc="layer.name"
/>
</div>
</li>
</ul>
</section>
</div>
<div t-else="" class="d-flex justify-content-center text-900">
<span class="title-panel">Layers (<t t-esc="numberOfLayers" />)</span>
</div>
</div>
</t>
</templates>

View file

@ -0,0 +1,83 @@
/** @odoo-module */
/**
* Copyright 2023 ACSONE SA/NV
*/
import {useService} from "@web/core/utils/hooks";
import {SearchBarRecords} from "./search_bar_records/search_bar_records.esm";
import {
Component,
onWillRender,
onWillStart,
onWillUpdateProps,
useState,
} from "@odoo/owl";
export class RecordsPanel extends Component {
setup() {
this.state = useState({
isFolded: false,
isClicked: 0,
modelDescription: "",
records: [],
});
this.orm = useService("orm");
onWillStart(() => (this.state.records = this.props.list.records));
onWillUpdateProps((nextProps) => (this.state.records = nextProps.list.records));
onWillRender(async () => {
// Retrieves the name of the current model
const result = await this.orm.call("ir.model", "display_name_for", [
[this.props.list.resModel],
]);
this.state.modelDescription = result[0].display_name;
});
}
/**
* This method allows you to open/close the panel.
*/
fold() {
this.state.isFolded = !this.state.isFolded;
this.state.records = this.props.list.records;
}
/**
* This method reacts to the click on a record.
* @param {*} record
*/
onDisplayPopupRecord(record) {
const rec = this.props.list.records.find(
(val) => val._values.id === record.resId
);
this.state.isClicked = record.resId;
this.props.onDisplayPopupRecord(rec);
}
/**
* When you press a key, it automatically performs the search.
* @param {*} value
*/
onInputKeyup(value) {
const val = this.filterItems(value, this.props.list.records);
this.state.records = val;
}
/**
* This method allows you to filter items according to the value passed in parameter.
* @param {*} value
* @param {*} items
* @returns
*/
filterItems(value, items) {
const lowerValue = value.toLowerCase();
return items.filter((item) =>
item.data && item.data.display_name
? item.data.display_name.toLowerCase().indexOf(lowerValue) >= 0
: false
);
}
}
RecordsPanel.template = "base_geoengine.RecordsPanel";
RecordsPanel.components = {SearchBarRecords};

View file

@ -0,0 +1,20 @@
.scroller {
overflow-y: scroll;
scrollbar-width: auto;
}
.record {
cursor: pointer;
}
.record:hover {
background-color: #f6f7fa;
}
.btn-record {
font-weight: normal;
}
.btn-search {
font-size: 1.2em;
}

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates>
<t t-name="base_geoengine.RecordsPanel" owl="1">
<div
class="menu border-end scroller"
t-attf-class="{{ state.isFolded ? 'o_layer_panel_fold ' : 'o_layer_panel bg-view' }}"
>
<div
class="w-100 d-flex"
t-attf-class="{{ state.isFolded ? 'justify-content-center' : 'justify-content-start'}}"
>
<button
class="btn p-0 mb-3"
t-attf-class="{{state.isFolded ? 'unfold text-900': 'fold'}}"
t-on-click="fold"
>
<i class="fa fa-arrows-h" />
</button>
</div>
<div t-if="!state.isFolded">
<section class="o_search_panel_section">
<header
class="o_search_panel_section_header text-uppercase cursor-default mb-2"
>
<span class="fs-6 fw-bold">
<t t-esc="state.modelDescription" />
</span>
</header>
<SearchBarRecords onInputKeyup.bind="onInputKeyup" />
<ul class="raster list-group d-block o_search_panel_field">
<li
class="mb-1 record d-flex justify-content-between"
t-foreach="state.records"
t-as="record"
t-key="record.resId"
>
<button
class="btn btn-record"
t-on-click="() => this.onDisplayPopupRecord(record)"
>
<span>
<t t-esc="record.data.display_name" />
</span>
</button>
<t t-if="state.isClicked === record.resId">
<div>
<button
class="btn btn-search p-0"
t-on-click="() => this.props.zoomOnFeature(record)"
>
<i
class="fa fa-search-plus me-2 text-primary"
/>
</button>
<button
class="btn btn-search p-0"
t-on-click="() => this.props.zoomOutOnFeature(record)"
>
<i
class="fa fa-search-minus me-2 text-primary"
/>
</button>
</div>
</t>
</li>
</ul>
</section>
</div>
<div t-else="" class="d-flex justify-content-center text-900">
<span class="title-panel">Records (<t
t-esc="props.list.records.length"
/>)</span>
</div>
</div>
</t>
</templates>

View file

@ -0,0 +1,25 @@
/** @odoo-module */
/**
* Copyright 2023 ACSONE SA/NV
*/
import {Component, useRef} from "@odoo/owl";
export class SearchBarRecords extends Component {
setup() {
this.searchComponentRef = useRef("searchComponent");
}
/**
* When a key is pressed, the props onInputKeyup method is called.
* @param {*} ev
*/
onInputKeyup(ev) {
this.props.onInputKeyup(this.searchComponentRef.el.value);
ev.preventDefault();
ev.stopPropagation();
}
}
SearchBarRecords.template = "base_geoengine.SearchBarRecords";

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates>
<t t-name="base_geoengine.SearchBarRecords" owl="1">
<div class="o_cp_searchview d-flex flex-grow-1" role="search">
<div
class="o_searchview pb-1 align-self-center border-bottom flex-grow-1"
role="search"
>
<i
class="o_searchview_icon oi oi-search"
role="img"
aria-label="Search..."
title="Search..."
/>
<div class="o_searchview_input_container">
<input
t-ref="searchComponent"
type="text"
class="o_searchview_input"
placeholder="Search..."
t-on-keyup="onInputKeyup"
/>
</div>
</div>
</div>
</t>
</templates>

View file

@ -0,0 +1,34 @@
/** @odoo-module **/
import {DomainField} from "@web/views/fields/domain/domain_field";
import {registry} from "@web/core/registry";
export class DomainFieldExtend extends DomainField {
async loadCount(props) {
if (!this.getResModel(props)) {
Object.assign(this.state, {recordCount: 0, isValid: true});
}
let recordCount = 0;
try {
let value = props.value.slice();
value = value.replace("not in active_ids", "not in");
value = value.replace("in active_ids", "in");
value = value.replace('"{ACTIVE_IDS}"', "[]");
const domain = this.getDomain(value).toList(this.getContext(props));
recordCount = await this.orm.silent.call(
this.getResModel(props),
"search_count",
[domain],
{context: this.getContext(props)}
);
} catch (_e) {
// WOWL TODO: rethrow error when not the expected type
Object.assign(this.state, {recordCount: 0, isValid: false});
return;
}
Object.assign(this.state, {recordCount, isValid: true});
}
}
registry.category("fields").add("domain", DomainFieldExtend, {force: true});

View file

@ -0,0 +1,22 @@
/** @odoo-module **/
/**
* Copyright 2023 ACSONE SA/NV
*/
import {Component, onRendered} from "@odoo/owl";
/**
* It allows you to set a default value for the field and a readonly property for the active_ids value.
*/
export class DomainSelectorFieldInputForActiveIds extends Component {
setup() {
onRendered(() => {
if (this.props.value !== "{ACTIVE_IDS}") {
this.props.update({value: "{ACTIVE_IDS}"});
}
});
}
}
DomainSelectorFieldInputForActiveIds.template =
"base_geoengine.DomainSelectorFieldInputForActiveIds";

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="base_geoengine.DomainSelectorFieldInputForActiveIds" owl="1">
<input
type="text"
class="o_input o_domain_leaf_value_input"
t-att-value="props.value"
t-on-change="onChange"
readonly="1"
/>
</t>
</templates>

View file

@ -0,0 +1,104 @@
/** @odoo-module **/
/**
* Copyright 2023 ACSONE SA/NV
*/
import {_lt} from "@web/core/l10n/translation";
import {registry} from "@web/core/registry";
import {DomainSelectorGeoFieldInput} from "../domain_selector_geo_field_input/domain_selector_geo_field_input.esm";
import {onDidChange} from "../domain_selector_operators.esm";
import {Component} from "@odoo/owl";
const dsf = registry.category("domain_selector/fields");
/**
* This class allows you to adapt the right-hand operand and the operator of the domain
* if the selected field is of type geo_field.
*/
export class DomainSelectorGeoField extends Component {}
Object.assign(DomainSelectorGeoField, {
template: "base_geoengine.DomainSelectorGeoField",
components: {
DomainSelectorGeoFieldInput,
},
onDidTypeChange() {
return {value: ""};
},
getOperators() {
return [
{
category: "geospatial",
label: _lt("geo_contains"),
value: "geo_contains",
onDidChange: onDidChange((fieldChange) => fieldChange()),
matches({operator}) {
return operator === this.value;
},
},
{
category: "geospatial",
label: _lt("geo_greater"),
value: "geo_greater",
onDidChange: onDidChange((fieldChange) => fieldChange()),
matches({operator}) {
return operator === this.value;
},
},
{
category: "geospatial",
label: _lt("geo_lesser"),
value: "geo_lesser",
onDidChange: onDidChange((fieldChange) => fieldChange()),
matches({operator}) {
return operator === this.value;
},
},
{
category: "geospatial",
label: _lt("geo_equal"),
value: "geo_equal",
onDidChange: onDidChange((fieldChange) => fieldChange()),
matches({operator}) {
return operator === this.value;
},
},
{
category: "geospatial",
label: _lt("geo_touch"),
value: "geo_touch",
onDidChange: onDidChange((fieldChange) => fieldChange()),
matches({operator}) {
return operator === this.value;
},
},
{
category: "geospatial",
label: _lt("geo_within"),
value: "geo_within",
onDidChange: onDidChange((fieldChange) => fieldChange()),
matches({operator}) {
return operator === this.value;
},
},
{
category: "geospatial",
label: _lt("geo_intersect"),
value: "geo_intersect",
onDidChange: onDidChange((fieldChange) => fieldChange()),
matches({operator}) {
return operator === this.value;
},
},
];
},
});
dsf.add("geo_multi_polygon", DomainSelectorGeoField);
dsf.add("geo_multi_point", DomainSelectorGeoField);
dsf.add("geo_multi_line", DomainSelectorGeoField);
dsf.add("geo_polygon", DomainSelectorGeoField);
dsf.add("geo_point", DomainSelectorGeoField);
dsf.add("geo_line", DomainSelectorGeoField);

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates xml:space="preserve">
<t t-name="base_geoengine.DomainSelectorGeoField" owl="1">
<div class="o_ds_value_cell">
<DomainSelectorGeoFieldInput t-props="props" />
</div>
</t>
</templates>

View file

@ -0,0 +1,40 @@
/** @odoo-module **/
/**
* Copyright 2023 ACSONE SA/NV
*/
import {DomainSelectorDialog} from "@web/core/domain_selector_dialog/domain_selector_dialog";
import {_t} from "@web/core/l10n/translation";
/**
* This class is extended from DomainSelectorGeoField in order to be able to
* modify the title of the dialog window and to add some props to it.
*/
export class DomainSelectorGeoFieldDialog extends DomainSelectorDialog {
get dialogTitle() {
return _t(this.props.title);
}
}
DomainSelectorGeoFieldDialog.template = "base_geoengine.DomainSelectorGeoFieldDialog";
DomainSelectorGeoFieldDialog.props = {
close: Function,
className: {type: String, optional: true},
resModel: String,
readonly: {type: Boolean, optional: true},
isDebugMode: {type: Boolean, optional: true},
defaultLeafValue: {type: Array, optional: true},
initialValue: {type: String, optional: true},
onSelected: Function,
fieldName: {type: String, optional: true},
title: {type: String, optional: true},
model: {type: Object, optional: true},
};
DomainSelectorGeoFieldDialog.defaultProps = {
initialValue: "",
readonly: true,
isDebugMode: false,
title: "Domain",
};

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates xml:space="preserve">
<t
t-name="base_geoengine.DomainSelectorGeoFieldDialog"
t-inherit="web.DomainSelectorDialog"
t-inherit-mode="primary"
owl="1"
>
</t>
</templates>

View file

@ -0,0 +1,119 @@
/** @odoo-module **/
/**
* Copyright 2023 ACSONE SA/NV
*/
import {ModelFieldSelector} from "@web/core/model_field_selector/model_field_selector";
import {ModelSelector} from "@web/core/model_selector/model_selector";
import {Domain} from "@web/core/domain";
import {evaluate} from "@web/core/py_js/py_interpreter";
import {useOwnedDialogs} from "@web/core/utils/hooks";
import {DomainSelectorGeoFieldDialog} from "../domain_selector_geo_field_dialog/domain_selector_geo_field_dialog.esm";
import {Component, onWillStart, onWillUpdateProps, useState} from "@odoo/owl";
/**
* This class correspond to the value of the right operand when a geo_field has
* been selected.
*/
export class DomainSelectorGeoFieldInput extends Component {
setup() {
this.state = useState({
resModel: "",
fieldName: "",
subField: "",
operator: "",
value: "",
domain: {},
});
this.addDialog = useOwnedDialogs();
/**
* Before starting, if a value is already selected we had to know the fieldName and
* the resModel.
*/
onWillStart(async () => {
if (this.props.value instanceof Object) {
this.defaultKey = Object.keys(this.props.value)[0];
const index = this.defaultKey.lastIndexOf(".");
this.state.fieldName = this.defaultKey.substring(index + 1);
this.state.resModel = this.defaultKey.substring(0, index);
this.loadDomain();
} else {
this.state.value = this.props.value;
}
});
onWillUpdateProps((nextProps) => this.loadDomain(nextProps));
}
/**
* This method allow the domain to be loaded into a state.
* @param {*} nextProps
*/
loadDomain(nextProps) {
const props = nextProps === undefined ? this.props : nextProps;
if (this.defaultKey !== undefined) {
this.key = this.defaultKey;
}
this.state.domain = new Domain(props.value[this.key]);
}
/**
* This method updates the value of the right operand of the domain.
* @param {*} value
*/
update(value) {
this.key = this.state.resModel + "." + this.state.fieldName;
const obj = {};
let jsDomain = [];
if (value !== undefined) {
const domain = new Domain(value);
jsDomain = evaluate(domain.ast, {});
}
obj[this.key] = jsDomain;
this.props.update({value: obj});
}
/**
* This method reacts to changes of the sub field name.
* @param {*} fieldName
*/
async onFieldModelChange(fieldName) {
this.state.fieldName = fieldName;
this.update();
}
/**
* When we click on the edit button, this launches a dialog window allowing you to
* edit the sub-domain.
*/
display() {
const initialValue =
this.state.domain !== undefined ? this.state.domain.toString() : "[]";
this.addDialog(DomainSelectorGeoFieldDialog, {
resModel: this.state.resModel,
initialValue,
readonly: false,
isDebugMode: Boolean(this.env.debug),
fieldName: this.state.fieldName,
onSelected: (value) => this.update(value),
title: this.env._t("Subdomain"),
});
}
/**
* This method react to changes of the sub model.
* @param {*} newModel
*/
onModelChange(newModel) {
this.state.resModel = newModel.technical;
this.state.fieldName = "id";
this.state.subField = "";
this.update();
}
}
DomainSelectorGeoFieldInput.template = "base_geoengine.DomainSelectorGeoFieldInput";
DomainSelectorGeoFieldInput.components = {ModelFieldSelector, ModelSelector};

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="base_geoengine.DomainSelectorGeoFieldInput" owl="1">
<div class="d-flex align-items-center">
<ModelSelector
value="state.resModel"
onModelSelected.bind="onModelChange"
/>
<div t-if="state.resModel.length > 0" class="col-5">
<ModelFieldSelector
fieldName="state.fieldName"
resModel="state.resModel"
readonly="false"
update="(name) => this.onFieldModelChange(name)"
isDebugMode="false"
/>
</div>
<button class="btn btn-link col-1" t-on-click.prevent="display">
<i class="fa fa-pencil-square" />
</button>
</div>
</t>
</templates>

View file

@ -0,0 +1,74 @@
/** @odoo-module **/
/**
* Copyright 2023 ACSONE SA/NV
*/
import {registry} from "@web/core/registry";
import {_lt} from "@web/core/l10n/translation";
import {DomainSelectorFieldInput} from "@web/core/domain_selector/fields/domain_selector_field_input";
import {DomainSelectorFieldInputForActiveIds} from "../domain_selector_field_input_for_active_ids/domain_selector_field_input_for_active_ids.esm";
import {DomainSelectorFieldInputWithTags} from "@web/core/domain_selector/fields/domain_selector_field_input_with_tags";
import {onDidChange} from "../domain_selector_operators.esm";
const dso = registry.category("domain_selector/operator");
import {Component} from "@odoo/owl";
/**
* This method is extended from DomainSelectorNumberField to add some operators
* ("in active_ids", "not in active_ids", "in", "not in").
*/
export class DomainSelectorNumberFieldExtend extends Component {}
Object.assign(DomainSelectorNumberFieldExtend, {
template: "base_geoengine.DomainSelectorNumberFieldExtend",
components: {
DomainSelectorFieldInput,
DomainSelectorFieldInputWithTags,
DomainSelectorFieldInputForActiveIds,
},
onDidTypeChange() {
return {value: 0};
},
getOperators() {
const addOperators = [
{
category: "active_ids",
label: _lt("in active_ids"),
value: "in active_ids",
onDidChange: onDidChange((fieldChange) => fieldChange()),
matches({operator}) {
return operator === this.value;
},
},
{
category: "active_ids",
label: _lt("not in active_ids"),
value: "not in active_ids",
onDidChange: onDidChange((fieldChange) => fieldChange()),
matches({operator}) {
return operator === this.value;
},
},
];
const operators = [
"=",
"!=",
">",
"<",
">=",
"<=",
"ilike",
"not ilike",
"in",
"not in",
"set",
"not set",
].map((key) => dso.get(key));
return operators.concat(addOperators);
},
});
registry
.category("domain_selector/fields")
.add("integer", DomainSelectorNumberFieldExtend, {force: true});

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="base_geoengine.DomainSelectorNumberFieldExtend" owl="1">
<t t-if="props.operator.category === 'in'">
<DomainSelectorFieldInputWithTags t-props="props" />
</t>
<t t-elif="props.operator.category === 'active_ids'">
<DomainSelectorFieldInputForActiveIds
value="props.value"
update="props.update"
/>
</t>
<t t-else="">
<div class="o_ds_value_cell">
<DomainSelectorFieldInput t-props="props" />
</div>
</t>
</t>
</templates>

View file

@ -0,0 +1,13 @@
/** @odoo-module */
/**
* This is method is called when an operator changes its value.
*/
export function onDidChange(action) {
return function (oldOperator, fieldChange) {
if (this.category !== oldOperator.category) {
return action(fieldChange);
}
return {};
};
}

View file

@ -0,0 +1,13 @@
.o_field_geo_multi_polygon,
.o_field_geo_point,
.o_field_geo_polygon,
.o_field_geo_multi_line,
.o_field_geo_multi_point,
.o_field_geo_line {
width: 100%;
height: 550px;
}
.ol_map {
width: 100%;
height: 100%;
}

View file

@ -0,0 +1,324 @@
/** @odoo-module **/
/**
* Copyright 2023 ACSONE SA/NV
*/
import {loadBundle} from "@web/core/assets";
import {registry} from "@web/core/registry";
import {useService} from "@web/core/utils/hooks";
import {standardFieldProps} from "@web/views/fields/standard_field_props";
import {Component, onMounted, onRendered, onWillStart, useEffect} from "@odoo/owl";
export class FieldGeoEngineEditMap extends Component {
setup() {
// Allows you to have a unique id if you put the same field in the view several times
this.id = `map_${Date.now()}`;
this.orm = useService("orm");
onWillStart(() =>
Promise.all([
loadBundle({
jsLibs: [
"/base_geoengine/static/lib/ol-7.2.2/ol.js",
"/base_geoengine/static/lib/chromajs-2.4.2/chroma.js",
],
}),
])
);
// Is executed when component is mounted.
onMounted(async () => {
const result = await this.orm.call(
this.props.record.resModel,
"get_edit_info_for_geo_column",
[this.props.name]
);
this.projection = result.projection;
this.defaultExtent = result.default_extent;
this.defaultZoom = result.default_zoom;
this.restrictedExtent = result.restricted_extent;
this.srid = result.srid;
this.createLayers();
this.renderMap();
this.setValue(this.props.value);
});
useEffect(
() => {
if (!this.props.readonly && this.map !== undefined) {
this.setupControls();
}
},
() => [this.props.value]
);
// Is executed after component is rendered. When we use pagination.
onRendered(() => {
this.setValue(this.props.value);
});
}
/**
* Displays geo data on the map using the collection of features.
*/
createVectorLayer() {
this.features = new ol.Collection();
this.source = new ol.source.Vector({features: this.features});
const colorHex = this.props.color !== undefined ? this.props.color : "#ee9900";
const opacity = this.props.opacity !== undefined ? this.props.opacity : 1;
const color = chroma(colorHex).alpha(opacity).css();
const fill = new ol.style.Fill({
color: color,
});
const stroke = new ol.style.Stroke({
color,
width: 2,
});
return new ol.layer.Vector({
source: this.source,
style: new ol.style.Style({
fill,
stroke,
image: new ol.style.Circle({
radius: 5,
fill,
stroke,
}),
}),
});
}
/**
* Call the method that creates the layer to display the geo data on the map.
*/
createLayers() {
this.vectorLayer = this.createVectorLayer();
}
/**
* Allows you to centre the area defined for the user.
* If there is an item to display.
*/
updateMapZoom() {
if (this.source) {
var extent = this.source.getExtent();
var infinite_extent = [Infinity, Infinity, -Infinity, -Infinity];
if (extent !== infinite_extent) {
var map_view = this.map.getView();
if (map_view) {
map_view.fit(extent, {maxZoom: 14});
}
}
}
}
/**
* Allows you to centre the area defined for the user.
* If there is not item to display.
*/
updateMapEmpty() {
var map_view = this.map.getView();
if (map_view) {
var extent = this.defaultExtent.replace(/\s/g, "").split(",");
extent = extent.map((coord) => Number(coord));
map_view.fit(extent, {maxZoom: this.defaultZoom || 5});
}
}
/**
* Based on the value passed in props, adds a new feature to the collection.
* @param {*} value
*/
setValue(value) {
if (this.map) {
/**
* If the value to be displayed is equal to the one passed in props, do nothing
* otherwise clear the map and display the new value.
*/
if (this.displayValue == value) return;
this.displayValue = value;
var ft = new ol.Feature({
geometry: new ol.format.GeoJSON().readGeometry(value),
labelPoint: new ol.format.GeoJSON().readGeometry(value),
});
this.source.clear();
this.source.addFeature(ft);
if (value) {
this.updateMapZoom();
} else {
this.updateMapEmpty();
}
}
}
/**
* This is triggered when the view changed. When we have finished drawing our geo data, or
* when we clear the map.
* @param {*} geometry
*/
onUIChange(geometry) {
var value = null;
if (geometry) {
value = this.format.writeGeometry(geometry);
}
this.props.update(value);
}
/**
* Allow you to setup the trash button and the draw interaction.
*/
setupControls() {
if (!this.props.value) {
void (
this.selectInteraction !== undefined &&
this.map.removeInteraction(this.selectInteraction)
);
void (
this.modifyInteraction !== undefined &&
this.map.removeInteraction(this.modifyInteraction)
);
this.drawInteraction = new ol.interaction.Draw({
type: this.geoType,
source: this.source,
});
this.map.addInteraction(this.drawInteraction);
this.drawInteraction.on("drawend", (e) => {
this.onUIChange(e.feature.getGeometry());
});
} else {
void (
this.drawInteraction !== undefined &&
this.map.removeInteraction(this.drawInteraction)
);
this.selectInteraction = new ol.interaction.Select();
this.modifyInteraction = new ol.interaction.Modify({
features: this.selectInteraction.getFeatures(),
});
this.map.addInteraction(this.selectInteraction);
this.map.addInteraction(this.modifyInteraction);
this.modifyInteraction.on("modifyend", (e) => {
e.features.getArray().forEach((item) => {
this.onUIChange(item.getGeometry());
});
});
}
const element = this.createTrashControl();
this.clearmapControl = new ol.control.Control({element: element});
this.map.addControl(this.clearmapControl);
}
/**
* Create the trash button that clears the map.
* @returns the div in which the button is located.
*/
createTrashControl() {
const button = document.createElement("button");
button.innerHTML = '<i class="fa fa-trash"/>';
button.addEventListener("click", () => {
this.source.clear();
this.onUIChange(null);
});
const element = document.createElement("div");
element.className = "ol-clear ol-unselectable ol-control";
element.appendChild(button);
return element;
}
/**
* Displays the map in the div provided.
*/
renderMap() {
this.map = new ol.Map({
target: this.id,
layers: [
new ol.layer.Tile({
source: new ol.source.OSM(),
}),
],
view: new ol.View({
center: [0, 0],
zoom: 5,
}),
});
this.map.addLayer(this.vectorLayer);
this.format = new ol.format.GeoJSON({
internalProjection: this.map.getView().getProjection(),
externalProjection: "EPSG:" + this.srid,
});
if (!this.props.readonly) {
this.setupControls();
}
}
}
FieldGeoEngineEditMap.template = "base_geoengine.FieldGeoEngineEditMap";
FieldGeoEngineEditMap.props = {
...standardFieldProps,
opacity: {type: Number, optional: true},
color: {type: String, optional: true},
};
FieldGeoEngineEditMap.extractProps = ({attrs}) => {
return {
opacity: attrs.options.opacity,
color: attrs.options.color,
};
};
export class FieldGeoEngineEditMapMultiPolygon extends FieldGeoEngineEditMap {
setup() {
this.geoType = "MultiPolygon";
super.setup();
}
}
export class FieldGeoEngineEditMapPolygon extends FieldGeoEngineEditMap {
setup() {
this.geoType = "Polygon";
super.setup();
}
}
export class FieldGeoEngineEditMapPoint extends FieldGeoEngineEditMap {
setup() {
this.geoType = "Point";
super.setup();
}
}
export class FieldGeoEngineEditMapMultiPoint extends FieldGeoEngineEditMap {
setup() {
this.geoType = "MultiPoint";
super.setup();
}
}
export class FieldGeoEngineEditMapLine extends FieldGeoEngineEditMap {
setup() {
this.geoType = "LineString";
super.setup();
}
}
export class FieldGeoEngineEditMapMultiLine extends FieldGeoEngineEditMap {
setup() {
this.geoType = "MultiLineString";
super.setup();
}
}
registry.category("fields").add("geo_multi_polygon", FieldGeoEngineEditMapMultiPolygon);
registry.category("fields").add("geo_polygon", FieldGeoEngineEditMapPolygon);
registry.category("fields").add("geo_point", FieldGeoEngineEditMapPoint);
registry.category("fields").add("geo_multi_point", FieldGeoEngineEditMapMultiPoint);
registry.category("fields").add("geo_line", FieldGeoEngineEditMapLine);
registry.category("fields").add("geo_multi_line", FieldGeoEngineEditMapMultiLine);

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates xml:space="preserve">
<t t-name="base_geoengine.FieldGeoEngineEditMap" owl="1">
<div class="ol_map" t-att-id="id" />
</t>
</templates>

View file

@ -0,0 +1,2 @@
# Copyright 2023 ACSONE SA/NV
from . import test_model

View file

@ -0,0 +1,40 @@
# Copyright 2023 ACSONE SA/NV
from odoo import fields, models
class GeoModelTest(models.Model):
"""GeoModel for testing"""
_name = "geo.model.test"
name = fields.Char("GeoModelTest")
geo_multi_polygon = fields.GeoMultiPolygon()
geo_polygon = fields.GeoPolygon()
geo_line = fields.GeoLine()
geo_point = fields.GeoPoint()
geo_multi_line = fields.GeoMultiLine()
geo_multi_point = fields.GeoMultiPoint()
class DummyZip(models.Model):
"""DummyZip model for testing"""
_name = "dummy.zip"
_description = "Geoengine demo ZIP"
name = fields.Char("ZIP", index=True, required=True)
city = fields.Char(index=True, required=True)
the_geom = fields.GeoMultiPolygon("NPA Shape")
the_poly = fields.GeoPolygon()
class RetailMachine(models.Model):
"""RetailMachine model for testing"""
_name = "retail.machine"
_description = "Geoengine demo retailing machine"
the_point = fields.GeoPoint("Coordinate")
total_sales = fields.Float("Total sale", index=True)
money_level = fields.Char(index=True)
state = fields.Selection([("hs", "HS"), ("ok", "OK")], index=True)
name = fields.Char("Serial number", required=True)

View file

@ -0,0 +1,704 @@
# Copyright 2023 ACSONE SA/NV
import geojson
from odoo_test_helper import FakeModelLoader
from shapely import wkt
from shapely.geometry import shape
from odoo.tests.common import TransactionCase
from ..fields import GeoPoint
class TestModel(TransactionCase):
@classmethod
def setUpClass(cls):
super(TestModel, cls).setUpClass()
cls.loader = FakeModelLoader(cls.env, cls.__module__)
cls.loader.backup_registry()
from .models import DummyZip, GeoModelTest, RetailMachine
cls.loader.update_registry((GeoModelTest, DummyZip, RetailMachine))
cls.geo_model = cls.env["geo.model.test"].create({})
cls.env["dummy.zip"].create(
{
"name": "1146",
"city": "Mollens (VD))",
"the_geom": "MULTIPOLYGON (((711089.693203108 5873735.48072837, "
+ "711092.576313001 5873519.88384687,"
+ "711067.054090924 5873435.51305599,710995.218898879 5873198.04330687,"
+ "710996.388615927 5873110.64387336,711039.179454242 5873060.22832024,"
+ "711042.287896265 5872936.43418925,711026.423105771 5872819.67065428,"
+ "711261.754450894 5872597.025222,711241.609394529 5872474.38185775,"
+ "711059.74912495 5872499.61499977,710956.020676372 5872329.22952511,"
+ "710692.244689654 5872290.70879558,710486.478264092 5871933.93784652,"
+ "710024.93171231 5872025.28274803,710005.024342666 5871778.82753806,"
+ "709728.110667306 5871639.59028651,709724.582871056 5871578.36223351,"
+ "709682.212415895 5871490.38595657,709610.808017364 5871402.01470814,"
+ "709552.306572401 5871356.73905814,709495.853317159 5871313.04976156,"
+ "709236.521364341 5871163.84756429,709195.304008703 5871128.42409022,"
+ "709150.409305552 5871089.84044459,709093.837273492 5871008.051534,"
+ "709008.015634898 5870883.97195414,708922.106609628 5870795.4022844,"
+ "708836.258869397 5870702.46391409,708174.672218121 5871197.33373336,"
+ "707846.164800298 5870800.97891085,707605.589841331 5870979.71730235,"
+ "706369.331113847 5871966.12906172,706306.149363559 5872332.34885008,"
+ "705390.804735349 5873401.94343156,704603.284854186 5873838.0955764,"
+ "704092.91256737 5873980.90856737,704011.87512573 5873857.35915706,"
+ "703030.73225885 5874621.40143472,702006.482932752 5874937.39404808,"
+ "702849.34839414 5875420.27638601,705799.405382342 5875341.31671473,"
+ "707632.65678248 5875553.44607092,708858.019414722 5875286.16417218,"
+ "709639.976192618 5874932.54154212,709800.865455979 5874636.00773277,"
+ "710188.467536495 5874437.27183537,710138.406154331 5874271.93819522,"
+ "710464.959230515 5874290.93364989,711089.693203108 5873735.48072837)))",
}
)
cls.env["dummy.zip"].create(
{
"name": "1169",
"city": "Yens",
"the_geom": "MULTIPOLYGON (((712499.646588959 5867275.9182732,"
+ "713419.026718887 5867001.27747293,714015.722251894 5866538.88540113,"
+ "714694.030364397 5866492.43868073,714765.262342783 5865922.7082706,"
+ "714997.785886538 5865558.90341594,715151.666992536 5865665.71173242,"
+ "715609.031373263 5865079.20249973,715885.038705088 5865053.65786512,"
+ "716495.789172412 5864492.40980924,716976.367311019 5864008.08379334,"
+ "716781.749152281 5863910.99389739,716608.651725694 5863833.09642092,"
+ "716518.052861577 5863201.78267731,716064.089435707 5863535.02734419,"
+ "715643.594094414 5862954.76617529,715325.079468014 5863244.60059218,"
+ "715203.241075229 5863130.96403033,715274.223325112 5862913.60004965,"
+ "714897.772002756 5862635.12980843,714563.017533803 5862610.39226806,"
+ "714258.266707042 5862620.95887512,714249.030777221 5862438.94330443,"
+ "713724.197313172 5862641.59763197,713517.069387174 5862507.90536999,"
+ "712861.144693087 5862860.12184101,713026.004126525 5862898.68588355,"
+ "712886.267874965 5863045.26900106,712827.103281182 5863131.79990304,"
+ "712738.929710136 5863217.94640341,712691.900706143 5863250.04961387,"
+ "712592.546151257 5863317.87261312,712504.368398659 5863404.01855574,"
+ "712405.215033586 5863533.04505435,712371.521154897 5863576.88960266,"
+ "712355.411589612 5863605.09059754,712223.007660214 5863836.87550522,"
+ "712156.578632893 5863923.31295655,712134.435332338 5863952.12551606,"
+ "712134.049617032 5863981.22835121,712079.319781245 5864066.52919356,"
+ "712059.980001849 5864096.67295644,712051.318250669 5864144.84287664,"
+ "712044.316598475 5864183.7895569,711908.743067266 5864560.40002656,"
+ "711809.521376786 5864821.06690256,711185.861440992 5865020.84754387,"
+ "710829.505021716 5865627.41104201,710236.389289627 5865815.90923937,"
+ "710091.432898977 5866557.84150433,709133.629951634 5866771.90114324,"
+ "709458.164419682 5866919.01207252,709913.809001053 5867137.78068809,"
+ "710889.333222594 5868419.19110438,711446.96086477 5867965.10731405,"
+ "711555.097159712 5867474.42262634,711838.254231148 5867572.85286263,"
+ "711873.968689887 5867287.95989594,712499.646588959 5867275.9182732)))",
}
)
cls.env["retail.machine"].create(
{
"name": "34",
"total_sales": 734.0,
"money_level": "low",
"the_point": "POINT(711341.795470746 5866150.25857961)",
"state": "ok",
}
)
cls.env["retail.machine"].create(
{
"name": "33",
"total_sales": 492.0,
"money_level": "low",
"the_point": "POINT(711652.69365366 5866548.27641024)",
"state": "ok",
}
)
cls.env["retail.machine"].create(
{
"name": "18",
"total_sales": 892.0,
"money_level": "high",
"the_point": "POINT(708451.36372351 5872547.88349207)",
"state": "ok",
}
)
cls.env["retail.machine"].create(
{
"name": "23",
"total_sales": 1781.0,
"money_level": "high",
"the_point": "POINT(708037.67602773 5873788.9465794)",
"state": "ok",
}
)
cls.env["retail.machine"].create(
{
"name": "21",
"total_sales": 1533.0,
"money_level": "high",
"the_point": "POINT(706962.088018701 5873623.47150109)",
"state": "ok",
}
)
@classmethod
def tearDownClass(cls):
cls.loader.restore_registry()
super(TestModel, cls).tearDownClass()
def test_create_multipolygon_wkt_format(self):
"""Create a multi polygon"""
multi_polygon = """MULTIPOLYGON (((726469.938970306 5873145.03456248,
726520.174105436 5872946.03059195,726394.076006275 5872675.04267336,
726404.709252018 5872259.98652277,726212.557712441 5871801.78900894,
726471.645243121 5871748.01187117,726384.798016214 5871474.60597039,
726524.862821204 5871416.52279756,726560.408193573 5870936.9668625,
726765.583679957 5870941.58021817,726800.675791552 5870794.8818083,
726830.398385972 5870610.25496982,726667.324961485 5870525.33567258,
726559.158856422 5870713.41380138,726483.618814776 5870842.15916191,
726282.880000044 5870874.76667995,726075.942331799 5870940.79690473,
726060.620082161 5870636.20922038,725995.373875086 5870504.36168216,
725871.917698916 5870508.73664946,725866.910956205 5870440.22483982,
725760.074408205 5870513.2465806,724947.130321146 5870506.55170525,
724956.64886783 5870684.35364044,724709.437725183 5870717.82930599,
724640.837901402 5870502.91208435,723632.716917063 5871035.58668793,
723353.587063504 5870580.71145654,722975.340414945 5870644.60022452,
723063.193608096 5871187.48904965,723286.565782157 5871691.25884814,
722865.254617585 5871951.28387527,722963.962738856 5872074.84140304,
722831.245902295 5872122.76389775,722813.837916803 5872242.00526964,
722827.655983236 5872300.44254147,722913.370119188 5872418.02105384,
722942.233783707 5872432.93796481,722984.741992683 5872520.85895895,
723012.381471722 5872637.73702376,723086.082443995 5872667.76380156,
723157.229378648 5872668.62290333,723214.748202491 5872698.34771906,
723250.508284339 5872761.5280976,723286.16249158 5872815.86084868,
723315.028102666 5872830.77719621,723502.328395902 5872819.6206505,
723518.483057357 5872818.65863173,723567.82777813 5872844.15493286,
723749.413939088 5872937.98130854,723848.429817139 5873019.41326079,
723893.228455684 5873056.25562596,724000.782399525 5873179.05937195,
724022.179653074 5873203.48913931,724167.213521721 5873219.79321866,
724240.443943894 5873257.62460164,724345.033870514 5873311.65740904,
724427.033053176 5873354.0176493,724458.116953997 5873354.38826826,
724601.342527281 5873356.09453768,724659.368258544 5873356.78513641,
724699.259216658 5873263.86039926,724734.038257937 5873182.84407915,
724769.196071982 5873148.4004069,724792.808935311 5873125.2670227,
724828.594968331 5873107.84535952,724909.660761314 5873068.37954232,
724968.429515518 5873010.80204763,724969.116329931 5872952.53572905,
725027.883915669 5872894.95858978,725144.902548535 5872823.50310579,
725202.982873244 5872824.19072548,725231.165281494 5872845.9215328,
725260.549561619 5872868.5770444,725323.492507265 5872998.67536266,
725345.617620278 5873044.40573114,725374.316461685 5873073.88235996,
725461.097697554 5873104.044911,725548.221016413 5873105.07306152,
725593.723206956 5873117.05713947,725633.818030441 5873127.61507632,
725664.044374598 5873135.57621915,725715.023293758 5873205.45921561,
725835.567474206 5873370.70034401,725849.407491127 5873429.14029691,
725878.279489341 5873444.04917682,725936.874293492 5873401.02996477,
725995.978527806 5873314.30901098,726070.62161125 5873140.35732493,
726085.312084189 5873125.96087534,726100.002508819 5873111.56442448,
726217.015284978 5873040.09276485,726304.138055426 5873041.11333845,
726469.938970306 5873145.03456248)))"""
multi_polygon_loaded = wkt.loads(multi_polygon)
self.geo_model.geo_multi_polygon = multi_polygon
self.assertTrue(
self.geo_model.geo_multi_polygon.equals_exact(
multi_polygon_loaded, tolerance=0.2
)
)
def test_create_multipolygon_geojson_format(self):
multi_polygon_geojson = """{"type":"MultiPolygon","coordinates":[[[
[726469.938970306,5873145.03456248],[726520.174105436,5872946.03059195],
[726394.076006275,5872675.04267336],[726404.709252018,5872259.98652277],
[726212.557712441,5871801.78900894],[726471.645243121,5871748.01187117],
[726384.798016214,5871474.60597039],[726524.862821204,5871416.52279756],
[726560.408193573,5870936.9668625],[726765.583679957,5870941.58021817],
[726800.675791552,5870794.8818083],[726830.398385972,5870610.25496982],
[726667.324961485,5870525.33567258],[726559.158856422,5870713.41380138],
[726483.618814776,5870842.15916191],[726282.880000044,5870874.76667995],
[726075.942331799,5870940.79690473],[726060.620082161,5870636.20922038],
[725995.373875086,5870504.36168216],[725871.917698916,5870508.73664946],
[725866.910956205,5870440.22483982],[725760.074408205,5870513.2465806],
[724947.130321146,5870506.55170525],[724956.64886783,5870684.35364044],
[724709.437725183,5870717.82930599],[724640.837901402,5870502.91208435],
[723632.716917063,5871035.58668793],[723353.587063504,5870580.71145654],
[722975.340414945,5870644.60022452],[723063.193608096,5871187.48904965],
[723286.565782157,5871691.25884814],[722865.254617585,5871951.28387527],
[722963.962738856,5872074.84140304],[722831.245902295,5872122.76389775],
[722813.837916803,5872242.00526964],[722827.655983236,5872300.44254147],
[722913.370119188,5872418.02105384],[722942.233783707,5872432.93796481],
[722984.741992683,5872520.85895895],[723012.381471722,5872637.73702376],
[723086.082443995,5872667.76380156],[723157.229378648,5872668.62290333],
[723214.748202491,5872698.34771906],[723250.508284339,5872761.5280976],
[723286.16249158,5872815.86084868],[723315.028102666,5872830.77719621],
[723502.328395902,5872819.6206505],[723518.483057357,5872818.65863173],
[723567.82777813,5872844.15493286],[723749.413939088,5872937.98130854],
[723848.429817139,5873019.41326079],[723893.228455684,5873056.25562596],
[724000.782399525,5873179.05937195],[724022.179653074,5873203.48913931],
[724167.213521721,5873219.79321866],[724240.443943894,5873257.62460164],
[724345.033870514,5873311.65740904],[724427.033053176,5873354.0176493],
[724458.116953997,5873354.38826826],[724601.342527281,5873356.09453768],
[724659.368258544,5873356.78513641],[724699.259216658,5873263.86039926],
[724734.038257937,5873182.84407915],[724769.196071982,5873148.4004069],
[724792.808935311,5873125.2670227],[724828.594968331,5873107.84535952],
[724909.660761314,5873068.37954232],[724968.429515518,5873010.80204763],
[724969.116329931,5872952.53572905],[725027.883915669,5872894.95858978],
[725144.902548535,5872823.50310579],[725202.982873244,5872824.19072548],
[725231.165281494,5872845.9215328],[725260.549561619,5872868.5770444],
[725323.492507265,5872998.67536266],[725345.617620278,5873044.40573114],
[725374.316461685,5873073.88235996],[725461.097697554,5873104.044911],
[725548.221016413,5873105.07306152],[725593.723206956,5873117.05713947],
[725633.818030441,5873127.61507632],[725664.044374598,5873135.57621915],
[725715.023293758,5873205.45921561],[725835.567474206,5873370.70034401],
[725849.407491127,5873429.14029691],[725878.279489341,5873444.04917682],
[725936.874293492,5873401.02996477],[725995.978527806,5873314.30901098],
[726070.62161125,5873140.35732493],[726085.312084189,5873125.96087534],
[726100.002508819,5873111.56442448],[726217.015284978,5873040.09276485],
[726304.138055426,5873041.11333845],[726469.938970306,5873145.03456248]]]]}"""
geo_dict = geojson.loads(multi_polygon_geojson)
multi_polygon_loaded = shape(geo_dict)
self.geo_model.geo_multi_polygon = multi_polygon_geojson
self.assertTrue(
self.geo_model.geo_multi_polygon.equals_exact(
multi_polygon_loaded, tolerance=0.2
)
)
def test_create_polygon_wkt_format(self):
"""Create a polygon"""
polygon = """POLYGON((-95.80078125000001 40.09488212232117,
-95.07568359375001 36.68604127658193,
-90.439453125 37.80544394934273,-91.03271484375001 39.97712009843963,
-95.80078125000001 40.09488212232117))"""
polygon_loaded = wkt.loads(polygon)
self.geo_model.geo_polygon = polygon
self.assertTrue(
self.geo_model.geo_polygon.equals_exact(polygon_loaded, tolerance=0.2)
)
def test_create_polygon_geojson_format(self):
polygon_geojson = """{"type":"Polygon","coordinates":[[
[-95.80078125000001,40.09488212232117],
[-95.07568359375001,36.68604127658193],[-90.439453125,37.80544394934273],
[-91.03271484375001,39.97712009843963],[-95.80078125000001,40.09488212232117]]]}"""
geo_dict = geojson.loads(polygon_geojson)
polygon_loaded = shape(geo_dict)
self.geo_model.geo_polygon = polygon_geojson
self.assertTrue(
self.geo_model.geo_polygon.equals_exact(polygon_loaded, tolerance=0.2)
)
def test_create_line_wkt_format(self):
"""Create a line"""
line = """LINESTRING(-98.81103515625001 38.97649248553944,
-90.06591796875 38.99357205820945)"""
self.geo_model.geo_line = line
line_loaded = wkt.loads(line)
self.assertTrue(
self.geo_model.geo_line.equals_exact(line_loaded, tolerance=0.2)
)
def test_create_line_geosjon_format(self):
line_geojson = """{"type":"LineString","coordinates":[
[-98.81103515625001,38.97649248553944],
[-90.06591796875,38.99357205820945]]}"""
geo_dict = geojson.loads(line_geojson)
line_loaded = shape(geo_dict)
self.geo_model.geo_line = line_geojson
self.assertTrue(
self.geo_model.geo_line.equals_exact(line_loaded, tolerance=0.2)
)
def test_create_point_wkt_format(self):
"""Create a point"""
point = "POINT(-99.2724609375 38.25543637637949)"
self.geo_model.geo_point = point
point_loaded = wkt.loads(point)
self.assertTrue(
self.geo_model.geo_point.equals_exact(point_loaded, tolerance=0.2)
)
def test_create_point_geosjon_format(self):
point_geosjon = (
'{ "type": "Point", "coordinates": [-99.2724609375, 38.25543637637949] }'
)
geo_dict = geojson.loads(point_geosjon)
point_loaded = shape(geo_dict)
self.geo_model.geo_point = point_geosjon
self.assertTrue(
self.geo_model.geo_point.equals_exact(point_loaded, tolerance=0.2)
)
def test_create_multi_line_wkt_format(self):
"""Create multiline"""
multi_line = (
"MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))"
)
self.geo_model.geo_multi_line = multi_line
multi_line_loaded = wkt.loads(multi_line)
self.assertTrue(
self.geo_model.geo_multi_line.equals_exact(multi_line_loaded, tolerance=0.2)
)
def test_create_multi_line_geojson_format(self):
multi_line_geojson = """{"type":"MultiLineString","coordinates":[[
[10,10],[20,20],[10,40]],[[40,40],[30,30],[40,20],[30,10]]]}"""
geo_dict = geojson.loads(multi_line_geojson)
multi_line_loaded = shape(geo_dict)
self.geo_model.geo_multi_line = multi_line_geojson
self.assertTrue(
self.geo_model.geo_multi_line.equals_exact(multi_line_loaded, tolerance=0.2)
)
def test_create_multi_point_wkt_format(self):
"""Create multipoint"""
multi_point = "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))"
self.geo_model.geo_multi_point = multi_point
multi_point_loaded = wkt.loads(multi_point)
self.assertTrue(
self.geo_model.geo_multi_point.equals_exact(
multi_point_loaded, tolerance=0.2
)
)
def test_create_multi_point_geosjson_format(self):
multi_point_geojson = (
'{"type":"MultiPoint","coordinates":[[10,40],[40,30],[20,20],[30,10]]}'
)
geo_dict = geojson.loads(multi_point_geojson)
multi_point_loaded = shape(geo_dict)
self.geo_model.geo_multi_point = multi_point_geojson
self.assertTrue(
self.geo_model.geo_multi_point.equals_exact(
multi_point_loaded, tolerance=0.2
)
)
def test_delete(self):
self.geo_model.unlink()
self.assertFalse(self.geo_model.exists())
def test_search_intersect_for_zip_1169(self):
retails = self.env["retail.machine"]
zip_item = self.env["dummy.zip"].search([("name", "ilike", "1169")])
result = retails.search([("the_point", "geo_intersect", zip_item.the_geom)])
self.assertEqual(len(result.ids), 2)
result = retails.search(
[
(
"the_point",
"geo_intersect",
{"dummy.zip.the_geom": [("id", "=", zip_item.id)]},
)
]
)
self.assertEqual(len(result.ids), 2)
def test_search_intersect_with_dict_id(self):
retails = self.env["retail.machine"]
result = retails.search(
[
(
"the_point",
"geo_intersect",
{"dummy.zip.the_geom": [("id", "in", ["1"])]},
)
]
)
self.assertEqual(len(result.ids), 3)
result = retails.search(
[
(
"the_point",
"geo_intersect",
{"dummy.zip.the_geom": [("id", "in", ["2"])]},
)
]
)
self.assertEqual(len(result.ids), 2)
result = retails.search(
[
(
"the_point",
"geo_intersect",
{"dummy.zip.the_geom": [("id", "in", ["1", "2"])]},
)
]
)
self.assertEqual(len(result.ids), 5)
def test_search_intersect_for_zip_1169_with_multiple_domain(self):
retails = self.env["retail.machine"]
result = retails.search(
[
(
"the_point",
"geo_intersect",
{
"dummy.zip.the_geom": [
("id", "in", ["1", "2"]),
("name", "ilike", "1169"),
]
},
)
]
)
self.assertEqual(len(result.ids), 2)
def test_search_intersect_for_zip_1149(self):
retails = self.env["retail.machine"]
zip_item = self.env["dummy.zip"].search([("name", "ilike", "1146")])
result = retails.search([("the_point", "geo_intersect", zip_item.the_geom)])
self.assertEqual(len(result.ids), 3)
def test_search_contains_for_retails_34(self):
zip_item = self.env["dummy.zip"]
retails = self.env["retail.machine"].search([("name", "=", "34")])
result = zip_item.search([("the_geom", "geo_contains", retails.the_point)])
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Yens")
result = zip_item.search(
[
(
"the_geom",
"geo_contains",
{"retail.machine.the_point": [("id", "=", retails.id)]},
)
]
)
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Yens")
def test_search_contains_for_retails_21(self):
zip_item = self.env["dummy.zip"]
retails = self.env["retail.machine"].search([("name", "ilike", "21")])
result = zip_item.search([("the_geom", "geo_contains", retails.the_point)])
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Mollens (VD))")
def test_search_within_for_retails_34(self):
retails = self.env["retail.machine"]
zip_item = self.env["dummy.zip"].search([("city", "ilike", "Yens")])
result = retails.search(
[("name", "ilike", "34"), ("the_point", "geo_within", zip_item.the_geom)]
)
self.assertEqual(1, len(result))
self.assertEqual(result.name, "34")
result = retails.search(
[
("name", "ilike", "34"),
(
"the_point",
"geo_within",
{"dummy.zip.the_geom": [("id", "=", zip_item.id)]},
),
]
)
self.assertEqual(1, len(result))
self.assertEqual(result.name, "34")
def test_search_within_for_retails_21(self):
retails = self.env["retail.machine"]
zip_item = self.env["dummy.zip"].search([("city", "ilike", "Mollens (VD))")])
result = retails.search(
[("name", "ilike", "21"), ("the_point", "geo_within", zip_item.the_geom)]
)
self.assertEqual(1, len(result))
self.assertEqual(result.name, "21")
def test_search_equals(self):
zip_item = self.env["dummy.zip"].search([("city", "ilike", "Mollens (VD))")])
result = zip_item.search(
[
(
"the_geom",
"geo_equal",
{"dummy.zip.the_geom": [("name", "ilike", "1146")]},
),
]
)
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Mollens (VD))")
def test_search_touch_polygon(self):
zip_item = self.env["dummy.zip"]
self.env["dummy.zip"].create(
{
"name": "10000",
"city": "Poly1",
"the_poly": "POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))",
}
)
poly2 = self.env["dummy.zip"].create(
{
"name": "11000",
"city": "Poly2",
"the_poly": "POLYGON ((1 0, 1 1, 2 1, 2 0, 1 0))",
}
)
result = zip_item.search([("the_poly", "geo_touch", poly2.the_poly)])
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Poly1")
result = zip_item.search(
[("the_poly", "geo_touch", {"dummy.zip.the_poly": [("id", "=", poly2.id)]})]
)
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Poly1")
def test_search_touch_multi_polygon(self):
zip_item = self.env["dummy.zip"]
self.env["dummy.zip"].create(
{
"name": "1192",
"city": "Multi1",
"the_geom": """MULTIPOLYGON (((0 0, 0 1, 1 1, 1 0, 0 0)),
((1 0, 1 1, 2 1, 2 0, 1 0)))""",
}
)
multi_poly_2 = self.env["dummy.zip"].create(
{
"name": "6708",
"city": "Multi2",
"the_geom": """MULTIPOLYGON (((2 0, 2 1, 3 1, 3 0, 2 0)),
((3 0, 3 1, 4 1, 4 0, 3 0)))""",
}
)
result = zip_item.search([("the_geom", "geo_touch", multi_poly_2.the_geom)])
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Multi1")
def test_search_greater_multi_polygon(self):
zip_item = self.env["dummy.zip"]
mp1 = self.env["dummy.zip"].create(
{
"name": "1192",
"city": "Mp1",
"the_geom": """MULTIPOLYGON (((0 0, 0 5, 5 5, 5 0, 0 0)),
((1 1, 2 1, 2 2, 1 2, 1 1)), ((3 3, 4 3, 4 4, 3 4, 3 3)))""",
}
)
self.env["dummy.zip"].create(
{
"name": "6708",
"city": "Mp2",
"the_geom": """MULTIPOLYGON (((-5 -5, -5 10, 10 10, 10 -5, -5 -5)),
((-4 -4, -3 -4, -3 -3, -4 -3, -4 -4)), ((6 6, 7 6, 7 7, 6 7, 6 6)))""",
}
)
result = zip_item.search(
[("city", "ilike", "Mp2"), ("the_geom", "geo_greater", mp1.the_geom)]
)
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Mp2")
result = zip_item.search(
[
("city", "ilike", "Mp2"),
(
"the_geom",
"geo_greater",
{"dummy.zip.the_geom": [("id", "=", mp1.id)]},
),
]
)
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Mp2")
def test_search_lesser_multi_polygon(self):
zip_item = self.env["dummy.zip"]
self.env["dummy.zip"].create(
{
"name": "1192",
"city": "Mp1",
"the_geom": """MULTIPOLYGON (((0 0, 0 5, 5 5, 5 0, 0 0)),
((1 1, 2 1, 2 2, 1 2, 1 1)), ((3 3, 4 3, 4 4, 3 4, 3 3)))""",
}
)
mp2 = self.env["dummy.zip"].create(
{
"name": "6708",
"city": "Mp2",
"the_geom": """MULTIPOLYGON (((-5 -5, -5 10, 10 10, 10 -5, -5 -5)),
((-4 -4, -3 -4, -3 -3, -4 -3, -4 -4)), ((6 6, 7 6, 7 7, 6 7, 6 6)))""",
}
)
result = zip_item.search(
[("city", "ilike", "Mp1"), ("the_geom", "geo_lesser", mp2.the_geom)]
)
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Mp1")
result = zip_item.search(
[
("city", "ilike", "Mp1"),
(
"the_geom",
"geo_lesser",
{"dummy.zip.the_geom": [("id", "=", mp2.id)]},
),
]
)
self.assertEqual(1, len(result))
self.assertEqual(result.city, "Mp1")
def test_from_lat_lon(self):
latitude = 49.72842315886126
longitude = 5.400488376617026
# This is computed with postgis in postgres:
expected_coordinates = [601179.61612, 6399375.681364]
geo_point = GeoPoint.from_latlon(self.env.cr, latitude, longitude)
self.assertAlmostEqual(geo_point.x, expected_coordinates[0], 4)
self.assertAlmostEqual(geo_point.y, expected_coordinates[1], 4)
def test_to_lat_lon(self):
geo_point = '{ "type": "Point", "coordinates": [601179.61612, 6399375.681364] }'
longitude, latitude = GeoPoint.to_latlon(self.env.cr, geo_point)
self.assertAlmostEqual(latitude, 49.72842315886126, 4)
self.assertAlmostEqual(longitude, 5.400488376617026, 4)
def test_deprecated_geo_search__intersect_for_zip_1169(self):
retails = self.env["retail.machine"]
zip_item = self.env["dummy.zip"].search([("name", "ilike", "1169")])
result = retails.geo_search(
geo_domain=[("the_point", "geo_intersect", zip_item.the_geom)]
)
self.assertEqual(len(result), 2)
def test_deprecated_geo_search__intersect_for_zip_1169_with_dict(self):
retails = self.env["retail.machine"]
zip_item = self.env["dummy.zip"].search([("name", "ilike", "1169")])
result = retails.geo_search(
geo_domain=[
(
"the_point",
"geo_intersect",
{"dummy.zip.the_geom": [("id", "=", zip_item.id)]},
)
]
)
self.assertEqual(len(result), 2)

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<menuitem
name="GeoEngine Backend"
id="geoengine_base_menu"
web_icon="base_geoengine,images/map.png"
groups="group_geoengine_admin"
/>
</odoo>

View file

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="geo_raster_view_form">
<field name="name">geoengine.raster.layer.form</field>
<field name="model">geoengine.raster.layer</field>
<field name="arch" type="xml">
<form string="Raster Layer View">
<sheet>
<group string="General" col="4">
<field name="name" />
<field name="view_id" />
<field name="raster_type" />
<field name="sequence" />
<field name="overlay" />
<field name="use_to_edit" />
<field
name="url"
colspan="4"
widget="url"
attrs="{'required': ['|',('is_wmts', '=', True),('is_wms', '=', True)]}"
/>
</group>
<field name="is_wmts" invisible="1" />
<group
string="WMTS options"
attrs="{'invisible': [('is_wmts', '=', False)]}"
colspan="4"
>
<field
name="matrix_set"
attrs="{'required': [('is_wmts', '=', True)]}"
/>
<field
name="format_suffix"
attrs="{'required': [('is_wmts', '=', True)]}"
/>
<field name="request_encoding" />
<field
name="projection"
attrs="{'required': [('is_wmts', '=', True)]}"
/>
<field name="units" /> <!-- Not used -->
<field
name="resolutions"
attrs="{'required': [('is_wmts', '=', True)]}"
/>
<field name="max_extent" />
<field name="dimensions" />
<field name="params" />
</group>
<field name="is_wms" invisible="1" />
<group
string="WMS options"
attrs="{'invisible': [('is_wms', '=', False)]}"
colspan="4"
>
<field
name="params_wms"
attrs="{'required': [('is_wms', '=', True)]}"
/>
<field
name="server_type"
attrs="{'required': [('is_wms', '=', True)]}"
/>
</group>
<field name="has_type" invisible="1" />
<group
string="Layer data"
attrs="{'invisible': [('has_type', '=', False)]}"
colspan="4"
>
<field
name="type_id"
widget="selection"
attrs="{'required': [('has_type', '=', True)]}"
/>
</group>
<group
string="Odoo layer data (Not implemented)"
attrs="{'invisible': [('raster_type', '!=', 'odoo')]}"
colspan="4"
>
<field name="field_id" />
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="geo_raster_view_tree">
<field name="name">geoengine.raster.layer.tree</field>
<field name="model">geoengine.raster.layer</field>
<field name="arch" type="xml">
<tree>
<field name="name" select="1" />
<field name="raster_type" select="1" />
<field name="sequence" />
<field name="overlay" select="1" />
</tree>
</field>
</record>
<record id="geo_engine_view_rater_action" model="ir.actions.act_window">
<field name="name">Raster Layer</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">geoengine.raster.layer</field>
<field name="view_id" ref="geo_raster_view_tree" />
</record>
<menuitem
name="Raster Layer Management"
id="geoengine_raster_layer_menu"
parent="geoengine_base_view_menu"
action="geo_engine_view_rater_action"
groups="group_geoengine_admin"
/>
</odoo>

View file

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="geo_vector_view_form">
<field name="name">geoengine.vector.layer.form</field>
<field name="model">geoengine.vector.layer</field>
<field name="arch" type="xml">
<form string="Vector">
<sheet>
<group string="General" col="4">
<field name="name" />
<field name="view_id" />
<field name="geo_field_id" />
<field
name="attribute_field_id"
attrs="{'required': [('geo_repr', 'in', ['choropleth', 'proportion', 'colored'])]}"
/>
<field name="active_on_startup" />
<field name="display_polygon_labels" />
<field name="sequence" />
<field name="readonly" />
<field name="layer_opacity" />
</group>
<group string="Related Model" col="4">
<field name="model_id" />
<field
name="model_view_id"
domain="[('mode', '=', 'geoengine')]"
/>
<field name="model_name" invisible="1" />
<field
name="model_domain"
colspan="4"
widget="domain"
options="{'model': 'model_name'}"
/>
</group>
<group string="Representation" col="4" colspan="4">
<field name="geo_repr" />
</group>
<group
string="Classification"
colspan="4"
attrs="{'invisible': [('geo_repr', '=', 'basic')]}"
>
<field
name="classification"
attrs="{'invisible': [('geo_repr', '=', 'basic')], 'required': [('geo_repr', 'in', ['choropleth', 'proportion', 'colored'])]}"
/>
</group>
<group string="Colors" col="4" colspan="4">
<field name="begin_color" widget="color" />
<group
colspan="4"
attrs="{'invisible': [('geo_repr', 'in', ['basic', 'proportion'])]}"
>
<field
name="end_color"
attrs="{'invisible': [('classification', 'in', ['unique', False])], 'required': [('classification', 'in', ['interval', 'quantil'])]}"
widget="color"
/>
<field
name="nb_class"
attrs="{'invisible': [('classification', 'in', ['unique', False])], 'required': [('classification', 'in', ['interval', 'quantil'])]}"
/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="geo_vector_geoengine_view_form">
<field name="name">geoengine.vector.layer.geoengine.form</field>
<field name="model">geoengine.vector.layer</field>
<field name="arch" type="xml">
<form>
<sheet>
<group string="General" col="4">
<field name="name" />
<field name="geo_field_id" />
<field
name="attribute_field_id"
attrs="{'required': [('geo_repr', 'in', ['choropleth', 'proportion', 'colored'])]}"
/>
<field name="display_polygon_labels" />
<field name="sequence" readonly="1" />
<field name="readonly" />
<field name="layer_opacity" />
</group>
<!-- <group string="Related Model" col="4">
<field name="model_id" />
<field
name="model_view_id"
domain="[('mode', '=', 'geoengine')]"
/>
<field name="model_name" invisible="1" />
<field
name="model_domain"
colspan="4"
widget="domain"
options="{'model': 'model_name'}"
/>
</group> -->
<group string="Representation" col="4" colspan="4">
<field name="geo_repr" />
</group>
<group
string="Classification"
colspan="4"
attrs="{'invisible': [('geo_repr', '=', 'basic')]}"
>
<field
name="classification"
attrs="{'invisible': [('geo_repr', '=', 'basic')], 'required': [('geo_repr', 'in', ['choropleth', 'proportion', 'colored'])]}"
/>
</group>
<group string="Colors" col="4" colspan="4">
<field name="begin_color" widget="color" />
<group
colspan="4"
attrs="{'invisible': [('geo_repr', 'in', ['basic', 'proportion'])]}"
>
<field
name="end_color"
attrs="{'invisible': [('classification', 'in', ['unique', False])], 'required': [('classification', 'in', ['interval', 'quantil'])]}"
widget="color"
/>
<field
name="nb_class"
attrs="{'invisible': [('classification', 'in', ['unique', False])], 'required': [('classification', 'in', ['interval', 'quantil'])]}"
/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="geo_vector_geoengine_view_action" model="ir.actions.act_window">
<field name="name">Vector Layer</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">geoengine.vector.layer</field>
<field name="target">new</field>
<field name="view_id" ref="geo_vector_geoengine_view_form" />
</record>
<record model="ir.ui.view" id="geo_vector_view_tree">
<field name="name">geoengine.vector.layer.tree</field>
<field name="model">geoengine.vector.layer</field>
<field name="arch" type="xml">
<tree>
<field name="name" select="1" />
<field name="geo_repr" select="1" />
<field name="classification" select="1" />
<field name="geo_field_id" select="1" />
<field name="attribute_field_id" select="1" />
<field name="active_on_startup" />
<field name="sequence" />
</tree>
</field>
</record>
<record id="geo_engine_view_raster_action" model="ir.actions.act_window">
<field name="name">Vector Layer</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">geoengine.vector.layer</field>
<field name="view_id" ref="geo_vector_view_tree" />
</record>
<menuitem
name="Vector Layer Management"
id="geoengine_vector_layer_menu"
parent="geoengine_base_view_menu"
action="geo_engine_view_raster_action"
groups="group_geoengine_admin"
/>
</odoo>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="view_model_fields_form_geo">
<!-- must be unique in this module. -->
<field name="name">ir.model.fields.form.geo</field>
<field name="model">ir.model.fields</field>
<field name="inherit_id" ref="base.view_model_fields_form" />
<field name="arch" type="xml">
<sheet position="inside">
<group>
<!-- nasty attrs but ilike operator does not work as expected -->
<field
name="srid"
attrs="{'readonly': [('ttype', 'not in', ['geo_polygon', 'geo_multi_polygon', 'geo_point', 'geo_multi_point', 'geo_line', 'geo_multi_line'])]}"
/>
<field
name="geo_type"
attrs="{'readonly': [('ttype', 'not in', ['geo_polygon', 'geo_multi_polygon', 'geo_point', 'geo_multi_point', 'geo_line', 'geo_multi_line'])]}"
/>
<field
name="dim"
attrs="{'readonly': [('ttype', 'not in', ['geo_polygon', 'geo_multi_polygon', 'geo_point', 'geo_multi_point', 'geo_line', 'geo_multi_line'])]}"
/>
<field
name="gist_index"
attrs="{'readonly': [('ttype', 'not in', ['geo_polygon', 'geo_multi_polygon', 'geo_point', 'geo_multi_point', 'geo_line', 'geo_multi_line'])]}"
/>
</group>
</sheet>
</field>
</record>
</odoo>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="geoengine_view_form">
<field name="name">geoengine.view.form</field>
<field name="model">ir.ui.view</field>
<field name="inherit_id" ref="base.view_view_form" />
<field name="arch" type="xml">
<notebook position="inside">
<page
string="GeoEngine Data"
col="4"
attrs="{'invisible': [('type', '!=', 'geoengine')]}"
>
<group>
<field name="projection" />
<field name="restricted_extent" />
<field name="default_extent" />
<field name="default_zoom" />
</group>
<separator string="Vector (Active layers)" colspan="4" />
<field name="vector_layer_ids" colspan="4" nolabel="1" />
<separator string="Raster (Background layers)" colspan="4" />
<field name="raster_layer_ids" colspan="4" nolabel="1" />
</page>
</notebook>
</field>
</record>
<menuitem
name="Configuration"
id="geoengine_base_view_menu"
parent="geoengine_base_menu"
groups="group_geoengine_admin"
/>
<record id="geo_engine_view_action" model="ir.actions.act_window">
<field name="name">Views</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.ui.view</field>
<field name="view_id" ref="base.view_view_tree" />
<field name="search_view_id" ref="base.action_view_search" />
<field name="domain">[('type', '=', 'geoengine')]</field>
</record>
<menuitem
name="View Management"
id="geoengine_view_menu"
parent="geoengine_base_view_menu"
action="geo_engine_view_action"
groups="group_geoengine_admin"
/>
</odoo>

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Base_geoengine Module - base_geoengine
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for base_geoengine. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,6 @@
# Dependencies
This addon depends on:
- base
- [web](../../odoo-bringout-oca-ocb-web)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon base_geoengine or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-geospatial-base_geoengine"
# or
uv pip install odoo-bringout-oca-geospatial-base_geoengine"
```

View file

@ -0,0 +1,17 @@
# Models
Detected core models and extensions in base_geoengine.
```mermaid
classDiagram
class geoengine_raster_layer
class geoengine_raster_layer_type
class geoengine_vector_layer
class base
class ir_model_fields
class ir_ui_view
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: base_geoengine. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon base_geoengine
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,42 @@
# Security
Access control and security definitions in base_geoengine.
## Access Control Lists (ACLs)
Model access permissions defined in:
- **[ir.model.access.csv](../base_geoengine/security/ir.model.access.csv)**
- 6 model access rules
## Record Rules
Row-level security rules defined in:
## Security Groups & Configuration
Security groups and permissions defined in:
- **[data.xml](../base_geoengine/security/data.xml)**
- 4 security groups defined
```mermaid
graph TB
subgraph "Security Layers"
A[Users] --> B[Groups]
B --> C[Access Control Lists]
C --> D[Models]
B --> E[Record Rules]
E --> F[Individual Records]
end
```
Security files overview:
- **[data.xml](../base_geoengine/security/data.xml)**
- Security groups, categories, and XML-based rules
- **[ir.model.access.csv](../base_geoengine/security/ir.model.access.csv)**
- Model access permissions (CRUD rights)
Notes
- Access Control Lists define which groups can access which models
- Record Rules provide row-level security (filter records by user/group)
- Security groups organize users and define permission sets
- All security is enforced at the ORM level by Odoo

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon base_geoengine
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -0,0 +1,43 @@
[project]
name = "odoo-bringout-oca-geospatial-base_geoengine"
version = "16.0.0"
description = "Geospatial support for Odoo - Odoo addon"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ocb-base>=16.0.0",
"odoo-bringout-oca-ocb-web>=16.0.0",
"requests>=2.25.1"
]
readme = "README.md"
requires-python = ">= 3.11"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]
[project.urls]
homepage = "https://github.com/bringout/0"
repository = "https://github.com/bringout/0"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["base_geoengine"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]