From 5ae427e8b3a02286c179c0c38bb10822b9d7f8a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Mon, 20 Nov 2023 14:50:11 +0100 Subject: [PATCH 1/3] Backport pre-commit from branch 14.0 --- .pre-commit-config.yaml | 43 +- base_geoengine/doc/source/conf.py | 1 - base_geoengine/geo_model.py | 2 +- base_geoengine/geo_operators.py | 2 +- base_geoengine/geo_view/geo_raster_layer.py | 1 - .../src/js/widgets/geoengine_widgets.js | 922 ++++++++++-------- .../tests/test_geolocalize_openstreetmap.py | 2 +- base_google_map/models/res_config_settings.py | 4 +- .../geo_view/geo_raster_layer.py | 8 +- .../tests/test_base_geoengine.py | 2 +- web_view_leaflet_map/models/ir_http.py | 2 +- .../models/res_config_settings.py | 4 +- 12 files changed, 544 insertions(+), 449 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e238afee95..f2b7b81270 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,4 +56,45 @@ repos: args: - --rcfile=.pylintrc-mandatory additional_dependencies: *pylint_deps - + - repo: https://github.com/myint/autoflake + rev: v1.4 + hooks: + - id: autoflake + args: + - --expand-star-imports + - --ignore-init-module-imports + - --in-place + - --remove-all-unused-imports + - --remove-duplicate-keys + - --remove-unused-variables + # - repo: https://github.com/psf/black + # rev: 22.3.0 + # hooks: + # - id: black + # - repo: https://github.com/pre-commit/mirrors-prettier + # rev: v2.1.2 + # hooks: + # - id: prettier + # name: prettier (with plugin-xml) + # additional_dependencies: + # - "prettier@2.1.2" + # - "@prettier/plugin-xml@0.12.0" + # args: + # - --plugin=@prettier/plugin-xml + # files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$ + - repo: https://github.com/asottile/pyupgrade + rev: v2.7.2 + hooks: + - id: pyupgrade + args: + - --keep-percent-format + - --py36-plus + # - repo: https://github.com/PyCQA/isort + # rev: 5.12.0 + # language_version: python3.8 + # hooks: + # - id: isort + # name: isort except __init__.py + # args: + # - --settings=. + # exclude: /__init__\.py$ diff --git a/base_geoengine/doc/source/conf.py b/base_geoengine/doc/source/conf.py index 28c30e2247..225c040546 100644 --- a/base_geoengine/doc/source/conf.py +++ b/base_geoengine/doc/source/conf.py @@ -12,7 +12,6 @@ # serve to show the default. import sys -import imp import os import sphinx_bootstrap_theme diff --git a/base_geoengine/geo_model.py b/base_geoengine/geo_model.py index 10cf7e73af..5590ba50c5 100644 --- a/base_geoengine/geo_model.py +++ b/base_geoengine/geo_model.py @@ -131,7 +131,7 @@ def get_edit_info_for_geo_column(self, column): 'default_extent': view.default_extent or DEFAULT_EXTENT, 'default_zoom': view.default_zoom, } - logger.debug("Parameters for geo field {}:\n{}".format(column, res)) + logger.debug(f"Parameters for geo field {column}:\n{res}") return res @api.model diff --git a/base_geoengine/geo_operators.py b/base_geoengine/geo_operators.py index 96463b7f8e..6e78ec7600 100644 --- a/base_geoengine/geo_operators.py +++ b/base_geoengine/geo_operators.py @@ -126,7 +126,7 @@ def geo_search(model, domain=None, geo_domain=None, offset=0, return [] -class GeoOperator(object): +class GeoOperator: def __init__(self, geo_field): self.geo_field = geo_field diff --git a/base_geoengine/geo_view/geo_raster_layer.py b/base_geoengine/geo_view/geo_raster_layer.py index 4180dc8aef..bc602f6cef 100644 --- a/base_geoengine/geo_view/geo_raster_layer.py +++ b/base_geoengine/geo_view/geo_raster_layer.py @@ -86,4 +86,3 @@ def _compute_is_wmts(self): @api.onchange('raster_type') def onchange_set_wmts_options(self): """ Abstract method for WMTS modules to set default options """ - pass diff --git a/base_geoengine/static/src/js/widgets/geoengine_widgets.js b/base_geoengine/static/src/js/widgets/geoengine_widgets.js index 78d12b1ff2..03e68a1c3b 100644 --- a/base_geoengine/static/src/js/widgets/geoengine_widgets.js +++ b/base_geoengine/static/src/js/widgets/geoengine_widgets.js @@ -7,450 +7,485 @@ * License in __manifest__.py at root level of the module * --------------------------------------------------------- */ -odoo.define('base_geoengine.geoengine_widgets', function (require) { +odoo.define("base_geoengine.geoengine_widgets", function (require) { "use strict"; - var core = require('web.core'); - var AbstractField = require('web.AbstractField'); - var geoengine_common = require('base_geoengine.geoengine_common'); - var BackgroundLayers = require('base_geoengine.BackgroundLayers'); - var registry = require('web.field_registry'); - - var FieldGeoEngineEditMap = AbstractField.extend(geoengine_common.GeoengineMixin, { // eslint-disable-line max-len - template: 'FieldGeoEngineEditMap', - - geoType: null, - map: null, - defaultExtent: null, - format: null, - vectorLayer: null, - rasterLayers: null, - source: null, - features: null, - drawControl: null, - modifyControl: null, - tabListenerInstalled: false, - bgLayers: new BackgroundLayers(), - - // -------------------------------------------------------------------- - // Public - // -------------------------------------------------------------------- - - /** - * @override - */ - start: function () { - var def = this._super(); - - // Add a listener on parent tab if it exists in order to refresh - // geoengine view we need to trigger it on DOM update for changes - // from view to edit mode. - core.bus.on('DOM_updated', this, function () { - this._addTabListener(); - }.bind(this)); - - return def; - }, - - init: function () { - this._super.apply(this, arguments); - this._setAdditionalLayers(); - }, - - // FIXME still used? - validate: function () { - this.invalid = false; - }, - - // -------------------------------------------------------------------- - // Private - // -------------------------------------------------------------------- - - /** - * Define additional readonly layers based on other fields of the model - * - * To set on the XML view through - * options={'add_layer_fields': ['field1', 'field2']} - */ - _setAdditionalLayers: function () { - this.vectorFields = this.nodeOptions.add_layer_fields || []; - }, - - _createVectorLayer: function (field, style) { - this.features[field] = new ol.Collection(); - this.source[field] = new ol.source.Vector({features: this.features[field]}); - return new ol.layer.Vector({ - source: this.source[field], - style: style, - }); - }, + var core = require("web.core"); + var AbstractField = require("web.AbstractField"); + var geoengine_common = require("base_geoengine.geoengine_common"); + var BackgroundLayers = require("base_geoengine.BackgroundLayers"); + var registry = require("web.field_registry"); + + var FieldGeoEngineEditMap = AbstractField.extend( + geoengine_common.GeoengineMixin, + { + // eslint-disable-line max-len + template: "FieldGeoEngineEditMap", + + geoType: null, + map: null, + defaultExtent: null, + format: null, + vectorLayer: null, + rasterLayers: null, + source: null, + features: null, + drawControl: null, + modifyControl: null, + tabListenerInstalled: false, + bgLayers: new BackgroundLayers(), + + // -------------------------------------------------------------------- + // Public + // -------------------------------------------------------------------- + + /** + * @override + */ + start: function () { + var def = this._super(); + + // Add a listener on parent tab if it exists in order to refresh + // geoengine view we need to trigger it on DOM update for changes + // from view to edit mode. + core.bus.on( + "DOM_updated", + this, + function () { + this._addTabListener(); + }.bind(this) + ); + + return def; + }, + + init: function () { + this._super.apply(this, arguments); + this._setAdditionalLayers(); + }, + + // FIXME still used? + validate: function () { + this.invalid = false; + }, + + // -------------------------------------------------------------------- + // Private + // -------------------------------------------------------------------- + + /** + * Define additional readonly layers based on other fields of the model + * + * To set on the XML view through + * options={'add_layer_fields': ['field1', 'field2']} + */ + _setAdditionalLayers: function () { + this.vectorFields = this.nodeOptions.add_layer_fields || []; + }, + + _createVectorLayer: function (field, style) { + this.features[field] = new ol.Collection(); + this.source[field] = new ol.source.Vector({ + features: this.features[field], + }); + return new ol.layer.Vector({ + source: this.source[field], + style: style, + }); + }, - _createFeatureStyles: function () { - var styles = { - edit: new ol.style.Style({ - fill: new ol.style.Fill({ - opacity: 0.7, - color: '#c0392b', - }), - stroke: new ol.style.Stroke({ - width: 5, - opacity: 1, - color: '#c0392b', - }), - image: new ol.style.Circle({ - radius: 7, + _createFeatureStyles: function () { + var styles = { + edit: new ol.style.Style({ fill: new ol.style.Fill({ - color: '#c0392b', + opacity: 0.7, + color: "#c0392b", + }), + stroke: new ol.style.Stroke({ + width: 5, + opacity: 1, + color: "#c0392b", + }), + image: new ol.style.Circle({ + radius: 7, + fill: new ol.style.Fill({ + color: "#c0392b", + }), }), }), - }), - readonly: new ol.style.Style({ - fill: new ol.style.Fill({ - color: '#ee9900', - opacity: 0.7, - }), - stroke: new ol.style.Stroke({ - color: '#ee9900', - width: 5, - opacity: 1, - }), - image: new ol.style.Circle({ - radius: 7, + readonly: new ol.style.Style({ fill: new ol.style.Fill({ - color: '#ffcb8e', + color: "#ee9900", + opacity: 0.7, + }), + stroke: new ol.style.Stroke({ + color: "#ee9900", + width: 5, + opacity: 1, + }), + image: new ol.style.Circle({ + radius: 7, + fill: new ol.style.Fill({ + color: "#ffcb8e", + }), }), }), - }) - }; - return styles - }, + }; + return styles; + }, - - /** - * Creates vector layers from config - * - * Those layers comes empty and are filled later with - * features. - */ - _createVectorLayers: function () { - this.vectorLayers = []; - this.source = {}; - this.features = {}; - var styles = this._createFeatureStyles() - this.vectorLayers.push(this._createVectorLayer(this.name, styles.edit)); - - _.each(this.vectorFields, function (field) { - this.vectorLayers.push(this._createVectorLayer(field, styles.readonly)); - }.bind(this)); - }, - - /** - * Add vector layers to the map - * - * Those layers are added in 2 groups - * The main overlay for edition - * The second overlay for readonly layers. - */ - _addVectorLayers: function () { - // first create the readonly layers to have a - // lower zIndex - var readonlyLayers = this.vectorLayers.slice(1); - if (readonlyLayers) { - this.readonlyOverlaysGroup = new ol.layer.Group({ - title: 'Readonly Overlays', - layers: readonlyLayers, + /** + * Creates vector layers from config + * + * Those layers comes empty and are filled later with + * features. + */ + _createVectorLayers: function () { + this.vectorLayers = []; + this.source = {}; + this.features = {}; + var styles = this._createFeatureStyles(); + this.vectorLayers.push( + this._createVectorLayer(this.name, styles.edit) + ); + + _.each( + this.vectorFields, + function (field) { + this.vectorLayers.push( + this._createVectorLayer(field, styles.readonly) + ); + }.bind(this) + ); + }, + + /** + * Add vector layers to the map + * + * Those layers are added in 2 groups + * The main overlay for edition + * The second overlay for readonly layers. + */ + _addVectorLayers: function () { + // first create the readonly layers to have a + // lower zIndex + var readonlyLayers = this.vectorLayers.slice(1); + if (readonlyLayers) { + this.readonlyOverlaysGroup = new ol.layer.Group({ + title: "Readonly Overlays", + layers: readonlyLayers, + }); + this.map.addLayer(this.readonlyOverlaysGroup); + } + this.mainOverlaysGroup = new ol.layer.Group({ + title: "Main Overlays", + layers: this.vectorLayers.slice(0, 1), }); - this.map.addLayer(this.readonlyOverlaysGroup); - } - this.mainOverlaysGroup = new ol.layer.Group({ - title: 'Main Overlays', - layers: this.vectorLayers.slice(0, 1), - }); - this.map.addLayer(this.mainOverlaysGroup); - - }, - - _createLayers: function (field_infos) { - this._createVectorLayers(); - this.rasterLayers = this.bgLayers.create([ - field_infos.edit_raster, - ]); - if (this.rasterLayers.length) { - this.rasterLayers[0].isBaseLayer = true; - } - }, - - _addTabListener: function () { - if (this.tabListenerInstalled) { - return; - } - var tab = this.$el.closest('div.tab-pane'); - if (!tab.length) { - return; - } - var tab_link = $('a[href="#' + tab[0].id + '"]'); - if (!tab_link.length) { - return; - } - tab_link.on('shown.bs.tab', function (e) { - this._render(); - }.bind(this)); - this.tabListenerInstalled = true; - }, - - _parseValue: function (value) { - return value; - }, - - _updateMapEmpty: function () { - var map_view = this.map.getView(); - // Default extent - 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}); - } - }, + this.map.addLayer(this.mainOverlaysGroup); + }, - _updateMapZoom: function (zoom) { - - var map_zoom = typeof zoom === 'undefined' ? true : zoom; + _createLayers: function (field_infos) { + this._createVectorLayers(); + this.rasterLayers = this.bgLayers.create([ + field_infos.edit_raster, + ]); + if (this.rasterLayers.length) { + this.rasterLayers[0].isBaseLayer = true; + } + }, - if (this.source[this.name]) { - var extent = this.source[this.name].getExtent(); - var infinite_extent = [ - Infinity, Infinity, -Infinity, -Infinity, - ]; - if (map_zoom && extent !== infinite_extent) { - var map_view = this.map.getView(); - if (map_view) { - map_view.fit(extent, {maxZoom: 15}); + _addTabListener: function () { + if (this.tabListenerInstalled) { + return; + } + var tab = this.$el.closest("div.tab-pane"); + if (!tab.length) { + return; + } + var tab_link = $('a[href="#' + tab[0].id + '"]'); + if (!tab_link.length) { + return; + } + tab_link.on( + "shown.bs.tab", + function (e) { + this._render(); + }.bind(this) + ); + this.tabListenerInstalled = true; + }, + + _parseValue: function (value) { + return value; + }, + + _updateMapEmpty: function () { + var map_view = this.map.getView(); + // Default extent + 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 }); + } + }, + + _updateMapZoom: function (zoom) { + var map_zoom = typeof zoom === "undefined" ? true : zoom; + + if (this.source[this.name]) { + var extent = this.source[this.name].getExtent(); + var infinite_extent = [ + Infinity, + Infinity, + -Infinity, + -Infinity, + ]; + if (map_zoom && extent !== infinite_extent) { + var map_view = this.map.getView(); + if (map_view) { + map_view.fit(extent, { maxZoom: 15 }); + } } } - } - }, - - _setValue: function (value, zoom) { - - this._super(value); - this.value = value; + }, - if (this.map) { - - var ft = new ol.Feature({ - geometry: new ol.format.GeoJSON().readGeometry(value), - labelPoint: new ol.format.GeoJSON().readGeometry(value), - }); - this.source[this.name].clear(); - this.source[this.name].addFeature(ft); - if (value) { - this._updateMapZoom(zoom); - } else { - this._updateMapEmpty(); - } + _setValue: function (value, zoom) { + this._super(value); + this.value = value; - _.each(this.vectorFields, function (fieldName) { - var value = this.record.data[fieldName]; + if (this.map) { var ft = new ol.Feature({ geometry: new ol.format.GeoJSON().readGeometry(value), - labelPoint: new ol.format.GeoJSON().readGeometry(value), + labelPoint: new ol.format.GeoJSON().readGeometry(value), }); - this.source[fieldName].clear(); - this.source[fieldName].addFeature(ft); - }.bind(this)); - } - }, - - _isTabVisible: function () { - var tab = this.$el.closest('div.tab-pane'); - if (!tab.length) { - return false; - } - return tab.is(":visible"); - }, - - _onUIChange: function () { - var value = null; - if (this._geometry) { - value = this.format.writeGeometry(this._geometry); - } - this._setValue(value, false); - }, + this.source[this.name].clear(); + this.source[this.name].addFeature(ft); + if (value) { + this._updateMapZoom(zoom); + } else { + this._updateMapEmpty(); + } - _setupControls: function () { + _.each( + this.vectorFields, + function (fieldName) { + var value = this.record.data[fieldName]; + var ft = new ol.Feature({ + geometry: new ol.format.GeoJSON().readGeometry( + value + ), + labelPoint: new ol.format.GeoJSON().readGeometry( + value + ), + }); + this.source[fieldName].clear(); + this.source[fieldName].addFeature(ft); + }.bind(this) + ); + } + }, - /* Add a draw interaction depending on geoType of the field - * plus adds a modify interaction to be able to change line - * and polygons. - * As modify needs to get pointer position on map it requires - * the map to be rendered before being created - */ - var handler = null; - if (this.geoType === 'POLYGON') { - handler = "Polygon"; - } else if (this.geoType === 'MULTIPOLYGON') { - handler = "MultiPolygon"; - } else if (this.geoType === 'LINESTRING') { - handler = "LineString"; - } else if (this.geoType === 'MULTILINESTRING') { - handler = "MultiLineString"; - } else if (this.geoType === 'POINT') { - handler = "Point"; - } else if (this.geoType === 'MULTIPOINT') { - handler = "MultiPoint"; - } else { - // FIXME: unsupported geo type - } + _isTabVisible: function () { + var tab = this.$el.closest("div.tab-pane"); + if (!tab.length) { + return false; + } + return tab.is(":visible"); + }, - var drawControl = function (options) { - ol.interaction.Draw.call(this, options); - }; - ol.inherits(drawControl, ol.interaction.Draw); - drawControl.prototype.finishDrawing = function () { - this.source_.clear(); - ol.interaction.Draw.prototype.finishDrawing.call(this); - }; - - this.drawControl = new drawControl({ - source: this.source[this.name], - type: handler, - }); - this.map.addInteraction(this.drawControl); - var onchange_geom = function (e) { - // Trigger onchanges when drawing is done - if (e.type === 'drawend') { - this._geometry = e.feature.getGeometry(); + _onUIChange: function () { + var value = null; + if (this._geometry) { + value = this.format.writeGeometry(this._geometry); + } + this._setValue(value, false); + }, + + _setupControls: function () { + /* Add a draw interaction depending on geoType of the field + * plus adds a modify interaction to be able to change line + * and polygons. + * As modify needs to get pointer position on map it requires + * the map to be rendered before being created + */ + var handler = null; + if (this.geoType === "POLYGON") { + handler = "Polygon"; + } else if (this.geoType === "MULTIPOLYGON") { + handler = "MultiPolygon"; + } else if (this.geoType === "LINESTRING") { + handler = "LineString"; + } else if (this.geoType === "MULTILINESTRING") { + handler = "MultiLineString"; + } else if (this.geoType === "POINT") { + handler = "Point"; + } else if (this.geoType === "MULTIPOINT") { + handler = "MultiPoint"; } else { - // Modify end - this._geometry = e.features.item(0).getGeometry(); + // FIXME: unsupported geo type } - this._onUIChange(); - }.bind(this); - this.drawControl.on('drawend', onchange_geom); - - this.features = [] - this.features[this.name] = this.source[this.name].getFeaturesCollection(); - this.modifyControl = new ol.interaction.Modify({ - features: this.features[this.name], - // The SHIFT key must be pressed to delete vertices, so - // that new vertices can be drawn at the same position - // of existing vertices - deleteCondition: function (event) { - return ol.events.condition.shiftKeyOnly(event) && - ol.events.condition.singleClick(event); - }, - }); - this.map.addInteraction(this.modifyControl); - this.modifyControl.on('modifyend', onchange_geom); - - var self = this; - var ClearMapControl = function (opt_options) { - var options = opt_options || {}; - var button = document.createElement('button'); - button.innerHTML = ''; - button.addEventListener('click', function () { - self.source[self.name].clear(); - self._geometry = null; - self._onUIChange(); - }); - var element = document.createElement('div'); - element.className = 'ol-clear ol-unselectable ol-control'; - element.appendChild(button); - - ol.control.Control.call(this, { - element: element, - target: options.target, - }); - }; - ol.inherits(ClearMapControl, ol.control.Control); - this.clearmapControl = new ClearMapControl(); - this.map.addControl(this.clearmapControl); - }, - _renderMap: function () { - if (!this.map) { - var projection = new ol.proj.get(this.projectionCode); - var $el = this.$el[0]; - $($el).css({width: '100%', height: '100%'}); - this.map = new ol.Map({ - layers: this.rasterLayers, - target: $el, - view: new ol.View({ - center: [0, 0], - zoom: 5, - projection: projection, - }), + var drawControl = function (options) { + ol.interaction.Draw.call(this, options); + }; + ol.inherits(drawControl, ol.interaction.Draw); + drawControl.prototype.finishDrawing = function () { + this.source_.clear(); + ol.interaction.Draw.prototype.finishDrawing.call(this); + }; + + this.drawControl = new drawControl({ + source: this.source[this.name], + type: handler, }); - - this._createVectorLayers(); - this._addVectorLayers(); - this.format = new ol.format.GeoJSON({ - featureProjection: projection, - defaultDataProjection: 'EPSG:' + this.srid, + this.map.addInteraction(this.drawControl); + var onchange_geom = function (e) { + // Trigger onchanges when drawing is done + if (e.type === "drawend") { + this._geometry = e.feature.getGeometry(); + } else { + // Modify end + this._geometry = e.features.item(0).getGeometry(); + } + this._onUIChange(); + }.bind(this); + this.drawControl.on("drawend", onchange_geom); + + this.features = []; + this.features[this.name] = this.source[ + this.name + ].getFeaturesCollection(); + this.modifyControl = new ol.interaction.Modify({ + features: this.features[this.name], + // The SHIFT key must be pressed to delete vertices, so + // that new vertices can be drawn at the same position + // of existing vertices + deleteCondition: function (event) { + return ( + ol.events.condition.shiftKeyOnly(event) && + ol.events.condition.singleClick(event) + ); + }, }); + this.map.addInteraction(this.modifyControl); + this.modifyControl.on("modifyend", onchange_geom); + + var self = this; + var ClearMapControl = function (opt_options) { + var options = opt_options || {}; + var button = document.createElement("button"); + button.innerHTML = ''; + button.addEventListener("click", function () { + self.source[self.name].clear(); + self._geometry = null; + self._onUIChange(); + }); + var element = document.createElement("div"); + element.className = "ol-clear ol-unselectable ol-control"; + element.appendChild(button); - $(document).trigger('FieldGeoEngineEditMap:ready', [this.map]); - this._setValue(this.value); + ol.control.Control.call(this, { + element: element, + target: options.target, + }); + }; + ol.inherits(ClearMapControl, ol.control.Control); + this.clearmapControl = new ClearMapControl(); + this.map.addControl(this.clearmapControl); + }, + + _renderMap: function () { + if (!this.map) { + var projection = new ol.proj.get(this.projectionCode); + var $el = this.$el[0]; + $($el).css({ width: "100%", height: "100%" }); + this.map = new ol.Map({ + layers: this.rasterLayers, + target: $el, + view: new ol.View({ + center: [0, 0], + zoom: 5, + projection: projection, + }), + }); - if (this.mode !== 'readonly' && - !this.get('effective_readonly')) { - this._setupControls(); - this.drawControl.setActive(true); - this.modifyControl.setActive(true); - this.clearmapControl.element.children[0].disabled = false; - } - } - }, + this._createVectorLayers(); + this._addVectorLayers(); + this.format = new ol.format.GeoJSON({ + featureProjection: projection, + defaultDataProjection: "EPSG:" + this.srid, + }); - _render: function () { - this._rpc({ - model: this.model, - method: 'get_edit_info_for_geo_column', - args: [this.name], - }).then(function (result) { - this._createLayers(result); - this.geoType = result.geo_type; - this.projectionCode = result.projection; - this.defaultExtent = result.default_extent; - this.defaultZoom = result.default_zoom; - this.restrictedExtent = result.restricted_extent; - this.srid = result.srid; - if (this.$el.is(":visible") || this._isTabVisible()) { - this._renderMap(); + $(document).trigger("FieldGeoEngineEditMap:ready", [ + this.map, + ]); + this._setValue(this.value); + + if ( + this.mode !== "readonly" && + !this.get("effective_readonly") + ) { + this._setupControls(); + this.drawControl.setActive(true); + this.modifyControl.setActive(true); + this.clearmapControl.element.children[0].disabled = false; + } } - }.bind(this)); - }, - }); + }, + + _render: function () { + this._rpc({ + model: this.model, + method: "get_edit_info_for_geo_column", + args: [this.name], + }).then( + function (result) { + this._createLayers(result); + this.geoType = result.geo_type; + this.projectionCode = result.projection; + this.defaultExtent = result.default_extent; + this.defaultZoom = result.default_zoom; + this.restrictedExtent = result.restricted_extent; + this.srid = result.srid; + if (this.$el.is(":visible") || this._isTabVisible()) { + this._renderMap(); + } + }.bind(this) + ); + }, + } + ); // TODO migrate the following widgets var FieldGeoPointXY = AbstractField.extend({ - template: 'FieldGeoPointXY', + template: "FieldGeoPointXY", start: function () { this._super.apply(this, arguments); - this.$input = this.$el.find('input'); + this.$input = this.$el.find("input"); this.$input.change(this._onUIChange); this.setupFocus(this.$input); }, get_coords: function () { - /* Get coordinates and check it has the right format * * @return [x, y] */ var x = openerp.web.parse_value(this.$input.eq(0).val(), { - type: 'float', + type: "float", }); var y = openerp.web.parse_value(this.$input.eq(1).val(), { - type: 'float', + type: "float", }); return [x, y]; }, make_GeoJSON: function (coords) { - return {"type": "Point", "coordinates": coords}; + return { type: "Point", coordinates: coords }; }, _setValue: function (value) { @@ -461,7 +496,7 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { this.$input.eq(0).val(geo_obj.coordinates[0]); this.$input.eq(1).val(geo_obj.coordinates[1]); } else { - this.$input.val(''); + this.$input.val(""); } }, @@ -482,10 +517,10 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { var coords = this.get_coords(); // Make sure the two coordinates are set or None - this.invalid = this.required && - (coords[0] === 0 || coords[1] === 0 ) || - coords[0] === false && coords[1] !== false || - coords[0] !== false && coords[1] === false; + this.invalid = + (this.required && (coords[0] === 0 || coords[1] === 0)) || + (coords[0] === false && coords[1] !== false) || + (coords[0] !== false && coords[1] === false); } catch (e) { this.invalid = true; } @@ -497,22 +532,26 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { }, set_readonly: function () { - this.$input.prop('readonly', this.readonly); + this.$input.prop("readonly", this.readonly); }, }); var FieldGeoPointXYReadonly = FieldGeoPointXY.extend({ - template: 'FieldGeoPointXY.readonly', + template: "FieldGeoPointXY.readonly", _setValue: function (value) { this._super.apply(this, arguments); - var show_value = ''; + var show_value = ""; if (value) { var geo_obj = JSON.parse(value); - show_value = "(" + geo_obj.coordinates[0] + ", " + - geo_obj.coordinates[1] + ")"; + show_value = + "(" + + geo_obj.coordinates[0] + + ", " + + geo_obj.coordinates[1] + + ")"; } - this.$el.find('div').text(show_value); + this.$el.find("div").text(show_value); return show_value; }, @@ -522,35 +561,37 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { }); var FieldGeoRect = AbstractField.extend({ - template: 'FieldGeoRect', + template: "FieldGeoRect", start: function () { this._super.apply(this, arguments); - this.$input = this.$el.find('input'); + this.$input = this.$el.find("input"); this.$input.change(this._onUIChange); this.setupFocus(this.$input); }, get_coords: function () { - /* Get coordinates in the input fields * * @return [[x1, y1],[x2, y2]] */ var x1 = openerp.web.parse_value(this.$input.eq(0).val(), { - type: 'float', + type: "float", }); var y1 = openerp.web.parse_value(this.$input.eq(1).val(), { - type: 'float', + type: "float", }); var x2 = openerp.web.parse_value(this.$input.eq(2).val(), { - type: 'float', + type: "float", }); var y2 = openerp.web.parse_value(this.$input.eq(3).val(), { - type: 'float', + type: "float", }); - return [[x1, y1], [x2, y2]]; + return [ + [x1, y1], + [x2, y2], + ]; }, make_GeoJSON: function (coords) { @@ -559,8 +600,8 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { var p3 = coords[1]; var p4 = [coords[1][0], coords[0][1]]; // Create a loop in clockwise - var points = [[ p1, p2, p3, p4, p1 ]]; - return {"type": "Polygon", "coordinates": points}; + var points = [[p1, p2, p3, p4, p1]]; + return { type: "Polygon", coordinates: points }; }, _setValue: function (value) { @@ -573,12 +614,11 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { this.$input.eq(2).val(geo_obj.coordinates[0][2][0]); this.$input.eq(3).val(geo_obj.coordinates[0][2][1]); } else { - this.$input.val(''); + this.$input.val(""); } }, correct_bounds: function (coords) { - /* Reverse bounds if the upper right * point is smaller than bottom left * @@ -595,7 +635,10 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { var miny = Math.min(y1, y2); var maxy = Math.max(y1, y2); - return [[minx, miny], [maxx, maxy]]; + return [ + [minx, miny], + [maxx, maxy], + ]; }, _onUIChange: function () { @@ -610,13 +653,21 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { }, all_are_set: function (coords) { - return coords[0][0] !== false && coords[0][1] !== false && - coords[1][0] !== false && coords[1][1] !== false; + return ( + coords[0][0] !== false && + coords[0][1] !== false && + coords[1][0] !== false && + coords[1][1] !== false + ); }, none_are_set: function (coords) { - return coords[0][0] === false && coords[0][1] === false && - coords[1][0] === false && coords[1][1] === false; + return ( + coords[0][0] === false && + coords[0][1] === false && + coords[1][0] === false && + coords[1][1] === false + ); }, validate: function () { @@ -627,8 +678,8 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { // Make sure all the coordinates are set // if not None or if required - this.invalid = (this.required || - !this.none_are_set(coords)) && + this.invalid = + (this.required || !this.none_are_set(coords)) && !this.all_are_set(coords); } catch (e) { this.invalid = true; @@ -641,24 +692,31 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { }, set_readonly: function () { - this.$input.prop('readonly', this.readonly); + this.$input.prop("readonly", this.readonly); }, }); var FieldGeoRectReadonly = FieldGeoRect.extend({ - template: 'FieldGeoRect.readonly', + template: "FieldGeoRect.readonly", _setValue: function (value) { this._super.apply(this, arguments); - var show_value = ''; + var show_value = ""; if (value) { var geo_obj = JSON.parse(value); - show_value = "(" + geo_obj.coordinates[0][0][0] + ", " + - geo_obj.coordinates[0][0][1] + "), " + - "(" + geo_obj.coordinates[0][2][0] + ", " + - geo_obj.coordinates[0][2][1] + ")"; + show_value = + "(" + + geo_obj.coordinates[0][0][0] + + ", " + + geo_obj.coordinates[0][0][1] + + "), " + + "(" + + geo_obj.coordinates[0][2][0] + + ", " + + geo_obj.coordinates[0][2][1] + + ")"; } - this.$el.find('div').text(show_value); + this.$el.find("div").text(show_value); return show_value; }, @@ -667,8 +725,7 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { }, }); - registry - .add('geo_edit_map', FieldGeoEngineEditMap); + registry.add("geo_edit_map", FieldGeoEngineEditMap); // .add('geo_point_xy', FieldGeoPointXY) // .add('geo_point_xy', FieldGeoPointXYReadonly) // .add('geo_rect', FieldGeoRect) @@ -676,10 +733,9 @@ odoo.define('base_geoengine.geoengine_widgets', function (require) { return { FieldGeoEngineEditMap: FieldGeoEngineEditMap, - // FieldGeoPointXY: FieldGeoPointXY, - // FieldGeoPointXYReadonly: FieldGeoPointXYReadonly, - // FieldGeoRect: FieldGeoRect, - // FieldGeoRectReadonly: FieldGeoRectReadonly, + // FieldGeoPointXY: FieldGeoPointXY, + // FieldGeoPointXYReadonly: FieldGeoPointXYReadonly, + // FieldGeoRect: FieldGeoRect, + // FieldGeoRectReadonly: FieldGeoRectReadonly, }; - }); diff --git a/base_geolocalize_openstreetmap/tests/test_geolocalize_openstreetmap.py b/base_geolocalize_openstreetmap/tests/test_geolocalize_openstreetmap.py index 029a83bef2..b7484c855c 100644 --- a/base_geolocalize_openstreetmap/tests/test_geolocalize_openstreetmap.py +++ b/base_geolocalize_openstreetmap/tests/test_geolocalize_openstreetmap.py @@ -6,7 +6,7 @@ class TestGeolocalizeOpenstreetmap(common.TransactionCase): def setUp(self): - super(TestGeolocalizeOpenstreetmap, self).setUp() + super().setUp() self.expected_latitude = 50.4311411 self.expected_longitude = 4.6132813 diff --git a/base_google_map/models/res_config_settings.py b/base_google_map/models/res_config_settings.py index 4e15a00514..d030faed3b 100644 --- a/base_google_map/models/res_config_settings.py +++ b/base_google_map/models/res_config_settings.py @@ -127,7 +127,7 @@ def onchange_lang_localization(self): @api.multi def set_values(self): - super(ResConfigSettings, self).set_values() + super().set_values() ICPSudo = self.env['ir.config_parameter'].sudo() lang_localization = self._set_google_maps_lang_localization() region_localization = self._set_google_maps_region_localization() @@ -148,7 +148,7 @@ def set_values(self): @api.model def get_values(self): - res = super(ResConfigSettings, self).get_values() + res = super().get_values() ICPSudo = self.env['ir.config_parameter'].sudo() lang_localization = self._get_google_maps_lang_localization() diff --git a/geoengine_swisstopo/geo_view/geo_raster_layer.py b/geoengine_swisstopo/geo_view/geo_raster_layer.py index 94d6da30b6..3e873964b5 100644 --- a/geoengine_swisstopo/geo_view/geo_raster_layer.py +++ b/geoengine_swisstopo/geo_view/geo_raster_layer.py @@ -5,8 +5,8 @@ class GeoRasterLayer(models.Model): - _inherit = 'geoengine.raster.layer' + _inherit = "geoengine.raster.layer" - raster_type = fields.Selection(selection_add=[('swisstopo', 'Swisstopo')]) - layername = fields.Char('Layer Machine Name') - time = fields.Char('Time Dimension') + raster_type = fields.Selection(selection_add=[("swisstopo", "Swisstopo")]) + layername = fields.Char("Layer Machine Name") + time = fields.Char("Time Dimension") diff --git a/test_base_geoengine/tests/test_base_geoengine.py b/test_base_geoengine/tests/test_base_geoengine.py index f4ed8aa54a..1a257dbae3 100644 --- a/test_base_geoengine/tests/test_base_geoengine.py +++ b/test_base_geoengine/tests/test_base_geoengine.py @@ -5,7 +5,7 @@ from io import StringIO import logging import geojson -import mock +from unittest import mock import simplejson from shapely.wkt import loads as wktloads diff --git a/web_view_leaflet_map/models/ir_http.py b/web_view_leaflet_map/models/ir_http.py index 09766554a0..9c86f33f7c 100644 --- a/web_view_leaflet_map/models/ir_http.py +++ b/web_view_leaflet_map/models/ir_http.py @@ -9,7 +9,7 @@ class Http(models.AbstractModel): _inherit = 'ir.http' def session_info(self): - result = super(Http, self).session_info() + result = super().session_info() config = self.env['ir.config_parameter'].sudo() result.update({ "leaflet.tile_url": config.get_param('leaflet.tile_url', default=''), diff --git a/web_widget_google_map_drawing/models/res_config_settings.py b/web_widget_google_map_drawing/models/res_config_settings.py index e5415ef954..b6b99b5b79 100644 --- a/web_widget_google_map_drawing/models/res_config_settings.py +++ b/web_widget_google_map_drawing/models/res_config_settings.py @@ -10,14 +10,14 @@ class ResConfigSettings(models.TransientModel): @api.multi def set_values(self): - super(ResConfigSettings, self).set_values() + super().set_values() ICPSudo = self.env['ir.config_parameter'].sudo() libraries = self._set_google_maps_drawing() ICPSudo.set_param('google.maps_libraries', libraries) @api.model def get_values(self): - res = super(ResConfigSettings, self).get_values() + res = super().get_values() lib_drawing = self._get_google_maps_drawing() res['google_maps_drawing'] = lib_drawing return res From 418e0d6e2a9227d0e32d2accc45736a3f75bd829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Wed, 15 Nov 2023 14:51:36 +0100 Subject: [PATCH 2/3] Fix reprojection --- .../src/js/widgets/geoengine_widgets.js | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/base_geoengine/static/src/js/widgets/geoengine_widgets.js b/base_geoengine/static/src/js/widgets/geoengine_widgets.js index 03e68a1c3b..6a4bf94018 100644 --- a/base_geoengine/static/src/js/widgets/geoengine_widgets.js +++ b/base_geoengine/static/src/js/widgets/geoengine_widgets.js @@ -254,11 +254,12 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { _setValue: function (value, zoom) { this._super(value); this.value = value; + let geometry = this.format.readGeometry(value); if (this.map) { - var ft = new ol.Feature({ - geometry: new ol.format.GeoJSON().readGeometry(value), - labelPoint: new ol.format.GeoJSON().readGeometry(value), + let ft = new ol.Feature({ + geometry: geometry, + labelPoint: geometry, }); this.source[this.name].clear(); this.source[this.name].addFeature(ft); @@ -272,13 +273,10 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { this.vectorFields, function (fieldName) { var value = this.record.data[fieldName]; + var geometry = this.format.readGeometry(value); var ft = new ol.Feature({ - geometry: new ol.format.GeoJSON().readGeometry( - value - ), - labelPoint: new ol.format.GeoJSON().readGeometry( - value - ), + geometry: geometry, + labelPoint: geometry, }); this.source[fieldName].clear(); this.source[fieldName].addFeature(ft); @@ -401,14 +399,15 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { var projection = new ol.proj.get(this.projectionCode); var $el = this.$el[0]; $($el).css({ width: "100%", height: "100%" }); + let view = new ol.View({ + center: [0, 0], + zoom: 5, + projection: projection, + }); this.map = new ol.Map({ layers: this.rasterLayers, target: $el, - view: new ol.View({ - center: [0, 0], - zoom: 5, - projection: projection, - }), + view: view, }); this._createVectorLayers(); From 3f3f6872336b0e2c3a937d1fce24e68d86010f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Mon, 20 Nov 2023 15:19:16 +0100 Subject: [PATCH 3/3] Support all projections from SwissTopo Use the service Capabilities --- base_geoengine/fields.py | 2 +- base_geoengine/geo_view/geo_raster_layer.py | 11 +- .../src/js/widgets/geoengine_widgets.js | 110 +++++----- .../geo_view/geo_raster_layer.py | 60 +++++- .../geo_view/geo_raster_layer_view.xml | 4 +- .../i18n/geoengine_swisstopo.pot | 2 +- .../static/src/js/geoengine_swisstopo.js | 190 +++++++++--------- 7 files changed, 220 insertions(+), 159 deletions(-) diff --git a/base_geoengine/fields.py b/base_geoengine/fields.py index f584e94443..7d7e18b4a2 100644 --- a/base_geoengine/fields.py +++ b/base_geoengine/fields.py @@ -48,7 +48,7 @@ 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 geo_direct_write in context you can pass directly WKT""" if not value: return None shape_to_write = self.entry_to_shape(value, same_type=True) diff --git a/base_geoengine/geo_view/geo_raster_layer.py b/base_geoengine/geo_view/geo_raster_layer.py index bc602f6cef..9753f16b19 100644 --- a/base_geoengine/geo_view/geo_raster_layer.py +++ b/base_geoengine/geo_view/geo_raster_layer.py @@ -41,17 +41,12 @@ class GeoRasterLayer(models.Model): matrix_set = fields.Char("matrixSet") format_suffix = fields.Char("formatSuffix", help="eg. png") request_encoding = fields.Char("requestEncoding", help="eg. REST") - projection = fields.Char("projection", help="eg. EPSG:21781") + projection = fields.Char("projection", help="eg. EPSG:3857") units = fields.Char(help="eg. m") resolutions = fields.Char("resolutions") max_extent = fields.Char("max_extent") - dimensions = fields.Char( - "dimensions", - help="List of dimensions separated by ','") - params = fields.Char( - "params", - help="Dictiorary of values for dimensions as JSON" - ) + dimensions = fields.Char("dimensions", help="List of dimensions separated by ','") + params = fields.Char("params", help="Dictionary of values for dimensions as JSON") # technical field to display or not layer type has_type = fields.Boolean(compute='_compute_has_type') diff --git a/base_geoengine/static/src/js/widgets/geoengine_widgets.js b/base_geoengine/static/src/js/widgets/geoengine_widgets.js index 6a4bf94018..9f941348f9 100644 --- a/base_geoengine/static/src/js/widgets/geoengine_widgets.js +++ b/base_geoengine/static/src/js/widgets/geoengine_widgets.js @@ -10,13 +10,13 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { "use strict"; - var core = require("web.core"); - var AbstractField = require("web.AbstractField"); - var geoengine_common = require("base_geoengine.geoengine_common"); - var BackgroundLayers = require("base_geoengine.BackgroundLayers"); - var registry = require("web.field_registry"); + const core = require("web.core"); + const AbstractField = require("web.AbstractField"); + const geoengine_common = require("base_geoengine.geoengine_common"); + const BackgroundLayers = require("base_geoengine.BackgroundLayers"); + const registry = require("web.field_registry"); - var FieldGeoEngineEditMap = AbstractField.extend( + const FieldGeoEngineEditMap = AbstractField.extend( geoengine_common.GeoengineMixin, { // eslint-disable-line max-len @@ -43,7 +43,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { * @override */ start: function () { - var def = this._super(); + let def = this._super(); // Add a listener on parent tab if it exists in order to refresh // geoengine view we need to trigger it on DOM update for changes @@ -95,7 +95,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, _createFeatureStyles: function () { - var styles = { + let styles = { edit: new ol.style.Style({ fill: new ol.style.Fill({ opacity: 0.7, @@ -144,7 +144,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { this.vectorLayers = []; this.source = {}; this.features = {}; - var styles = this._createFeatureStyles(); + let styles = this._createFeatureStyles(); this.vectorLayers.push( this._createVectorLayer(this.name, styles.edit) ); @@ -169,7 +169,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { _addVectorLayers: function () { // first create the readonly layers to have a // lower zIndex - var readonlyLayers = this.vectorLayers.slice(1); + let readonlyLayers = this.vectorLayers.slice(1); if (readonlyLayers) { this.readonlyOverlaysGroup = new ol.layer.Group({ title: "Readonly Overlays", @@ -220,10 +220,10 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, _updateMapEmpty: function () { - var map_view = this.map.getView(); + let map_view = this.map.getView(); // Default extent if (map_view) { - var extent = this.defaultExtent + let extent = this.defaultExtent .replace(/\s/g, "") .split(","); extent = extent.map((coord) => Number(coord)); @@ -232,18 +232,18 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, _updateMapZoom: function (zoom) { - var map_zoom = typeof zoom === "undefined" ? true : zoom; + let map_zoom = typeof zoom === "undefined" ? true : zoom; if (this.source[this.name]) { - var extent = this.source[this.name].getExtent(); - var infinite_extent = [ + let extent = this.source[this.name].getExtent(); + let infinite_extent = [ Infinity, Infinity, -Infinity, -Infinity, ]; if (map_zoom && extent !== infinite_extent) { - var map_view = this.map.getView(); + let map_view = this.map.getView(); if (map_view) { map_view.fit(extent, { maxZoom: 15 }); } @@ -286,7 +286,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, _isTabVisible: function () { - var tab = this.$el.closest("div.tab-pane"); + let tab = this.$el.closest("div.tab-pane"); if (!tab.length) { return false; } @@ -294,7 +294,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, _onUIChange: function () { - var value = null; + let value = null; if (this._geometry) { value = this.format.writeGeometry(this._geometry); } @@ -308,7 +308,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { * As modify needs to get pointer position on map it requires * the map to be rendered before being created */ - var handler = null; + let handler = null; if (this.geoType === "POLYGON") { handler = "Polygon"; } else if (this.geoType === "MULTIPOLYGON") { @@ -325,7 +325,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { // FIXME: unsupported geo type } - var drawControl = function (options) { + let drawControl = function (options) { ol.interaction.Draw.call(this, options); }; ol.inherits(drawControl, ol.interaction.Draw); @@ -396,8 +396,8 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { _renderMap: function () { if (!this.map) { - var projection = new ol.proj.get(this.projectionCode); - var $el = this.$el[0]; + let projection = new ol.proj.get(this.projectionCode); + let $el = this.$el[0]; $($el).css({ width: "100%", height: "100%" }); let view = new ol.View({ center: [0, 0], @@ -414,7 +414,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { this._addVectorLayers(); this.format = new ol.format.GeoJSON({ featureProjection: projection, - defaultDataProjection: "EPSG:" + this.srid, + defaultDataProjection: `EPSG:${this.srid}`, }); $(document).trigger("FieldGeoEngineEditMap:ready", [ @@ -459,7 +459,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { // TODO migrate the following widgets - var FieldGeoPointXY = AbstractField.extend({ + let FieldGeoPointXY = AbstractField.extend({ template: "FieldGeoPointXY", start: function () { @@ -474,10 +474,10 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { * * @return [x, y] */ - var x = openerp.web.parse_value(this.$input.eq(0).val(), { + let x = openerp.web.parse_value(this.$input.eq(0).val(), { type: "float", }); - var y = openerp.web.parse_value(this.$input.eq(1).val(), { + let y = openerp.web.parse_value(this.$input.eq(1).val(), { type: "float", }); return [x, y]; @@ -491,7 +491,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { this._super.apply(this, arguments); if (value) { - var geo_obj = JSON.parse(value); + let geo_obj = JSON.parse(value); this.$input.eq(0).val(geo_obj.coordinates[0]); this.$input.eq(1).val(geo_obj.coordinates[1]); } else { @@ -500,9 +500,9 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, _onUIChange: function () { - var coords = this.get_coords(); + let coords = this.get_coords(); if (coords[0] && coords[1]) { - var json = this.make_GeoJSON(coords); + let json = this.make_GeoJSON(coords); this.value = JSON.stringify(json); } else { this.value = false; @@ -513,7 +513,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { this.invalid = false; try { // Get coords to check if floats - var coords = this.get_coords(); + let coords = this.get_coords(); // Make sure the two coordinates are set or None this.invalid = @@ -535,14 +535,14 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, }); - var FieldGeoPointXYReadonly = FieldGeoPointXY.extend({ + let FieldGeoPointXYReadonly = FieldGeoPointXY.extend({ template: "FieldGeoPointXY.readonly", _setValue: function (value) { this._super.apply(this, arguments); - var show_value = ""; + let show_value = ""; if (value) { - var geo_obj = JSON.parse(value); + let geo_obj = JSON.parse(value); show_value = "(" + geo_obj.coordinates[0] + @@ -559,7 +559,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, }); - var FieldGeoRect = AbstractField.extend({ + let FieldGeoRect = AbstractField.extend({ template: "FieldGeoRect", start: function () { @@ -574,16 +574,16 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { * * @return [[x1, y1],[x2, y2]] */ - var x1 = openerp.web.parse_value(this.$input.eq(0).val(), { + let x1 = openerp.web.parse_value(this.$input.eq(0).val(), { type: "float", }); - var y1 = openerp.web.parse_value(this.$input.eq(1).val(), { + let y1 = openerp.web.parse_value(this.$input.eq(1).val(), { type: "float", }); - var x2 = openerp.web.parse_value(this.$input.eq(2).val(), { + let x2 = openerp.web.parse_value(this.$input.eq(2).val(), { type: "float", }); - var y2 = openerp.web.parse_value(this.$input.eq(3).val(), { + let y2 = openerp.web.parse_value(this.$input.eq(3).val(), { type: "float", }); @@ -594,12 +594,12 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, make_GeoJSON: function (coords) { - var p1 = coords[0]; - var p2 = [coords[0][0], coords[1][1]]; - var p3 = coords[1]; - var p4 = [coords[1][0], coords[0][1]]; + let p1 = coords[0]; + let p2 = [coords[0][0], coords[1][1]]; + let p3 = coords[1]; + let p4 = [coords[1][0], coords[0][1]]; // Create a loop in clockwise - var points = [[p1, p2, p3, p4, p1]]; + let points = [[p1, p2, p3, p4, p1]]; return { type: "Polygon", coordinates: points }; }, @@ -607,7 +607,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { this._super.apply(this, arguments); if (value) { - var geo_obj = JSON.parse(value); + let geo_obj = JSON.parse(value); this.$input.eq(0).val(geo_obj.coordinates[0][0][0]); this.$input.eq(1).val(geo_obj.coordinates[0][0][1]); this.$input.eq(2).val(geo_obj.coordinates[0][2][0]); @@ -623,16 +623,16 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { * * @return [[x1, y1],[x2, y2]] */ - var x1 = coords[0][0], + let x1 = coords[0][0], y1 = coords[0][1], x2 = coords[1][0], y2 = coords[1][1]; - var minx = Math.min(x1, x2); - var maxx = Math.max(x1, x2); + let minx = Math.min(x1, x2); + let maxx = Math.max(x1, x2); - var miny = Math.min(y1, y2); - var maxy = Math.max(y1, y2); + let miny = Math.min(y1, y2); + let maxy = Math.max(y1, y2); return [ [minx, miny], @@ -641,10 +641,10 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, _onUIChange: function () { - var coords = this.get_coords(); + let coords = this.get_coords(); if (this.all_are_set(coords)) { coords = this.correct_bounds(coords); - var json = this.make_GeoJSON(coords); + let json = this.make_GeoJSON(coords); this.value = JSON.stringify(json); } else { this.value = false; @@ -673,7 +673,7 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { this.invalid = false; try { // Get coords to check if floats - var coords = this.get_coords(); + let coords = this.get_coords(); // Make sure all the coordinates are set // if not None or if required @@ -695,14 +695,14 @@ odoo.define("base_geoengine.geoengine_widgets", function (require) { }, }); - var FieldGeoRectReadonly = FieldGeoRect.extend({ + let FieldGeoRectReadonly = FieldGeoRect.extend({ template: "FieldGeoRect.readonly", _setValue: function (value) { this._super.apply(this, arguments); - var show_value = ""; + let show_value = ""; if (value) { - var geo_obj = JSON.parse(value); + let geo_obj = JSON.parse(value); show_value = "(" + geo_obj.coordinates[0][0][0] + diff --git a/geoengine_swisstopo/geo_view/geo_raster_layer.py b/geoengine_swisstopo/geo_view/geo_raster_layer.py index 3e873964b5..6f44e184a9 100644 --- a/geoengine_swisstopo/geo_view/geo_raster_layer.py +++ b/geoengine_swisstopo/geo_view/geo_raster_layer.py @@ -1,12 +1,66 @@ # Copyright 2019 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +import logging + +import requests +from odoo import api, fields, models + + +_LOGGER = logging.getLogger(__name__) +_CAPABILITIES_URL = ( + "https://wmts.geo.admin.ch/EPSG/{matrix_set}/1.0.0/WMTSCapabilities.xml" +) class GeoRasterLayer(models.Model): _inherit = "geoengine.raster.layer" raster_type = fields.Selection(selection_add=[("swisstopo", "Swisstopo")]) - layername = fields.Char("Layer Machine Name") - time = fields.Char("Time Dimension") + projection = fields.Char( + "Projection", compute="_get_projection", readonly=True, store=True + ) + layername = fields.Char("Layer Name", default="ch.swisstopo.pixelkarte-farbe") + matrix_set = fields.Selection( + [ + ("2056", "LV95/CH1903+ (EPSG:2056)"), + ("21781", "LV03/CH1903 (EPSG:21781)"), + ("4326", "WGS84 (EPSG:4326, lat-lon)"), + ( + "3857", + "Spherical Mercator (EPSG:3857, as used in OSM, Bing, Google Map)", + ), + ], + default="2056", + string="TileMatrixSet", + ) + time = fields.Char("Time Dimension (optional)", default=None) + capabilities = fields.Char(compute="_get_capabilities", readonly=True, store=True) + + @api.depends("raster_type", "matrix_set") + def _get_projection(self): + for record in self: + if record.raster_type == "swisstopo": + record.projection = f"EPSG:{record.matrix_set}" + else: + record.projection = False + + @api.depends("raster_type", "matrix_set") + def _get_capabilities(self): + for record in self: + if record.raster_type == "swisstopo": + url = _CAPABILITIES_URL.format(matrix_set=record.matrix_set or "2056") + response = requests.get(url, timeout=30) + if response.ok: + record.capabilities = response.text + else: + _LOGGER.error( + "Swisstopo WMTS Capabilities request (%s)\n" + "failed with status code %s:\n%s", + url, + response.status_code, + response.text, + ) + record.capabilities = False + else: + record.capabilities = False diff --git a/geoengine_swisstopo/geo_view/geo_raster_layer_view.xml b/geoengine_swisstopo/geo_view/geo_raster_layer_view.xml index 73ac646635..17a765275b 100644 --- a/geoengine_swisstopo/geo_view/geo_raster_layer_view.xml +++ b/geoengine_swisstopo/geo_view/geo_raster_layer_view.xml @@ -14,10 +14,12 @@ -

