summaryrefslogtreecommitdiff
path: root/base_geoengine/static/src/js/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'base_geoengine/static/src/js/widgets')
-rw-r--r--base_geoengine/static/src/js/widgets/geoengine_record.js74
-rw-r--r--base_geoengine/static/src/js/widgets/geoengine_template_widgets.js104
-rw-r--r--base_geoengine/static/src/js/widgets/geoengine_widgets.js586
3 files changed, 764 insertions, 0 deletions
diff --git a/base_geoengine/static/src/js/widgets/geoengine_record.js b/base_geoengine/static/src/js/widgets/geoengine_record.js
new file mode 100644
index 0000000..4f6619e
--- /dev/null
+++ b/base_geoengine/static/src/js/widgets/geoengine_record.js
@@ -0,0 +1,74 @@
+odoo.define('base_geoengine.Record', function (require) {
+ "use strict";
+
+ var field_utils = require('web.field_utils');
+ var session = require('web.session');
+ var Widget = require('web.Widget');
+
+ var fields_registry = require('base_geoengine.template_widgets').registry;
+
+ var GeoengineRecord = Widget.extend({
+ template: 'Geoengine.Record',
+
+ init: function (parent, record, options) {
+ this._super(parent);
+ this.fields = options.fields;
+ this.qweb = options.qweb;
+ this.record = record;
+ this.id = record.id;
+ this.sub_widgets = [];
+ this._initContent();
+ },
+
+ _initContent: function () {
+ this.record = this._transformRecord(this.record);
+ this._renderContent();
+ },
+
+ _transformRecord: function (record) {
+ var new_record = {};
+ _.each(_.extend(_.object(_.keys(this.fields), []), record),
+ function (value, name) {
+ var r = _.clone(this.fields[name] || {});
+ r.raw_value = value;
+ var formatted_value = field_utils.format[r.type](value, r);
+ r.value = formatted_value;
+ new_record[name] = r;
+ }.bind(this)
+ );
+ return new_record;
+ },
+
+ _renderContent: function () {
+ var qweb_context = {
+ record: this.record,
+ widget: this,
+ user_context: session.user_context,
+ formats: formats,
+ };
+ this.content = this.qweb.render('layer-box', qweb_context);
+ },
+
+ start: function () {
+ this._addWidgets();
+ },
+
+ _addWidgets: function () {
+ var self = this;
+ self.$("field").each(function () {
+ var $field = $(this);
+ var field = self.record[$field.attr("name")];
+ var type = $field.attr("widget") || field.type;
+ var FieldWidget = fields_registry.get(type);
+ if (FieldWidget !== undefined) {
+ var widget = new FieldWidget(self, field, $field);
+ widget.replace($field);
+ self.sub_widgets.push(widget);
+ }
+ });
+ },
+
+ });
+
+ return GeoengineRecord;
+});
diff --git a/base_geoengine/static/src/js/widgets/geoengine_template_widgets.js b/base_geoengine/static/src/js/widgets/geoengine_template_widgets.js
new file mode 100644
index 0000000..21d8177
--- /dev/null
+++ b/base_geoengine/static/src/js/widgets/geoengine_template_widgets.js
@@ -0,0 +1,104 @@
+/* ---------------------------------------------------------
+ * Odoo base_geoengine
+ * Author Benjamin Willig 2017 Acsone SA/NV
+ * ---------------------------------------------------------
+ */
+
+odoo.define('base_geoengine.template_widgets', function (require) {
+ "use strict";
+
+ var field_utils = require('web.field_utils');
+ var pyUtils = require('web.py_utils');
+ var Registry = require('web.Registry');
+ var Widget = require('web.Widget');
+
+ /**
+ * Interface to be implemented by geonengine fields.
+ *
+ */
+ var FieldInterface = {
+
+ /**
+ * @constructor
+ * - parent: The widget's parent.
+ * - field: A dictionary giving details about the field, including
+ * the current field's value in the raw_value field.
+ * - $node: The field <field> tag as it appears in the view,
+ * encapsulated in a jQuery object.
+ */
+ init: function (parent, field, $node) {},
+ };
+
+ /**
+ * Abstract class for classes implementing FieldInterface.
+ *
+ * Properties:
+ * - value: useful property to hold the value of the field. By default,
+ * the constructor sets value property.
+ */
+ var AbstractField = Widget.extend(FieldInterface, {
+
+ /**
+ * @constructor
+ * Saves the field and $node parameters and sets the "value" property.
+ */
+ init: function (parent, field, $node) {
+ this._super(parent, field, $node);
+ this.field = field;
+ this.$node = $node;
+ this.options = pyUtils.py_eval(this.$node.attr("options") || '{}');
+ this.format_descriptor = _.extend({}, this.field, {
+ 'widget': this.$node.attr('widget'),
+ });
+ this.set("value", field.raw_value);
+ },
+
+ renderElement: function () {
+ var format = field_utils.format[this.field.type];
+ this.$el.text(format(this.field.raw_value, this.format_descriptor));
+ },
+ });
+
+ var FieldChar = AbstractField.extend({
+ tagName: 'span',
+ });
+
+ var FieldMany2one = AbstractField.extend({
+ tagName: 'span',
+ });
+
+ var FieldFloat = AbstractField.extend({
+ tagName: 'span',
+ });
+
+ var FieldFloatTime = AbstractField.extend({
+ tagName: 'span',
+ });
+
+ var FieldDate = AbstractField.extend({
+ tagName: 'span',
+ });
+
+ var FieldDatetime = AbstractField.extend({
+ tagName: 'span',
+ });
+
+ // TODO: rather use:
+ // var registry = require('web.field_registry');
+ // registry.add(...
+ var fields_registry = new Registry();
+ fields_registry
+ .add('char', FieldChar)
+ .add('many2one', FieldMany2one)
+ .add('float', FieldFloat)
+ .add('float_time', FieldFloatTime)
+ .add('date', FieldDate)
+ .add('datetime', FieldDatetime)
+ ;
+
+ return {
+ AbstractField: AbstractField,
+ registry: fields_registry,
+ };
+
+});
diff --git a/base_geoengine/static/src/js/widgets/geoengine_widgets.js b/base_geoengine/static/src/js/widgets/geoengine_widgets.js
new file mode 100644
index 0000000..bca843c
--- /dev/null
+++ b/base_geoengine/static/src/js/widgets/geoengine_widgets.js
@@ -0,0 +1,586 @@
+/* ---------------------------------------------------------
+ * Odoo base_geoengine
+ * Author B.Binet Copyright Camptocamp SA
+ * Contributor N. Bessi Copyright Camptocamp SA
+ * Contributor Laurent Mignon 2015 Acsone SA/NV
+ * Contributor Yannick Vaucher 2015-2016 Camptocamp SA
+ * License in __manifest__.py at root level of the module
+ * ---------------------------------------------------------
+ */
+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 field_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;
+ },
+
+ // FIXME still used?
+ validate: function () {
+ this.invalid = false;
+ },
+
+ // --------------------------------------------------------------------
+ // Private
+ // --------------------------------------------------------------------
+
+ _createVectorLayer: function () {
+ this.features = new ol.Collection();
+ this.source = new ol.source.Vector({features: this.features});
+ return new ol.layer.Vector({
+ source: this.source,
+ style: new ol.style.Style({
+ fill: new ol.style.Fill({
+ color: '#ee9900',
+ opacity: 0.7,
+ }),
+ stroke: new ol.style.Stroke({
+ color: '#ee9900',
+ width: 3,
+ opacity: 1,
+ }),
+ image: new ol.style.Circle({
+ radius: 7,
+ fill: new ol.style.Fill({
+ color: '#ffcc33',
+ }),
+ }),
+ }),
+ });
+ },
+
+ _createLayers: function (field_infos) {
+ this.vectorLayer = this._createVectorLayer();
+ 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});
+ }
+ },
+
+ _updateMapZoom: function (zoom) {
+
+ var map_zoom = typeof zoom === 'undefined' ? true : zoom;
+
+ if (this.source) {
+ var extent = this.source.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.clear();
+ this.source.addFeature(ft);
+ if (value) {
+ this._updateMapZoom(zoom);
+ } else {
+ this._updateMapEmpty();
+ }
+ }
+ },
+
+ _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);
+ },
+
+ _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 {
+ // FIXME: unsupported geo type
+ }
+
+ 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,
+ 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();
+ } else {
+ // Modify end
+ this._geometry = e.features.item(0).getGeometry();
+ }
+ this._onUIChange();
+ }.bind(this);
+ this.drawControl.on('drawend', onchange_geom);
+
+ this.features = this.source.getFeaturesCollection();
+ this.modifyControl = new ol.interaction.Modify({
+ features: this.features,
+ // 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 = '<i class="fa fa-trash"/>';
+ button.addEventListener('click', function () {
+ self.source.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 $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,
+ }),
+ });
+ this.map.addLayer(this.vectorLayer);
+
+ this.format = new ol.format.GeoJSON({
+ internalProjection: this.map.getView().getProjection(),
+ externalProjection: 'EPSG:' + this.srid,
+ });
+
+ $(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;
+ }
+ }
+ },
+
+ _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.projection = 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',
+
+ start: function () {
+ this._super.apply(this, arguments);
+ 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',
+ });
+ var y = openerp.web.parse_value(this.$input.eq(1).val(), {
+ type: 'float',
+ });
+ return [x, y];
+ },
+
+ make_GeoJSON: function (coords) {
+ return {"type": "Point", "coordinates": coords};
+ },
+
+ _setValue: function (value) {
+ this._super.apply(this, arguments);
+
+ if (value) {
+ var geo_obj = JSON.parse(value);
+ this.$input.eq(0).val(geo_obj.coordinates[0]);
+ this.$input.eq(1).val(geo_obj.coordinates[1]);
+ } else {
+ this.$input.val('');
+ }
+ },
+
+ _onUIChange: function () {
+ var coords = this.get_coords();
+ if (coords[0] && coords[1]) {
+ var json = this.make_GeoJSON(coords);
+ this.value = JSON.stringify(json);
+ } else {
+ this.value = false;
+ }
+ },
+
+ validate: function () {
+ this.invalid = false;
+ try {
+ // Get coords to check if floats
+ 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;
+ } catch (e) {
+ this.invalid = true;
+ }
+ },
+
+ update_dom: function () {
+ this._super.apply(this, arguments);
+ this.set_readonly(this.readonly);
+ },
+
+ set_readonly: function () {
+ this.$input.prop('readonly', this.readonly);
+ },
+ });
+
+ var FieldGeoPointXYReadonly = FieldGeoPointXY.extend({
+ template: 'FieldGeoPointXY.readonly',
+
+ _setValue: function (value) {
+ this._super.apply(this, arguments);
+ var show_value = '';
+ if (value) {
+ var geo_obj = JSON.parse(value);
+ show_value = "(" + geo_obj.coordinates[0] + ", " +
+ geo_obj.coordinates[1] + ")";
+ }
+ this.$el.find('div').text(show_value);
+ return show_value;
+ },
+
+ validate: function () {
+ this.invalid = false;
+ },
+ });
+
+ var FieldGeoRect = AbstractField.extend({
+ template: 'FieldGeoRect',
+
+ start: function () {
+ this._super.apply(this, arguments);
+ 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',
+ });
+ var y1 = openerp.web.parse_value(this.$input.eq(1).val(), {
+ type: 'float',
+ });
+ var x2 = openerp.web.parse_value(this.$input.eq(2).val(), {
+ type: 'float',
+ });
+ var y2 = openerp.web.parse_value(this.$input.eq(3).val(), {
+ type: 'float',
+ });
+
+ return [[x1, y1], [x2, y2]];
+ },
+
+ 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]];
+ // Create a loop in clockwise
+ var points = [[ p1, p2, p3, p4, p1 ]];
+ return {"type": "Polygon", "coordinates": points};
+ },
+
+ _setValue: function (value) {
+ this._super.apply(this, arguments);
+
+ if (value) {
+ var 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]);
+ this.$input.eq(3).val(geo_obj.coordinates[0][2][1]);
+ } else {
+ this.$input.val('');
+ }
+ },
+
+ correct_bounds: function (coords) {
+
+ /* Reverse bounds if the upper right
+ * point is smaller than bottom left
+ *
+ * @return [[x1, y1],[x2, y2]]
+ */
+ var 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);
+
+ var miny = Math.min(y1, y2);
+ var maxy = Math.max(y1, y2);
+
+ return [[minx, miny], [maxx, maxy]];
+ },
+
+ _onUIChange: function () {
+ var coords = this.get_coords();
+ if (this.all_are_set(coords)) {
+ coords = this.correct_bounds(coords);
+ var json = this.make_GeoJSON(coords);
+ this.value = JSON.stringify(json);
+ } else {
+ this.value = false;
+ }
+ },
+
+ all_are_set: function (coords) {
+ 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;
+ },
+
+ validate: function () {
+ this.invalid = false;
+ try {
+ // Get coords to check if floats
+ var coords = this.get_coords();
+
+ // Make sure all the coordinates are set
+ // if not None or if required
+ this.invalid = (this.required ||
+ !this.none_are_set(coords)) &&
+ !this.all_are_set(coords);
+ } catch (e) {
+ this.invalid = true;
+ }
+ },
+
+ update_dom: function () {
+ this._super.apply(this, arguments);
+ this.set_readonly(this.readonly);
+ },
+
+ set_readonly: function () {
+ this.$input.prop('readonly', this.readonly);
+ },
+ });
+
+ var FieldGeoRectReadonly = FieldGeoRect.extend({
+ template: 'FieldGeoRect.readonly',
+
+ _setValue: function (value) {
+ this._super.apply(this, arguments);
+ 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] + ")";
+ }
+ this.$el.find('div').text(show_value);
+ return show_value;
+ },
+
+ validate: function () {
+ this.invalid = false;
+ },
+ });
+
+ field_registry.add('geo_edit_map', FieldGeoEngineEditMap);
+ // .add('geo_point_xy', FieldGeoPointXY)
+ // .add('geo_point_xy', FieldGeoPointXYReadonly)
+ // .add('geo_rect', FieldGeoRect)
+ // .add('geo_rect', FieldGeoRectReadonly);
+
+ return {
+ FieldGeoEngineEditMap: FieldGeoEngineEditMap,
+ // FieldGeoPointXY: FieldGeoPointXY,
+ // FieldGeoPointXYReadonly: FieldGeoPointXYReadonly,
+ // FieldGeoRect: FieldGeoRect,
+ // FieldGeoRectReadonly: FieldGeoRectReadonly,
+ };
+
+});