From e691044c1ffbad137351cef8f3fa7d38d735dbd8 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] Support all projections from SwissTopo Use the service Capabilities --- base_geoengine/fields.py | 2 +- base_geoengine/geo_view/geo_raster_layer.py | 11 +- .../geo_view/geo_raster_layer.py | 10 +- .../geo_view/geo_raster_layer_view.xml | 4 +- .../i18n/geoengine_swisstopo.pot | 2 +- .../static/src/js/geoengine_swisstopo.js | 261 ++++++++++++------ 6 files changed, 198 insertions(+), 92 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/geoengine_swisstopo/geo_view/geo_raster_layer.py b/geoengine_swisstopo/geo_view/geo_raster_layer.py index 98fc091c83..e6a7ba00d5 100644 --- a/geoengine_swisstopo/geo_view/geo_raster_layer.py +++ b/geoengine_swisstopo/geo_view/geo_raster_layer.py @@ -1,7 +1,15 @@ # 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): 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..d1566be780 100644 --- a/geoengine_swisstopo/static/src/js/geoengine_swisstopo.js +++ b/geoengine_swisstopo/static/src/js/geoengine_swisstopo.js @@ -1,69 +1,179 @@ -/** - * 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 -]; +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 BASE_URL = 'https://wmts{0-9}.geo.admin.ch/1.0.0/{Layer}/default/{Time}/21781/{TileMatrix}/{TileRow}/{TileCol}.{format}'; +const DEFAULT_PROJECTION_CODE = "EPSG:2056"; -var ATTRIBUTIONS = 'swisstopo'; +function init_proj4(self) { + // Adding proj4 + 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]); + } + } +} -/** - * Extents of Swiss projections. (EPSG:21781) - */ -var EXTENT = [420000, 30000, 900000, 350000]; +odoo.define("geoengine_swisstopo.projection_EPSG_4326", function (require) { + "use strict"; -var PROJECTION_CODE = "EPSG:21781"; + const GeoengineWidgets = require("base_geoengine.geoengine_widgets"); + const GeoengineView = require("base_geoengine.GeoengineView"); -var init_EPSG_21781 = function (self) { - // Adding proj4 - self.jsLibs.push( - '/geoengine_swisstopo/static/lib/proj4.js' - ); -}; + GeoengineWidgets.FieldGeoEngineEditMap.include({ + init: function (parent) { + this._super.apply(this, arguments); + init_proj4(this); + }, + _render: function (parent) { + define_projections(); + this._super.apply(this, arguments); + }, -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'); - } -}; + }); + GeoengineView.include({ + init: function (parent) { + this._super.apply(this, arguments); + init_proj4(this); + }, + _render: function (parent) { + define_projections(); + this._super.apply(this, arguments); + }, + }); +}); +odoo.define("geoengine_swisstopo.projection_EPSG_21781", function (require) { + "use strict"; + const GeoengineWidgets = require("base_geoengine.geoengine_widgets"); + const GeoengineView = require("base_geoengine.GeoengineView"); -odoo.define('geoengine_swisstopo.projection_EPSG_21781', function (require) { + GeoengineWidgets.FieldGeoEngineEditMap.include({ + init: function (parent) { + this._super.apply(this, arguments); + init_proj4(this); + }, + _render: function (parent) { + define_projections(); + this._super.apply(this, arguments); + }, + }); + GeoengineView.include({ + init: function (parent) { + this._super.apply(this, arguments); + init_proj4(this); + }, + _render: function (parent) { + define_projections(); + this._super.apply(this, arguments); + }, + + }); +}); +odoo.define("geoengine_swisstopo.projection_EPSG_2056", 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); + init_proj4(this); }, _render: function (parent) { - define_EPSG_21781(); + define_projections(); this._super.apply(this, arguments); }, - }); GeoengineView.include({ init: function (parent) { this._super.apply(this, arguments); - init_EPSG_21781(this); + init_proj4(this); }, _render: function (parent) { - define_EPSG_21781(); + define_projections(); this._super.apply(this, arguments); }, + }); +}); +odoo.define("geoengine_swisstopo.projection_EPSG_3857", function (require) { + "use strict"; + 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_proj4(this); + }, + _render: function (parent) { + define_projections(); + this._super.apply(this, arguments); + }, + }); + GeoengineView.include({ + init: function (parent) { + this._super.apply(this, arguments); + init_proj4(this); + }, + _render: function (parent) { + define_projections(); + this._super.apply(this, arguments); + }, }); }); @@ -71,54 +181,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;