See GeoAdmin API documentation for registration info and available layers. As for now only projection EPSG:21781 is supported.

+

See GeoAdmin API documentation for registration info and available layers.

+ +
diff --git a/geoengine_swisstopo/i18n/geoengine_swisstopo.pot b/geoengine_swisstopo/i18n/geoengine_swisstopo.pot index e09b634a63..26eab0af27 100644 --- a/geoengine_swisstopo/i18n/geoengine_swisstopo.pot +++ b/geoengine_swisstopo/i18n/geoengine_swisstopo.pot @@ -80,6 +80,6 @@ msgstr "" #. module: geoengine_swisstopo #: model_terms:ir.ui.view,arch_db:geoengine_swisstopo.geo_raster_view_form -msgid "for registration info and available layers. As for now only projection EPSG:21781 is supported." +msgid "for registration info and available layers." msgstr "" diff --git a/geoengine_swisstopo/static/src/js/geoengine_swisstopo.js b/geoengine_swisstopo/static/src/js/geoengine_swisstopo.js index 60062ac93f..1e4a5ef33a 100644 --- a/geoengine_swisstopo/static/src/js/geoengine_swisstopo.js +++ b/geoengine_swisstopo/static/src/js/geoengine_swisstopo.js @@ -1,69 +1,88 @@ -/** - * Available resolutions as defined in - * https://api3.geo.admin.ch/services/sdiservices.html#wmts. - * @const {!Array.} - */ -var RESOLUTIONS = [ - 4000, 3750, 3500, 3250, 3000, 2750, 2500, 2250, 2000, 1750, 1500, 1250, - 1000, 750, 650, 500, 250, 100, 50, 20, 10, 5, 2.5, 2, 1.5, 1, 0.5, - 0.25, 0.1 -]; - -var BASE_URL = 'https://wmts{0-9}.geo.admin.ch/1.0.0/{Layer}/default/{Time}/21781/{TileMatrix}/{TileRow}/{TileCol}.{format}'; - -var ATTRIBUTIONS = 'swisstopo'; - -/** - * Extents of Swiss projections. (EPSG:21781) - */ -var EXTENT = [420000, 30000, 900000, 350000]; +const ATTRIBUTIONS = + 'swisstopo'; + +const PROJECTION_DEFINITIONS = { + "EPSG:21781": [ + "+proj=somerc", + "+lat_0=46.95240555555556", + "+lon_0=7.439583333333333", + "+k_0=1", + "+x_0=600000", + "+y_0=200000", + "+ellps=bessel", + "+towgs84=674.4,15.1,405.3,0,0,0,0", + "+units=m", + "+no_defs", + ].join(" "), + "EPSG:2056": [ + "+proj=somerc", + "+lat_0=46.95240555555556", + "+lon_0=7.43958333333333", + "+k_0=1", + "+x_0=2600000", + "+y_0=1200000", + "+ellps=bessel", + "+towgs84=674.374,15.056,405.346,0,0,0,0", + "+units=m", + "+no_defs", + "+type=crs", + ].join(" "), + "EPSG:4326": [ + "+proj=longlat", + "+datum=WGS84", + "+no_defs", + "+type=crs", + ].join(" "), + "EPSG:3857": [ + "+proj=merc", + "+a=6378137", + "+b=6378137", + "+lat_ts=0", + "+lon_0=0", + "+x_0=0", + "+y_0=0", + "+k=1", + "+units=m", + "+nadgrids=@null", + "+wktext", + "+no_defs", + "+type=crs", + ].join(" "), +}; -var PROJECTION_CODE = "EPSG:21781"; +const DEFAULT_PROJECTION_CODE = "EPSG:2056"; -var init_EPSG_21781 = function (self) { +function init_proj4(self) { // Adding proj4 - self.jsLibs.push( - '/geoengine_swisstopo/static/lib/proj4.js' - ); -}; - -var define_EPSG_21781 = function () { - // add swiss projection to allow conversions - if (!ol.proj.get(PROJECTION_CODE)) { - proj4.defs('EPSG:21781', '+proj=somerc +lat_0=46.95240555555556 ' + - '+lon_0=7.439583333333333 +k_0=1 +x_0=600000 +y_0=200000 +ellps=bessel ' + - '+towgs84=674.4,15.1,405.3,0,0,0,0 +units=m +no_defs'); + self.jsLibs.push("/geoengine_swisstopo/static/lib/proj4.js"); +} + +function define_projections() { + // add the required projections to allow conversions + for (let code in PROJECTION_DEFINITIONS) { + if (!ol.proj.get(code)) { + proj4.defs(code, PROJECTION_DEFINITIONS[code]); + } } -}; +} - -odoo.define('geoengine_swisstopo.projection_EPSG_21781', function (require) { +odoo.define("geoengine_swisstopo.projection", function (require) { "use strict"; - var GeoengineWidgets = require('base_geoengine.geoengine_widgets'); - var GeoengineView = require('base_geoengine.GeoengineView'); + const GeoengineWidgets = require("base_geoengine.geoengine_widgets"); + const GeoengineView = require("base_geoengine.GeoengineView"); GeoengineWidgets.FieldGeoEngineEditMap.include({ init: function (parent) { this._super.apply(this, arguments); - init_EPSG_21781(this); - }, - _render: function (parent) { - define_EPSG_21781(); - this._super.apply(this, arguments); + init_proj4(this); }, - }); GeoengineView.include({ init: function (parent) { this._super.apply(this, arguments); - init_EPSG_21781(this); + init_proj4(this); }, - _render: function (parent) { - define_EPSG_21781(); - this._super.apply(this, arguments); - }, - }); }); @@ -71,54 +90,45 @@ odoo.define('geoengine_swisstopo.projection_EPSG_21781', function (require) { odoo.define('geoengine_swisstopo.BackgroundLayers', function (require) { "use strict"; - var BackgroundLayers = require('base_geoengine.BackgroundLayers'); + const BackgroundLayers = require("base_geoengine.BackgroundLayers"); BackgroundLayers.include({ - - createTileGrid: function() { - return new ol.tilegrid.WMTS({ - extent: EXTENT, - resolutions: RESOLUTIONS, - matrixIds: RESOLUTIONS.map(function(item, index) { - return String(index); - }), - }); - }, - - handleCustomLayers: function(l) { - var out = this._super.apply(this, arguments); - if (l.raster_type == 'swisstopo') { - var format = l.format_suffix || 'jpeg'; - var layer = l.layername || 'ch.swisstopo.pixelkarte-farbe'; - - var url = BASE_URL.replace('{format}', format); - var projection = ol.proj.get(PROJECTION_CODE); - var source = new ol.source.WMTS({ - attributions: [ - new ol.Attribution({ - html: ATTRIBUTIONS, - }) - ], - url: url, - dimensions: { - 'Time': l.time || 'current', + handleCustomLayers: function (l) { + define_projections(); + let out = this._super.apply(this, arguments); + if (l.raster_type == "swisstopo") { + let format = l.format_suffix || "jpeg"; + let projection_code = l.projection || DEFAULT_PROJECTION_CODE; + let options = ol.source.WMTS.optionsFromCapabilities( + new ol.format.WMTSCapabilities().read(l.capabilities), + { + crossOrigin: "anonymous", + layer: l.layername, + projection: projection_code, + format: `image/${format}`, }, - projection: projection, - requestEncoding: 'REST', - layer: layer, - style: 'default', - matrixSet: '21781', - format: 'image/' + format, - tileGrid: this.createTileGrid(), - crossOrigin: 'anonymous', - }); + ); + if (!options) { + console.error("the layer is not in the capabilities"); + return out; + } + if (l.time && options.dimensions.Time) { + options.dimensions.Time = l.time; + } + options.attributions = [ + new ol.Attribution({ + html: ATTRIBUTIONS, + }), + ]; + + let source = new ol.source.WMTS(options); out.push( new ol.layer.Tile({ title: l.name, visible: !l.overlay, - type: 'base', - source: source - }) + type: "base", + source: source, + }), ); } return out;