Skip to content

Commit

Permalink
Support all projections from SwissTopo
Browse files Browse the repository at this point in the history
Use the service Capabilities
  • Loading branch information
sbrunner committed Nov 20, 2023
1 parent 4e3d7f3 commit 7ef6418
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 94 deletions.
2 changes: 1 addition & 1 deletion base_geoengine/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 3 additions & 8 deletions base_geoengine/geo_view/geo_raster_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
13 changes: 10 additions & 3 deletions geoengine_swisstopo/geo_view/geo_raster_layer.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -46,8 +54,7 @@ def _get_capabilities(self):
record.capabilities = response.text

Check warning on line 54 in geoengine_swisstopo/geo_view/geo_raster_layer.py

View check run for this annotation

Codecov / codecov/patch

geoengine_swisstopo/geo_view/geo_raster_layer.py#L54

Added line #L54 was not covered by tests
else:
_LOGGER.error(

Check warning on line 56 in geoengine_swisstopo/geo_view/geo_raster_layer.py

View check run for this annotation

Codecov / codecov/patch

geoengine_swisstopo/geo_view/geo_raster_layer.py#L56

Added line #L56 was not covered by tests
"Swisstopo WMTS Capabilities request (%s)\n"
"failed with status code %s:\n%s",
"Swisstopo WMTS Capabilities request (%s)\nfailed with status code %s:\n%s",
url,
response.status_code,
response.text,
Expand Down
4 changes: 3 additions & 1 deletion geoengine_swisstopo/geo_view/geo_raster_layer_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
<group string="Swisstopo WMTS options"
attrs="{'invisible': [('raster_type', '!=', 'swisstopo')]}"
colspan="4">
<p colspan="2">See <a href="https://api3.geo.admin.ch/services/sdiservices.html#wmts" target="_blank">GeoAdmin API documentation</a> for registration info and available layers. As for now only projection EPSG:21781 is supported.</p>
<p colspan="2">See <a href="https://api3.geo.admin.ch/services/sdiservices.html#wmts" target="_blank">GeoAdmin API documentation</a> for registration info and available layers.</p>
<field name="layername"/>
<field name="time"/>
<field name="format_suffix"/>
<field name="projection"/>
<field name="matrix_set"/>
</group>
</xpath>
</field>
Expand Down
2 changes: 1 addition & 1 deletion geoengine_swisstopo/i18n/geoengine_swisstopo.pot
Original file line number Diff line number Diff line change
Expand Up @@ -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 ""

261 changes: 181 additions & 80 deletions geoengine_swisstopo/static/src/js/geoengine_swisstopo.js
Original file line number Diff line number Diff line change
@@ -1,124 +1,225 @@
/**
* Available resolutions as defined in
* https://api3.geo.admin.ch/services/sdiservices.html#wmts.
* @const {!Array.<number>}
*/
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 =
'<a target="_blank" href="https://www.swisstopo.admin.ch">swisstopo</a>';

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 = '<a target="_blank" href="https://www.swisstopo.admin.ch">swisstopo</a>';
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);
},
});
});


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;
Expand Down

0 comments on commit 7ef6418

Please sign in to comment.